From 4bb703e9bfadf44c562f69bab069fd8be26eda97 Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Sat, 3 Apr 2021 13:38:11 +0300 Subject: [PATCH 001/198] Change FontReference to reference type FontReference is mutating itself by storing the cached font for later reuse, but this is not working as intended because FontReference is returned as a value type in UIControls so the cached font is actually stored in a copy of the FontReference and not in the original property. --- Source/Editor/Windows/Assets/FontWindow.cs | 2 +- Source/Engine/Render2D/FontReference.cs | 18 +++++++++++------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Windows/Assets/FontWindow.cs b/Source/Editor/Windows/Assets/FontWindow.cs index 63201ea9e..a4e843c42 100644 --- a/Source/Editor/Windows/Assets/FontWindow.cs +++ b/Source/Editor/Windows/Assets/FontWindow.cs @@ -135,7 +135,7 @@ namespace FlaxEditor.Windows.Assets /// protected override void UnlinkItem() { - _textPreview.Font = new FontReference(); + _textPreview.Font = null; base.UnlinkItem(); } diff --git a/Source/Engine/Render2D/FontReference.cs b/Source/Engine/Render2D/FontReference.cs index 0d725a5a6..b8616f428 100644 --- a/Source/Engine/Render2D/FontReference.cs +++ b/Source/Engine/Render2D/FontReference.cs @@ -7,7 +7,7 @@ namespace FlaxEngine /// /// Font reference that defines the font asset and font size to use. /// - public struct FontReference + public class FontReference { [NoSerialize] private FontAsset _font; @@ -96,9 +96,9 @@ namespace FlaxEngine /// true if the specified is equal to this instance; otherwise, false. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(ref FontReference other) + public bool Equals(FontReference other) { - return _font == other._font && _size == other._size; + return !(other is null) && _font == other._font && _size == other._size; } /// @@ -109,7 +109,9 @@ namespace FlaxEngine /// True if font references are equal, otherwise false. public static bool operator ==(FontReference lhs, FontReference rhs) { - return lhs.Equals(ref rhs); + if (lhs is null) + return rhs is null; + return lhs.Equals(rhs); } /// @@ -120,7 +122,9 @@ namespace FlaxEngine /// True if font references are not equal, otherwise false. public static bool operator !=(FontReference lhs, FontReference rhs) { - return !lhs.Equals(ref rhs); + if (lhs is null) + return !(rhs is null); + return !lhs.Equals(rhs); } /// @@ -129,7 +133,7 @@ namespace FlaxEngine if (!(other is FontReference)) return false; var fontReference = (FontReference)other; - return Equals(ref fontReference); + return Equals(fontReference); } /// @@ -137,7 +141,7 @@ namespace FlaxEngine { unchecked { - int hashCode = _font.GetHashCode(); + int hashCode = _font ? _font.GetHashCode() : 0; hashCode = (hashCode * 397) ^ _size; return hashCode; } From 91672b55d8a7935c2c20d5f17d0fb07787e4338b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 20 May 2021 09:36:02 +0200 Subject: [PATCH 002/198] Fix disabling VertexSnapping for 3D UI rendering #368 --- Source/Engine/UI/UICanvas.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Engine/UI/UICanvas.cs b/Source/Engine/UI/UICanvas.cs index fdb413837..3b84a42f3 100644 --- a/Source/Engine/UI/UICanvas.cs +++ b/Source/Engine/UI/UICanvas.cs @@ -77,7 +77,11 @@ namespace FlaxEngine GPUTexture depthBuffer = Canvas.IgnoreDepth ? null : renderContext.Buffers.DepthBuffer; // Render GUI in 3D + var features = Render2D.Features; + if (Canvas.RenderMode == CanvasRenderMode.WorldSpace || Canvas.RenderMode == CanvasRenderMode.WorldSpaceFaceCamera) + Render2D.Features &= ~Render2D.RenderingFeatures.VertexSnapping; Render2D.CallDrawing(Canvas.GUI, context, input, depthBuffer, ref viewProjectionMatrix); + Render2D.Features = features; Profiler.EndEventGPU(); } From a69fa0b00d0a61eafa6577a61a80dcee988418fb Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 20 May 2021 17:13:36 +0200 Subject: [PATCH 003/198] Add optional command line argument define. --- Source/Engine/Engine/CommandLine.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Source/Engine/Engine/CommandLine.cpp b/Source/Engine/Engine/CommandLine.cpp index edfc9c268..eedc8d356 100644 --- a/Source/Engine/Engine/CommandLine.cpp +++ b/Source/Engine/Engine/CommandLine.cpp @@ -102,6 +102,23 @@ bool CommandLine::Parse(const Char* cmdLine) *(end - len) = 0; \ end -= len; \ } + +#define PARSE_ARG_OPT_SWITCH(text, field) \ + pos = (Char*)StringUtils::FindIgnoreCase(buffer.Get(), TEXT(text)); \ + if (pos) \ + { \ + len = ARRAY_COUNT(text) - 1; \ + if (ParseArg(pos + len, argStart, argEnd)) \ + Options.field = String::Empty; \ + else \ + { \ + Options.field = String(argStart, static_cast(argEnd - argStart)); \ + len = static_cast((argEnd - pos) + 1); \ + Platform::MemoryCopy(pos, pos + len, (end - pos - len) * 2); \ + *(end - len) = 0; \ + end -= len; \ + } \ + } PARSE_BOOL_SWITCH("-windowed ", Windowed); PARSE_BOOL_SWITCH("-fullscreen ", Fullscreen); From ec3972d511012ff6f7005277cc8459d4ba187b5b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 20 May 2021 17:14:01 +0200 Subject: [PATCH 004/198] Add -game command line argument. --- Source/Engine/Engine/CommandLine.cpp | 1 + Source/Engine/Engine/CommandLine.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/Source/Engine/Engine/CommandLine.cpp b/Source/Engine/Engine/CommandLine.cpp index eedc8d356..6d282b6b2 100644 --- a/Source/Engine/Engine/CommandLine.cpp +++ b/Source/Engine/Engine/CommandLine.cpp @@ -151,6 +151,7 @@ bool CommandLine::Parse(const Char* cmdLine) PARSE_ARG_SWITCH("-build ", Build); PARSE_BOOL_SWITCH("-skipcompile ", SkipCompile); PARSE_BOOL_SWITCH("-shaderdebug ", ShaderDebug); + PARSE_ARG_OPT_SWITCH("-game ", Game); #endif diff --git a/Source/Engine/Engine/CommandLine.h b/Source/Engine/Engine/CommandLine.h index 278620bf5..f1dfc5c27 100644 --- a/Source/Engine/Engine/CommandLine.h +++ b/Source/Engine/Engine/CommandLine.h @@ -155,6 +155,11 @@ public: /// Nullable ShaderDebug; + /// + /// -game !scene! ( Scene to play, can be null to use default ) + /// + Nullable Game; + #endif }; From d3b920dfc17cd301ca94d50f30c6517ce31afcda Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 20 May 2021 17:16:02 +0200 Subject: [PATCH 005/198] Prevent Mesh being rendered while uninitialised ( async task not executed yet ). --- Source/Engine/Graphics/Models/Mesh.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index dddbd6a3e..8b7f601f8 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -363,7 +363,8 @@ void Mesh::GetDrawCallGeometry(DrawCall& drawCall) const void Mesh::Render(GPUContext* context) const { - ASSERT(IsInitialized()); + if (!IsInitialized()) + return; context->BindVB(ToSpan((GPUBuffer**)_vertexBuffers, 3)); context->BindIB(_indexBuffer); @@ -372,7 +373,7 @@ void Mesh::Render(GPUContext* context) const void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom) const { - if (!material || !material->IsSurface()) + if (!material || !material->IsSurface() || !IsInitialized()) return; // Submit draw call @@ -403,6 +404,9 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const { + if (!IsInitialized()) + return; + // Cache data const auto& entry = info.Buffer->At(_materialSlotIndex); if (!entry.Visible || !IsInitialized()) From 2293bd5fe0f683a0aef93c57d0c09326fa354340 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 20 May 2021 17:17:53 +0200 Subject: [PATCH 006/198] Expose Simulation.RequestStartPlay() to cpp. --- Source/Editor/Editor.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 9a9500b7b..811599d9c 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -1307,6 +1307,11 @@ namespace FlaxEditor AnimGraphDebugFlow?.Invoke(debugFlow); } + internal static void Internal_RequestStartPlay() + { + Instance.Simulation.RequestStartPlay(); + } + [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int Internal_ReadOutputLogs(string[] outMessages, byte[] outLogTypes, long[] outLogTimes); From 5601be630e56a6732300ac67f69ddb3f2fe8bf94 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 20 May 2021 17:18:48 +0200 Subject: [PATCH 007/198] RequestStartPlay glue code. --- Source/Editor/Managed/ManagedEditor.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Source/Editor/Managed/ManagedEditor.cpp b/Source/Editor/Managed/ManagedEditor.cpp index 35219a906..59475110e 100644 --- a/Source/Editor/Managed/ManagedEditor.cpp +++ b/Source/Editor/Managed/ManagedEditor.cpp @@ -35,6 +35,7 @@ MMethod* Internal_GetGameWindowSize = nullptr; MMethod* Internal_OnAppExit = nullptr; MMethod* Internal_OnVisualScriptingDebugFlow = nullptr; MMethod* Internal_OnAnimGraphDebugFlow = nullptr; +MMethod* Internal_RequestStartPlay = nullptr; void OnLightmapsBake(ShadowsOfMordor::BuildProgressStep step, float stepProgress, float totalProgress, bool isProgressEvent) { @@ -481,6 +482,18 @@ bool ManagedEditor::OnAppExit() return MUtils::Unbox(Internal_OnAppExit->Invoke(GetManagedInstance(), nullptr, nullptr)); } +void ManagedEditor::RequestStartPlay() +{ + if (!HasManagedInstance()) + return; + if (Internal_RequestStartPlay == nullptr) + { + Internal_RequestStartPlay = GetClass()->GetMethod("Internal_RequestStartPlay"); + ASSERT(Internal_RequestStartPlay); + } + Internal_RequestStartPlay->Invoke(GetManagedInstance(), nullptr, nullptr); +} + void ManagedEditor::OnEditorAssemblyLoaded(MAssembly* assembly) { ASSERT(!HasManagedInstance()); From c01cd78225fa9b538570b63c23d456eef9b9db8b Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 20 May 2021 17:19:24 +0200 Subject: [PATCH 008/198] RequestStartPlay glue code. --- Source/Editor/Managed/ManagedEditor.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Source/Editor/Managed/ManagedEditor.h b/Source/Editor/Managed/ManagedEditor.h index 5e0b612b5..ef58fd737 100644 --- a/Source/Editor/Managed/ManagedEditor.h +++ b/Source/Editor/Managed/ManagedEditor.h @@ -133,6 +133,11 @@ public: /// True if exit engine, otherwise false. bool OnAppExit(); + /// + /// Requests switch to play mode. + /// + void RequestStartPlay(); + private: void OnEditorAssemblyLoaded(MAssembly* assembly); From d7f03d2c7d1980f4ad0b317f4e05b30629103a44 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 20 May 2021 17:20:00 +0200 Subject: [PATCH 009/198] Handle game argument. --- Source/Editor/Editor.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Source/Editor/Editor.cpp b/Source/Editor/Editor.cpp index 2284478dd..69963aa66 100644 --- a/Source/Editor/Editor.cpp +++ b/Source/Editor/Editor.cpp @@ -519,6 +519,11 @@ bool Editor::Init() return true; } + if (CommandLine::Options.Game.HasValue()) + { + CommandLine::Options.SkipCompile.SetValue(true); + } + // If during last lightmaps baking engine crashed we could try to restore the progress ShadowsOfMordor::Builder::Instance()->CheckIfRestoreState(); @@ -534,6 +539,11 @@ bool Editor::Init() // Initialize managed editor Managed->Init(); + if (CommandLine::Options.Game.HasValue()) + { + Managed->RequestStartPlay(); + } + return false; } From 66c12017865d82ea5d88323d9ae9ba7bef0ae4d4 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 20 May 2021 17:21:45 +0200 Subject: [PATCH 010/198] Load scene specified in -game argument. --- Source/Editor/Editor.cs | 17 ++++++++++++++++- Source/Editor/Managed/ManagedEditor.cpp | 8 +++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 811599d9c..a775139a6 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -271,10 +271,14 @@ namespace FlaxEditor module.OnEndInit(); } - internal void Init(bool isHeadless, bool skipCompile) + private Guid _startupSceneArgument; + + internal void Init(bool isHeadless, bool skipCompile, Guid sceneId) { + Debug.Log("sceneId string : " + JsonSerializer.GetStringID(sceneId)); EnsureState(); _isHeadlessMode = isHeadless; + _startupSceneArgument = sceneId; Log("Editor init"); if (isHeadless) Log("Running in headless mode"); @@ -332,6 +336,17 @@ namespace FlaxEditor } // Load scene + + // scene cmd line argument + var scene = ContentDatabase.Find(_startupSceneArgument); + if (scene is SceneItem) + { + Editor.Log("Loading scene specified in command line"); + Scene.OpenScene(_startupSceneArgument); + return; + } + + // if no scene cmd line argument is provided var startupSceneMode = Options.Options.General.StartupSceneMode; if (startupSceneMode == GeneralOptions.StartupSceneModes.LastOpened && !ProjectCache.HasCustomData(ProjectDataLastScene)) { diff --git a/Source/Editor/Managed/ManagedEditor.cpp b/Source/Editor/Managed/ManagedEditor.cpp index 59475110e..cdae24a1d 100644 --- a/Source/Editor/Managed/ManagedEditor.cpp +++ b/Source/Editor/Managed/ManagedEditor.cpp @@ -210,7 +210,7 @@ ManagedEditor::~ManagedEditor() void ManagedEditor::Init() { // Note: editor modules should perform quite fast init, any longer things should be done in async during 'editor splash screen time - void* args[2]; + void* args[3]; MClass* mclass = GetClass(); if (mclass == nullptr) { @@ -231,6 +231,12 @@ void ManagedEditor::Init() bool skipCompile = CommandLine::Options.SkipCompile.IsTrue(); args[0] = &isHeadless; args[1] = &skipCompile; + Guid sceneId; + if (Guid::Parse(CommandLine::Options.Game.GetValue(), sceneId)) + { + sceneId = Guid::Empty; + } + args[2] = CommandLine::Options.Game.HasValue() ? &sceneId : nullptr; initMethod->Invoke(instance, args, &exception); if (exception) { From 9fb29529966813447024b0ef5bb36a8204662f42 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 20 May 2021 17:28:55 +0200 Subject: [PATCH 011/198] Tweaks / Cleanup. --- Source/Editor/Editor.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index a775139a6..43f7428cd 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -46,6 +46,7 @@ namespace FlaxEditor private bool _isAfterInit, _areModulesInited, _areModulesAfterInitEnd, _isHeadlessMode; private string _projectToOpen; private float _lastAutoSaveTimer; + private Guid _startupSceneCmdLine; private const string ProjectDataLastScene = "LastScene"; private const string ProjectDataLastSceneSpawn = "LastSceneSpawn"; @@ -271,14 +272,11 @@ namespace FlaxEditor module.OnEndInit(); } - private Guid _startupSceneArgument; - - internal void Init(bool isHeadless, bool skipCompile, Guid sceneId) + internal void Init(bool isHeadless, bool skipCompile, Guid startupScene) { - Debug.Log("sceneId string : " + JsonSerializer.GetStringID(sceneId)); EnsureState(); _isHeadlessMode = isHeadless; - _startupSceneArgument = sceneId; + _startupSceneCmdLine = startupScene; Log("Editor init"); if (isHeadless) Log("Running in headless mode"); @@ -338,11 +336,11 @@ namespace FlaxEditor // Load scene // scene cmd line argument - var scene = ContentDatabase.Find(_startupSceneArgument); + var scene = ContentDatabase.Find(_startupSceneCmdLine); if (scene is SceneItem) { Editor.Log("Loading scene specified in command line"); - Scene.OpenScene(_startupSceneArgument); + Scene.OpenScene(_startupSceneCmdLine); return; } From 62b47fa65b40eaeff893b85282a300236b31855d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 20 May 2021 17:30:43 +0200 Subject: [PATCH 012/198] Comments. --- Source/Editor/Editor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/Editor.cpp b/Source/Editor/Editor.cpp index 69963aa66..1249e1b5e 100644 --- a/Source/Editor/Editor.cpp +++ b/Source/Editor/Editor.cpp @@ -519,6 +519,7 @@ bool Editor::Init() return true; } + // Skip compilation if play on start if (CommandLine::Options.Game.HasValue()) { CommandLine::Options.SkipCompile.SetValue(true); @@ -539,6 +540,7 @@ bool Editor::Init() // Initialize managed editor Managed->Init(); + // Start play if requested by cmd line if (CommandLine::Options.Game.HasValue()) { Managed->RequestStartPlay(); From 316590ec59700a769ca7d77a91d71a8f0e2344a7 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 20 May 2021 17:37:05 +0200 Subject: [PATCH 013/198] Tweak. --- Source/Engine/Engine/CommandLine.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Engine/CommandLine.h b/Source/Engine/Engine/CommandLine.h index f1dfc5c27..6283fc02e 100644 --- a/Source/Engine/Engine/CommandLine.h +++ b/Source/Engine/Engine/CommandLine.h @@ -156,7 +156,7 @@ public: Nullable ShaderDebug; /// - /// -game !scene! ( Scene to play, can be null to use default ) + /// -game !scene! ( Scene to play, can be empty to use default ) /// Nullable Game; From 8eb6fd2b6bd956265549a113b7f4a91adbcb9c11 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 20 May 2021 20:03:58 +0200 Subject: [PATCH 014/198] Fix. --- Source/Editor/Editor.cs | 13 +++++++++++-- Source/Editor/Managed/ManagedEditor.cpp | 12 ++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 43f7428cd..092875be1 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -1320,9 +1320,18 @@ namespace FlaxEditor AnimGraphDebugFlow?.Invoke(debugFlow); } - internal static void Internal_RequestStartPlay() + private static void RequestStartPlayOnStartup() { - Instance.Simulation.RequestStartPlay(); + if (Instance.StateMachine.IsEditMode) + { + Instance.Simulation.RequestStartPlay(); + Instance.StateMachine.StateChanged -= RequestStartPlayOnStartup; + } + } + + internal static void Internal_RequestStartPlayOnStartup() + { + Instance.StateMachine.StateChanged += RequestStartPlayOnStartup; } [MethodImpl(MethodImplOptions.InternalCall)] diff --git a/Source/Editor/Managed/ManagedEditor.cpp b/Source/Editor/Managed/ManagedEditor.cpp index cdae24a1d..6be85e0b9 100644 --- a/Source/Editor/Managed/ManagedEditor.cpp +++ b/Source/Editor/Managed/ManagedEditor.cpp @@ -35,7 +35,7 @@ MMethod* Internal_GetGameWindowSize = nullptr; MMethod* Internal_OnAppExit = nullptr; MMethod* Internal_OnVisualScriptingDebugFlow = nullptr; MMethod* Internal_OnAnimGraphDebugFlow = nullptr; -MMethod* Internal_RequestStartPlay = nullptr; +MMethod* Internal_RequestStartPlayOnStartup = nullptr; void OnLightmapsBake(ShadowsOfMordor::BuildProgressStep step, float stepProgress, float totalProgress, bool isProgressEvent) { @@ -232,7 +232,7 @@ void ManagedEditor::Init() args[0] = &isHeadless; args[1] = &skipCompile; Guid sceneId; - if (Guid::Parse(CommandLine::Options.Game.GetValue(), sceneId)) + if (CommandLine::Options.Game.HasValue() && Guid::Parse(CommandLine::Options.Game.GetValue(), sceneId)) { sceneId = Guid::Empty; } @@ -492,12 +492,12 @@ void ManagedEditor::RequestStartPlay() { if (!HasManagedInstance()) return; - if (Internal_RequestStartPlay == nullptr) + if (Internal_RequestStartPlayOnStartup == nullptr) { - Internal_RequestStartPlay = GetClass()->GetMethod("Internal_RequestStartPlay"); - ASSERT(Internal_RequestStartPlay); + Internal_RequestStartPlayOnStartup = GetClass()->GetMethod("Internal_RequestStartPlayOnStartup"); + ASSERT(Internal_RequestStartPlayOnStartup); } - Internal_RequestStartPlay->Invoke(GetManagedInstance(), nullptr, nullptr); + Internal_RequestStartPlayOnStartup->Invoke(GetManagedInstance(), nullptr, nullptr); } void ManagedEditor::OnEditorAssemblyLoaded(MAssembly* assembly) From 8ac8f9a6d17092c1a697155ab59755fedbad6af0 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 20 May 2021 20:05:44 +0200 Subject: [PATCH 015/198] Tweaks. --- Source/Editor/Editor.cs | 2 +- Source/Editor/Managed/ManagedEditor.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 092875be1..cfbbc93b1 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -1329,7 +1329,7 @@ namespace FlaxEditor } } - internal static void Internal_RequestStartPlayOnStartup() + internal static void Internal_RequestStartPlayOnEditMode() { Instance.StateMachine.StateChanged += RequestStartPlayOnStartup; } diff --git a/Source/Editor/Managed/ManagedEditor.cpp b/Source/Editor/Managed/ManagedEditor.cpp index 6be85e0b9..14da2bb41 100644 --- a/Source/Editor/Managed/ManagedEditor.cpp +++ b/Source/Editor/Managed/ManagedEditor.cpp @@ -35,7 +35,7 @@ MMethod* Internal_GetGameWindowSize = nullptr; MMethod* Internal_OnAppExit = nullptr; MMethod* Internal_OnVisualScriptingDebugFlow = nullptr; MMethod* Internal_OnAnimGraphDebugFlow = nullptr; -MMethod* Internal_RequestStartPlayOnStartup = nullptr; +MMethod* Internal_RequestStartPlayOnEditMode = nullptr; void OnLightmapsBake(ShadowsOfMordor::BuildProgressStep step, float stepProgress, float totalProgress, bool isProgressEvent) { @@ -492,12 +492,12 @@ void ManagedEditor::RequestStartPlay() { if (!HasManagedInstance()) return; - if (Internal_RequestStartPlayOnStartup == nullptr) + if (Internal_RequestStartPlayOnEditMode == nullptr) { - Internal_RequestStartPlayOnStartup = GetClass()->GetMethod("Internal_RequestStartPlayOnStartup"); - ASSERT(Internal_RequestStartPlayOnStartup); + Internal_RequestStartPlayOnEditMode = GetClass()->GetMethod("Internal_RequestStartPlayOnEditMode"); + ASSERT(Internal_RequestStartPlayOnEditMode); } - Internal_RequestStartPlayOnStartup->Invoke(GetManagedInstance(), nullptr, nullptr); + Internal_RequestStartPlayOnEditMode->Invoke(GetManagedInstance(), nullptr, nullptr); } void ManagedEditor::OnEditorAssemblyLoaded(MAssembly* assembly) From 9e2867ed7d2cd6c9aebef5340d36c8d3d218f06a Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 20 May 2021 20:07:39 +0200 Subject: [PATCH 016/198] Tweak. --- Source/Editor/Managed/ManagedEditor.cpp | 2 +- Source/Editor/Managed/ManagedEditor.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Managed/ManagedEditor.cpp b/Source/Editor/Managed/ManagedEditor.cpp index 14da2bb41..9ea0f9cfc 100644 --- a/Source/Editor/Managed/ManagedEditor.cpp +++ b/Source/Editor/Managed/ManagedEditor.cpp @@ -488,7 +488,7 @@ bool ManagedEditor::OnAppExit() return MUtils::Unbox(Internal_OnAppExit->Invoke(GetManagedInstance(), nullptr, nullptr)); } -void ManagedEditor::RequestStartPlay() +void ManagedEditor::RequestStartPlayOnEditMode() { if (!HasManagedInstance()) return; diff --git a/Source/Editor/Managed/ManagedEditor.h b/Source/Editor/Managed/ManagedEditor.h index ef58fd737..df05e87bd 100644 --- a/Source/Editor/Managed/ManagedEditor.h +++ b/Source/Editor/Managed/ManagedEditor.h @@ -134,9 +134,9 @@ public: bool OnAppExit(); /// - /// Requests switch to play mode. + /// Requests play mode when the editor is in edit mode ( once ). /// - void RequestStartPlay(); + void RequestStartPlayOnEditMode(); private: From 320a1a984e2679553f57f71cf28ff12c55473eac Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Thu, 20 May 2021 20:08:08 +0200 Subject: [PATCH 017/198] Fix. --- Source/Editor/Editor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Editor.cpp b/Source/Editor/Editor.cpp index 1249e1b5e..c90e80e70 100644 --- a/Source/Editor/Editor.cpp +++ b/Source/Editor/Editor.cpp @@ -543,7 +543,7 @@ bool Editor::Init() // Start play if requested by cmd line if (CommandLine::Options.Game.HasValue()) { - Managed->RequestStartPlay(); + Managed->RequestStartPlayOnEditMode(); } return false; From 3a68e2891e988e258e718914a45afd2bad25e6e7 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Fri, 21 May 2021 18:18:11 +0200 Subject: [PATCH 018/198] Add null check. --- Source/Editor/Managed/ManagedEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Managed/ManagedEditor.cpp b/Source/Editor/Managed/ManagedEditor.cpp index 9ea0f9cfc..93f36f337 100644 --- a/Source/Editor/Managed/ManagedEditor.cpp +++ b/Source/Editor/Managed/ManagedEditor.cpp @@ -232,7 +232,7 @@ void ManagedEditor::Init() args[0] = &isHeadless; args[1] = &skipCompile; Guid sceneId; - if (CommandLine::Options.Game.HasValue() && Guid::Parse(CommandLine::Options.Game.GetValue(), sceneId)) + if (!CommandLine::Options.Game.HasValue() || (CommandLine::Options.Game.HasValue() && Guid::Parse(CommandLine::Options.Game.GetValue(), sceneId))) { sceneId = Guid::Empty; } From 6eb3bc0577e890247ab433444a34d7e4584a179c Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Fri, 21 May 2021 18:18:20 +0200 Subject: [PATCH 019/198] Tweak. --- Source/Editor/Editor.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index cfbbc93b1..749d7ac6e 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -1320,18 +1320,18 @@ namespace FlaxEditor AnimGraphDebugFlow?.Invoke(debugFlow); } - private static void RequestStartPlayOnStartup() + private static void RequestStartPlayOnEditMode() { if (Instance.StateMachine.IsEditMode) { Instance.Simulation.RequestStartPlay(); - Instance.StateMachine.StateChanged -= RequestStartPlayOnStartup; + Instance.StateMachine.StateChanged -= RequestStartPlayOnEditMode; } } internal static void Internal_RequestStartPlayOnEditMode() { - Instance.StateMachine.StateChanged += RequestStartPlayOnStartup; + Instance.StateMachine.StateChanged += RequestStartPlayOnEditMode; } [MethodImpl(MethodImplOptions.InternalCall)] From 198d9d9dd543cbac2dbd60ecfede44517eb324b2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 23 May 2021 11:44:14 +0200 Subject: [PATCH 020/198] Ignore ReSharper warnings in low-level cpu particles code --- .../CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp index ae25e01e0..4d8e02a97 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp @@ -3,6 +3,12 @@ #include "ParticleEmitterGraph.CPU.h" #include "Engine/Core/Random.h" +// ReSharper disable CppCStyleCast +// ReSharper disable CppClangTidyClangDiagnosticCastAlign +// ReSharper disable CppDefaultCaseNotHandledInSwitchStatement +// ReSharper disable CppClangTidyCppcoreguidelinesMacroUsage +// ReSharper disable CppClangTidyClangDiagnosticOldStyleCast + #define RAND Random::Rand() #define RAND2 Vector2(RAND, RAND) #define RAND3 Vector3(RAND, RAND, RAND) @@ -62,7 +68,7 @@ namespace float scale = 1.0f; for (int32 i = 0; i < octaves; i++) { - const float curWeight = Math::Pow(1.0f - ((float)i / octaves), Math::Lerp(2.0f, 0.2f, roughness)); + const float curWeight = Math::Pow(1.0f - ((float)i / (float)octaves), Math::Lerp(2.0f, 0.2f, roughness)); noise += Noise3D(position * scale) * curWeight; weight += curWeight; @@ -156,7 +162,7 @@ int32 ParticleEmitterGraphCPUExecutor::ProcessSpawnModule(int32 index) // Calculate actual spawn amount spawnCount = Math::Max(spawnCount, 0.0f); const int32 result = Math::FloorToInt(spawnCount); - spawnCount -= result; + spawnCount -= (float)result; data.SpawnCounter = spawnCount; return result; @@ -475,7 +481,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* auto& velocity = _data->Buffer->Layout->Attributes[node->Attributes[0]]; auto& mass = _data->Buffer->Layout->Attributes[node->Attributes[1]]; - byte* spriteSizePtr = useSpriteSize ? start + _data->Buffer->Layout->Attributes[node->Attributes[2]].Offset : 0; + byte* spriteSizePtr = useSpriteSize ? start + _data->Buffer->Layout->Attributes[node->Attributes[2]].Offset : nullptr; byte* velocityPtr = start + velocity.Offset; byte* massPtr = start + mass.Offset; From c78fb7995e9fdf50ba00233f5f3378452d768780 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 23 May 2021 12:09:35 +0200 Subject: [PATCH 021/198] Add more indentation to scene tree items to make it more readable --- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index 5e2792dc4..b5e5e2464 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -50,6 +50,7 @@ namespace FlaxEditor.SceneGraph.GUI public ActorTreeNode() : base(true) { + ChildrenIndent = 16.0f; } internal virtual void LinkNode(ActorNode node) From 566756ecf2c8841ba26276746598efdb1d8edefb Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sun, 23 May 2021 19:31:44 +0200 Subject: [PATCH 022/198] Remove duplicate IsInitialized. --- Source/Engine/Graphics/Models/Mesh.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index 8b7f601f8..35b5fe5fb 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -404,9 +404,6 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const { - if (!IsInitialized()) - return; - // Cache data const auto& entry = info.Buffer->At(_materialSlotIndex); if (!entry.Visible || !IsInitialized()) From ce3caede6c5aaa328936ec0a227238a0d53d0184 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sun, 23 May 2021 19:32:45 +0200 Subject: [PATCH 023/198] Remove skipcompile when -play is used. --- Source/Editor/Editor.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Source/Editor/Editor.cpp b/Source/Editor/Editor.cpp index c90e80e70..1b18034c4 100644 --- a/Source/Editor/Editor.cpp +++ b/Source/Editor/Editor.cpp @@ -518,12 +518,6 @@ bool Editor::Init() exit(failed ? 1 : 0); return true; } - - // Skip compilation if play on start - if (CommandLine::Options.Game.HasValue()) - { - CommandLine::Options.SkipCompile.SetValue(true); - } // If during last lightmaps baking engine crashed we could try to restore the progress ShadowsOfMordor::Builder::Instance()->CheckIfRestoreState(); @@ -541,7 +535,7 @@ bool Editor::Init() Managed->Init(); // Start play if requested by cmd line - if (CommandLine::Options.Game.HasValue()) + if (CommandLine::Options.Play.HasValue()) { Managed->RequestStartPlayOnEditMode(); } From 1f3a86dca921a2861774986b8ceb307a4ab12628 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sun, 23 May 2021 19:33:36 +0200 Subject: [PATCH 024/198] Fix -play not working with compilation at start. --- Source/Editor/Editor.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 749d7ac6e..0df2d8203 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -1323,12 +1323,11 @@ namespace FlaxEditor private static void RequestStartPlayOnEditMode() { if (Instance.StateMachine.IsEditMode) - { Instance.Simulation.RequestStartPlay(); + if (Instance.StateMachine.IsPlayMode) Instance.StateMachine.StateChanged -= RequestStartPlayOnEditMode; - } } - + internal static void Internal_RequestStartPlayOnEditMode() { Instance.StateMachine.StateChanged += RequestStartPlayOnEditMode; From 2f12642405701c23a3fabb0ae85a926bd46beb87 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sun, 23 May 2021 19:34:16 +0200 Subject: [PATCH 025/198] Rename -game to -play & Fix when play not used. --- Source/Editor/Managed/ManagedEditor.cpp | 4 ++-- Source/Engine/Engine/CommandLine.cpp | 2 +- Source/Engine/Engine/CommandLine.h | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Managed/ManagedEditor.cpp b/Source/Editor/Managed/ManagedEditor.cpp index 93f36f337..18a905d4b 100644 --- a/Source/Editor/Managed/ManagedEditor.cpp +++ b/Source/Editor/Managed/ManagedEditor.cpp @@ -232,11 +232,11 @@ void ManagedEditor::Init() args[0] = &isHeadless; args[1] = &skipCompile; Guid sceneId; - if (!CommandLine::Options.Game.HasValue() || (CommandLine::Options.Game.HasValue() && Guid::Parse(CommandLine::Options.Game.GetValue(), sceneId))) + if (!CommandLine::Options.Play.HasValue() || (CommandLine::Options.Play.HasValue() && Guid::Parse(CommandLine::Options.Play.GetValue(), sceneId))) { sceneId = Guid::Empty; } - args[2] = CommandLine::Options.Game.HasValue() ? &sceneId : nullptr; + args[2] = &sceneId; initMethod->Invoke(instance, args, &exception); if (exception) { diff --git a/Source/Engine/Engine/CommandLine.cpp b/Source/Engine/Engine/CommandLine.cpp index 6d282b6b2..45ab454e6 100644 --- a/Source/Engine/Engine/CommandLine.cpp +++ b/Source/Engine/Engine/CommandLine.cpp @@ -151,7 +151,7 @@ bool CommandLine::Parse(const Char* cmdLine) PARSE_ARG_SWITCH("-build ", Build); PARSE_BOOL_SWITCH("-skipcompile ", SkipCompile); PARSE_BOOL_SWITCH("-shaderdebug ", ShaderDebug); - PARSE_ARG_OPT_SWITCH("-game ", Game); + PARSE_ARG_OPT_SWITCH("-play ", Play); #endif diff --git a/Source/Engine/Engine/CommandLine.h b/Source/Engine/Engine/CommandLine.h index 6283fc02e..8b3f8324c 100644 --- a/Source/Engine/Engine/CommandLine.h +++ b/Source/Engine/Engine/CommandLine.h @@ -156,9 +156,9 @@ public: Nullable ShaderDebug; /// - /// -game !scene! ( Scene to play, can be empty to use default ) + /// -play !guid! ( Scene to play, can be empty to use default ) /// - Nullable Game; + Nullable Play; #endif }; From 327ccec4cb7e0dc6f41fa9559e02adead1fe94b6 Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Mon, 24 May 2021 18:56:09 +0300 Subject: [PATCH 026/198] Only build bindings for project references with C#-only projects --- Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs index dd4b5d951..dc35c9fcc 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs @@ -1009,7 +1009,7 @@ namespace Flax.Build { using (new ProfileEventScope(reference.Project.Name)) { - if (Configuration.BuildBindingsOnly || reference.Project.IsCSharpOnlyProject || !platform.HasRequiredSDKsInstalled) + if (Configuration.BuildBindingsOnly || project.IsCSharpOnlyProject || reference.Project.IsCSharpOnlyProject || !platform.HasRequiredSDKsInstalled) { BuildTargetReferenceNativeCppBindingsOnly(buildContext, buildData, reference); } From 2c7d62bb8e5d517641074483cc713fe78b138b12 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 24 May 2021 18:34:19 +0200 Subject: [PATCH 027/198] Add support for custom drag&drop into level/prefab viewports from custom asset items --- Source/Editor/Content/Items/AssetItem.cs | 20 +++ .../Editor/Content/Items/BinaryAssetItem.cs | 32 +++- Source/Editor/Content/Items/PrefabItem.cs | 12 ++ .../Editor/Content/Items/VisualScriptItem.cs | 12 ++ Source/Editor/Content/Proxy/AudioClipProxy.cs | 49 +++++- .../Editor/Content/Proxy/BinaryAssetProxy.cs | 8 +- .../Content/Proxy/CollisionDataProxy.cs | 33 +++- Source/Editor/Content/Proxy/ModelProxy.cs | 2 +- .../Content/Proxy/ParticleSystemProxy.cs | 31 ++++ .../Content/Proxy/SceneAnimationProxy.cs | 31 ++++ Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 161 +----------------- .../Viewport/MainEditorGizmoViewport.cs | 148 ++++------------ .../Editor/Viewport/PrefabWindowViewport.cs | 85 +-------- 13 files changed, 262 insertions(+), 362 deletions(-) diff --git a/Source/Editor/Content/Items/AssetItem.cs b/Source/Editor/Content/Items/AssetItem.cs index afb68d42a..f534e93eb 100644 --- a/Source/Editor/Content/Items/AssetItem.cs +++ b/Source/Editor/Content/Items/AssetItem.cs @@ -108,6 +108,26 @@ namespace FlaxEditor.Content return false; } + /// + /// Called when user dags this item into editor viewport or scene tree node. + /// + /// The editor context (eg. editor viewport or scene tree node). + /// True if item can be dropped in, otherwise false. + public virtual bool OnEditorDrag(object context) + { + return false; + } + + /// + /// Called when user drops the item into editor viewport or scene tree node. + /// + /// The editor context (eg. editor viewport or scene tree node). + /// The spawned object. + public virtual Actor OnEditorDrop(object context) + { + throw new NotSupportedException($"Asset {GetType()} doesn't support dropping into viewport."); + } + /// protected override bool DrawShadow => true; diff --git a/Source/Editor/Content/Items/BinaryAssetItem.cs b/Source/Editor/Content/Items/BinaryAssetItem.cs index 417bd23e4..2503e9a38 100644 --- a/Source/Editor/Content/Items/BinaryAssetItem.cs +++ b/Source/Editor/Content/Items/BinaryAssetItem.cs @@ -103,14 +103,26 @@ namespace FlaxEditor.Content /// Implementation of for assets. /// /// - public class ModelAssetItem : BinaryAssetItem + public class ModelItem : BinaryAssetItem { /// - public ModelAssetItem(string path, ref Guid id, string typeName, Type type) + public ModelItem(string path, ref Guid id, string typeName, Type type) : base(path, ref id, typeName, type, ContentItemSearchFilter.Model) { } + /// + public override bool OnEditorDrag(object context) + { + return true; + } + + /// + public override Actor OnEditorDrop(object context) + { + return new StaticModel { Model = FlaxEngine.Content.LoadAsync(ID) }; + } + /// protected override void OnBuildTooltipText(StringBuilder sb) { @@ -142,14 +154,26 @@ namespace FlaxEditor.Content /// Implementation of for assets. /// /// - public class SkinnedModelAssetItem : BinaryAssetItem + public class SkinnedModeItem : BinaryAssetItem { /// - public SkinnedModelAssetItem(string path, ref Guid id, string typeName, Type type) + public SkinnedModeItem(string path, ref Guid id, string typeName, Type type) : base(path, ref id, typeName, type, ContentItemSearchFilter.Model) { } + /// + public override bool OnEditorDrag(object context) + { + return true; + } + + /// + public override Actor OnEditorDrop(object context) + { + return new AnimatedModel { SkinnedModel = FlaxEngine.Content.LoadAsync(ID) }; + } + /// protected override void OnBuildTooltipText(StringBuilder sb) { diff --git a/Source/Editor/Content/Items/PrefabItem.cs b/Source/Editor/Content/Items/PrefabItem.cs index aee264d55..e758595ae 100644 --- a/Source/Editor/Content/Items/PrefabItem.cs +++ b/Source/Editor/Content/Items/PrefabItem.cs @@ -21,6 +21,18 @@ namespace FlaxEditor.Content { } + /// + public override bool OnEditorDrag(object context) + { + return true; + } + + /// + public override Actor OnEditorDrop(object context) + { + return PrefabManager.SpawnPrefab(FlaxEngine.Content.LoadAsync(ID), null); + } + /// public override ContentItemType ItemType => ContentItemType.Asset; diff --git a/Source/Editor/Content/Items/VisualScriptItem.cs b/Source/Editor/Content/Items/VisualScriptItem.cs index 04311644b..c1ccf5d73 100644 --- a/Source/Editor/Content/Items/VisualScriptItem.cs +++ b/Source/Editor/Content/Items/VisualScriptItem.cs @@ -538,6 +538,18 @@ namespace FlaxEditor.Content Editor.Instance.CodeEditing.ClearTypes(); } + /// + public override bool OnEditorDrag(object context) + { + return new ScriptType(typeof(Actor)).IsAssignableFrom(ScriptType) && ScriptType.CanCreateInstance; + } + + /// + public override Actor OnEditorDrop(object context) + { + return (Actor)ScriptType.CreateInstance(); + } + /// public override SpriteHandle DefaultThumbnail => Editor.Instance.Icons.VisualScript128; diff --git a/Source/Editor/Content/Proxy/AudioClipProxy.cs b/Source/Editor/Content/Proxy/AudioClipProxy.cs index ba60d6ef9..ceff22dd1 100644 --- a/Source/Editor/Content/Proxy/AudioClipProxy.cs +++ b/Source/Editor/Content/Proxy/AudioClipProxy.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Text; using FlaxEditor.Content.Thumbnails; using FlaxEditor.Viewport.Previews; using FlaxEditor.Windows; @@ -11,11 +12,51 @@ using FlaxEngine.GUI; namespace FlaxEditor.Content { + /// + /// Implementation of for assets. + /// + /// + class AudioClipItem : BinaryAssetItem + { + /// + public AudioClipItem(string path, ref Guid id, string typeName, Type type) + : base(path, ref id, typeName, type, ContentItemSearchFilter.Audio) + { + } + + /// + public override bool OnEditorDrag(object context) + { + return true; + } + + /// + public override Actor OnEditorDrop(object context) + { + return new AudioSource { Clip = FlaxEngine.Content.LoadAsync(ID) }; + } + + /// + protected override void OnBuildTooltipText(StringBuilder sb) + { + base.OnBuildTooltipText(sb); + + var asset = FlaxEngine.Content.Load(ID, 100); + if (asset) + { + var info = asset.Info; + sb.Append("Duration: ").Append(asset.Length).AppendLine(); + sb.Append("Channels: ").Append(info.NumChannels).AppendLine(); + sb.Append("Bit Depth: ").Append(info.BitDepth).AppendLine(); + } + } + } + /// /// A asset proxy object. /// /// - public class AudioClipProxy : BinaryAssetProxy + class AudioClipProxy : BinaryAssetProxy { private List _previews; @@ -34,6 +75,12 @@ namespace FlaxEditor.Content return new AudioClipWindow(editor, (AssetItem)item); } + /// + public override AssetItem ConstructItem(string path, string typeName, ref Guid id) + { + return new AudioClipItem(path, ref id, typeName, AssetType); + } + /// public override Color AccentColor => Color.FromRGB(0xB3452B); diff --git a/Source/Editor/Content/Proxy/BinaryAssetProxy.cs b/Source/Editor/Content/Proxy/BinaryAssetProxy.cs index 4d27f325b..d19f8fd63 100644 --- a/Source/Editor/Content/Proxy/BinaryAssetProxy.cs +++ b/Source/Editor/Content/Proxy/BinaryAssetProxy.cs @@ -47,9 +47,9 @@ namespace FlaxEditor.Content if (typeof(TextureBase).IsAssignableFrom(type)) return new TextureAssetItem(path, ref id, typeName, type); if (typeof(Model).IsAssignableFrom(type)) - return new ModelAssetItem(path, ref id, typeName, type); + return new ModelItem(path, ref id, typeName, type); if (typeof(SkinnedModel).IsAssignableFrom(type)) - return new SkinnedModelAssetItem(path, ref id, typeName, type); + return new SkinnedModeItem(path, ref id, typeName, type); ContentItemSearchFilter searchFilter; if (typeof(MaterialBase).IsAssignableFrom(type)) @@ -58,11 +58,9 @@ namespace FlaxEditor.Content searchFilter = ContentItemSearchFilter.Prefab; else if (typeof(SceneAsset).IsAssignableFrom(type)) searchFilter = ContentItemSearchFilter.Scene; - else if (typeof(AudioClip).IsAssignableFrom(type)) - searchFilter = ContentItemSearchFilter.Audio; else if (typeof(Animation).IsAssignableFrom(type)) searchFilter = ContentItemSearchFilter.Animation; - else if (typeof(ParticleEmitter).IsAssignableFrom(type) || typeof(ParticleSystem).IsAssignableFrom(type)) + else if (typeof(ParticleEmitter).IsAssignableFrom(type)) searchFilter = ContentItemSearchFilter.Particles; else searchFilter = ContentItemSearchFilter.Other; diff --git a/Source/Editor/Content/Proxy/CollisionDataProxy.cs b/Source/Editor/Content/Proxy/CollisionDataProxy.cs index c23730375..e7d437adc 100644 --- a/Source/Editor/Content/Proxy/CollisionDataProxy.cs +++ b/Source/Editor/Content/Proxy/CollisionDataProxy.cs @@ -8,11 +8,36 @@ using FlaxEngine; namespace FlaxEditor.Content { + /// + /// Implementation of for assets. + /// + /// + class CollisionDataItem : BinaryAssetItem + { + /// + public CollisionDataItem(string path, ref Guid id, string typeName, Type type) + : base(path, ref id, typeName, type, ContentItemSearchFilter.Other) + { + } + + /// + public override bool OnEditorDrag(object context) + { + return true; + } + + /// + public override Actor OnEditorDrop(object context) + { + return new MeshCollider { CollisionData = FlaxEngine.Content.LoadAsync(ID) }; + } + } + /// /// A asset proxy object. /// /// - public class CollisionDataProxy : BinaryAssetProxy + class CollisionDataProxy : BinaryAssetProxy { /// public override string Name => "Collision Data"; @@ -23,6 +48,12 @@ namespace FlaxEditor.Content return new CollisionDataWindow(editor, item as AssetItem); } + /// + public override AssetItem ConstructItem(string path, string typeName, ref Guid id) + { + return new CollisionDataItem(path, ref id, typeName, AssetType); + } + /// public override Color AccentColor => Color.FromRGB(0x2c3e50); diff --git a/Source/Editor/Content/Proxy/ModelProxy.cs b/Source/Editor/Content/Proxy/ModelProxy.cs index a28a022a8..2baf717e1 100644 --- a/Source/Editor/Content/Proxy/ModelProxy.cs +++ b/Source/Editor/Content/Proxy/ModelProxy.cs @@ -47,7 +47,7 @@ namespace FlaxEditor.Content menu.AddButton("Create collision data", () => { - var model = FlaxEngine.Content.LoadAsync(((ModelAssetItem)item).ID); + var model = FlaxEngine.Content.LoadAsync(((ModelItem)item).ID); var collisionDataProxy = (CollisionDataProxy)Editor.Instance.ContentDatabase.GetProxy(); collisionDataProxy.CreateCollisionDataFromModel(model); }); diff --git a/Source/Editor/Content/Proxy/ParticleSystemProxy.cs b/Source/Editor/Content/Proxy/ParticleSystemProxy.cs index f3b2eb67a..7e206fba9 100644 --- a/Source/Editor/Content/Proxy/ParticleSystemProxy.cs +++ b/Source/Editor/Content/Proxy/ParticleSystemProxy.cs @@ -10,6 +10,31 @@ using FlaxEngine.GUI; namespace FlaxEditor.Content { + /// + /// Implementation of for assets. + /// + /// + class ParticleSystemItem : BinaryAssetItem + { + /// + public ParticleSystemItem(string path, ref Guid id, string typeName, Type type) + : base(path, ref id, typeName, type, ContentItemSearchFilter.Particles) + { + } + + /// + public override bool OnEditorDrag(object context) + { + return true; + } + + /// + public override Actor OnEditorDrop(object context) + { + return new ParticleEffect { ParticleSystem = FlaxEngine.Content.LoadAsync(ID) }; + } + } + /// /// A asset proxy object. /// @@ -28,6 +53,12 @@ namespace FlaxEditor.Content return new ParticleSystemWindow(editor, item as AssetItem); } + /// + public override AssetItem ConstructItem(string path, string typeName, ref Guid id) + { + return new ParticleSystemItem(path, ref id, typeName, AssetType); + } + /// public override Color AccentColor => Color.FromRGB(0xFF790200); diff --git a/Source/Editor/Content/Proxy/SceneAnimationProxy.cs b/Source/Editor/Content/Proxy/SceneAnimationProxy.cs index 83d9247d8..66c1bfff2 100644 --- a/Source/Editor/Content/Proxy/SceneAnimationProxy.cs +++ b/Source/Editor/Content/Proxy/SceneAnimationProxy.cs @@ -7,6 +7,31 @@ using FlaxEngine; namespace FlaxEditor.Content { + /// + /// Implementation of for assets. + /// + /// + class SceneAnimationItem : BinaryAssetItem + { + /// + public SceneAnimationItem(string path, ref Guid id, string typeName, Type type) + : base(path, ref id, typeName, type, ContentItemSearchFilter.Other) + { + } + + /// + public override bool OnEditorDrag(object context) + { + return true; + } + + /// + public override Actor OnEditorDrop(object context) + { + return new SceneAnimationPlayer { Animation = FlaxEngine.Content.LoadAsync(ID) }; + } + } + /// /// A asset proxy object. /// @@ -22,6 +47,12 @@ namespace FlaxEditor.Content return new SceneAnimationWindow(editor, item as AssetItem); } + /// + public override AssetItem ConstructItem(string path, string typeName, ref Guid id) + { + return new SceneAnimationItem(path, ref id, typeName, AssetType); + } + /// public override Color AccentColor => Color.FromRGB(0xff5c4a87); diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index b5e5e2464..9dc0b1719 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -568,121 +568,12 @@ namespace FlaxEditor.SceneGraph.GUI { for (int i = 0; i < _dragAssets.Objects.Count; i++) { - var assetItem = _dragAssets.Objects[i]; - - if (assetItem.IsOfType()) - { - // Create actor - var model = FlaxEngine.Content.LoadAsync(assetItem.ID); - - var actor = new AnimatedModel - { - StaticFlags = Actor.StaticFlags, - Name = assetItem.ShortName, - SkinnedModel = model, - Transform = Actor.Transform - }; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - } - else if (assetItem.IsOfType()) - { - // Create actor - var model = FlaxEngine.Content.LoadAsync(assetItem.ID); - - var actor = new StaticModel - { - StaticFlags = Actor.StaticFlags, - Name = assetItem.ShortName, - Model = model, - Transform = Actor.Transform - }; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - } - else if (assetItem.IsOfType()) - { - // Create actor - var actor = new MeshCollider - { - StaticFlags = Actor.StaticFlags, - Name = assetItem.ShortName, - CollisionData = FlaxEngine.Content.LoadAsync(assetItem.ID), - Transform = Actor.Transform - }; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - } - else if (assetItem.IsOfType()) - { - // Create actor - var actor = new ParticleEffect - { - StaticFlags = Actor.StaticFlags, - Name = assetItem.ShortName, - ParticleSystem = FlaxEngine.Content.LoadAsync(assetItem.ID), - Transform = Actor.Transform - }; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - } - else if (assetItem.IsOfType()) - { - // Create actor - var actor = new SceneAnimationPlayer - { - StaticFlags = Actor.StaticFlags, - Name = assetItem.ShortName, - Animation = FlaxEngine.Content.LoadAsync(assetItem.ID), - Transform = Actor.Transform - }; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - } - else if (assetItem.IsOfType()) - { - // Create actor - var actor = new AudioSource - { - StaticFlags = Actor.StaticFlags, - Name = assetItem.ShortName, - Clip = FlaxEngine.Content.LoadAsync(assetItem.ID), - Transform = Actor.Transform - }; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - - break; - } - else if (assetItem.IsOfType()) - { - // Create prefab instance - var prefab = FlaxEngine.Content.LoadAsync(assetItem.ID); - var actor = PrefabManager.SpawnPrefab(prefab, null); - actor.StaticFlags = Actor.StaticFlags; - actor.Name = assetItem.ShortName; - actor.Transform = Actor.Transform; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - } - else if (assetItem is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance) - { - // Create actor - var actor = (Actor)visualScriptItem.ScriptType.CreateInstance(); - actor.StaticFlags = Actor.StaticFlags; - actor.Name = assetItem.ShortName; - actor.Transform = Actor.Transform; - - // Spawn - ActorNode.Root.Spawn(actor, Actor); - } + var item = _dragAssets.Objects[i]; + var actor = item.OnEditorDrop(this); + actor.StaticFlags = Actor.StaticFlags; + actor.Name = item.ShortName; + actor.Transform = Actor.Transform; + ActorNode.Root.Spawn(actor, Actor); } result = DragDropEffect.Move; @@ -737,46 +628,12 @@ namespace FlaxEditor.SceneGraph.GUI return actorNode.Actor != null && actorNode != ActorNode && actorNode.Find(Actor) == null; } - /// - /// Validates the asset for drag and drop into one of the scene tree nodes. - /// - /// The item. - /// True if can drag and drop it, otherwise false. - public static bool ValidateDragAsset(AssetItem assetItem) + private bool ValidateDragAsset(AssetItem assetItem) { - if (assetItem.IsOfType()) - return true; - - if (assetItem.IsOfType()) - return true; - - if (assetItem.IsOfType()) - return true; - - if (assetItem.IsOfType()) - return true; - - if (assetItem.IsOfType()) - return true; - - if (assetItem.IsOfType()) - return true; - - if (assetItem.IsOfType()) - return true; - - if (assetItem is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance) - return true; - - return false; + return assetItem.OnEditorDrag(this); } - /// - /// Validates the type of the actor for drag and drop into one of the scene tree nodes. - /// - /// Type of the actor. - /// True if can drag and drop it, otherwise false. - public static bool ValidateDragActorType(ScriptType actorType) + private static bool ValidateDragActorType(ScriptType actorType) { return true; } diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index 0c895640d..ea63a598b 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -36,7 +36,7 @@ namespace FlaxEditor.Viewport private readonly ViewportWidgetButton _rotateSnapping; private readonly ViewportWidgetButton _scaleSnapping; - private readonly DragAssets _dragAssets = new DragAssets(ValidateDragItem); + private readonly DragAssets _dragAssets; private readonly DragActorType _dragActorType = new DragActorType(ValidateDragActorType); private SelectionOutline _customSelectionOutline; @@ -187,6 +187,7 @@ namespace FlaxEditor.Viewport : base(Object.New(), editor.Undo) { _editor = editor; + _dragAssets = new DragAssets(ValidateDragItem); // Prepare rendering task Task.ActorsSource = ActorsSources.Scenes; @@ -800,31 +801,19 @@ namespace FlaxEditor.Viewport return result; } - private static bool ValidateDragItem(ContentItem contentItem) + private bool ValidateDragItem(ContentItem contentItem) { if (!Level.IsAnySceneLoaded) return false; if (contentItem is AssetItem assetItem) { - if (assetItem.IsOfType()) - return true; - if (assetItem.IsOfType()) + if (assetItem.OnEditorDrag(this)) return true; if (assetItem.IsOfType()) return true; - if (assetItem.IsOfType()) - return true; - if (assetItem.IsOfType()) - return true; - if (assetItem.IsOfType()) - return true; - if (assetItem.IsOfType()) - return true; if (assetItem.IsOfType()) return true; - if (assetItem is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance) - return true; } return false; @@ -891,119 +880,40 @@ namespace FlaxEditor.Viewport private void Spawn(AssetItem item, SceneGraphNode hit, ref Vector2 location, ref Vector3 hitLocation) { - if (item is AssetItem assetItem) + if (item.IsOfType()) { - if (assetItem.IsOfType()) + if (hit is StaticModelNode staticModelNode) { - var asset = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new ParticleEffect - { - Name = item.ShortName, - ParticleSystem = asset - }; - Spawn(actor, ref hitLocation); - return; - } - if (assetItem.IsOfType()) - { - var asset = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new SceneAnimationPlayer - { - Name = item.ShortName, - Animation = asset - }; - Spawn(actor, ref hitLocation); - return; - } - if (assetItem.IsOfType()) - { - if (hit is StaticModelNode staticModelNode) - { - var staticModel = (StaticModel)staticModelNode.Actor; - var ray = ConvertMouseToRay(ref location); - if (staticModel.IntersectsEntry(ref ray, out _, out _, out var entryIndex)) - { - var material = FlaxEngine.Content.LoadAsync(item.ID); - using (new UndoBlock(Undo, staticModel, "Change material")) - staticModel.SetMaterial(entryIndex, material); - } - } - else if (hit is BoxBrushNode.SideLinkNode brushSurfaceNode) + var staticModel = (StaticModel)staticModelNode.Actor; + var ray = ConvertMouseToRay(ref location); + if (staticModel.IntersectsEntry(ref ray, out _, out _, out var entryIndex)) { var material = FlaxEngine.Content.LoadAsync(item.ID); - using (new UndoBlock(Undo, brushSurfaceNode.Brush, "Change material")) - { - var surface = brushSurfaceNode.Surface; - surface.Material = material; - brushSurfaceNode.Surface = surface; - } + using (new UndoBlock(Undo, staticModel, "Change material")) + staticModel.SetMaterial(entryIndex, material); } - return; } - if (assetItem.IsOfType()) + else if (hit is BoxBrushNode.SideLinkNode brushSurfaceNode) { - var model = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new AnimatedModel + var material = FlaxEngine.Content.LoadAsync(item.ID); + using (new UndoBlock(Undo, brushSurfaceNode.Brush, "Change material")) { - Name = item.ShortName, - SkinnedModel = model - }; - Spawn(actor, ref hitLocation); - return; - } - if (assetItem.IsOfType()) - { - var model = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new StaticModel - { - Name = item.ShortName, - Model = model - }; - Spawn(actor, ref hitLocation); - return; - } - if (assetItem.IsOfType()) - { - var collisionData = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new MeshCollider - { - Name = item.ShortName, - CollisionData = collisionData - }; - Spawn(actor, ref hitLocation); - return; - } - if (assetItem.IsOfType()) - { - var clip = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new AudioSource - { - Name = item.ShortName, - Clip = clip - }; - Spawn(actor, ref hitLocation); - return; - } - if (assetItem.IsOfType()) - { - var prefab = FlaxEngine.Content.LoadAsync(item.ID); - var actor = PrefabManager.SpawnPrefab(prefab, null); - actor.Name = item.ShortName; - Spawn(actor, ref hitLocation); - return; - } - if (assetItem.IsOfType()) - { - Editor.Instance.Scene.OpenScene(item.ID, true); - return; - } - if (assetItem is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance) - { - var actor = (Actor)visualScriptItem.ScriptType.CreateInstance(); - actor.Name = item.ShortName; - Spawn(actor, ref hitLocation); - return; + var surface = brushSurfaceNode.Surface; + surface.Material = material; + brushSurfaceNode.Surface = surface; + } } + return; + } + if (item.IsOfType()) + { + Editor.Instance.Scene.OpenScene(item.ID, true); + return; + } + { + var actor = item.OnEditorDrop(this); + actor.Name = item.ShortName; + Spawn(actor, ref hitLocation); } } diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 6483700b6..e189a8e62 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Security.Policy; using FlaxEditor.Content; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; @@ -53,7 +54,7 @@ namespace FlaxEditor.Viewport private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32); private IntPtr _debugDrawContext; private PrefabSpritesRenderer _spritesRenderer; - private readonly DragAssets _dragAssets = new DragAssets(ValidateDragItem); + private readonly DragAssets _dragAssets; private readonly DragActorType _dragActorType = new DragActorType(ValidateDragActorType); private readonly DragHandlers _dragHandlers = new DragHandlers(); @@ -89,6 +90,7 @@ namespace FlaxEditor.Viewport Undo = window.Undo; ViewportCamera = new FPSCamera(); _debugDrawContext = DebugDraw.AllocateContext(); + _dragAssets = new DragAssets(ValidateDragItem); // Prepare rendering task Task.ActorsSource = ActorsSources.CustomActors; @@ -673,24 +675,14 @@ namespace FlaxEditor.Viewport return _dragHandlers.OnDragEnter(data); } - private static bool ValidateDragItem(ContentItem contentItem) + private bool ValidateDragItem(ContentItem contentItem) { if (contentItem is AssetItem assetItem) { - if (assetItem.IsOfType()) + if (assetItem.OnEditorDrag(this)) return true; if (assetItem.IsOfType()) return true; - if (assetItem.IsOfType()) - return true; - if (assetItem.IsOfType()) - return true; - if (assetItem.IsOfType()) - return true; - if (assetItem.IsOfType()) - return true; - if (assetItem is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance) - return true; } return false; @@ -743,17 +735,6 @@ namespace FlaxEditor.Viewport { if (item is BinaryAssetItem binaryAssetItem) { - if (binaryAssetItem.Type == typeof(ParticleSystem)) - { - var particleSystem = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new ParticleEffect - { - Name = item.ShortName, - ParticleSystem = particleSystem - }; - Spawn(actor, ref hitLocation); - return; - } if (typeof(MaterialBase).IsAssignableFrom(binaryAssetItem.Type)) { if (hit is StaticModelNode staticModelNode) @@ -769,65 +750,11 @@ namespace FlaxEditor.Viewport } return; } - if (typeof(SkinnedModel).IsAssignableFrom(binaryAssetItem.Type)) - { - var model = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new AnimatedModel - { - Name = item.ShortName, - SkinnedModel = model - }; - Spawn(actor, ref hitLocation); - return; - } - if (typeof(Model).IsAssignableFrom(binaryAssetItem.Type)) - { - var model = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new StaticModel - { - Name = item.ShortName, - Model = model - }; - Spawn(actor, ref hitLocation); - return; - } - if (binaryAssetItem.IsOfType()) - { - var collisionData = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new MeshCollider - { - Name = item.ShortName, - CollisionData = collisionData - }; - Spawn(actor, ref hitLocation); - return; - } - if (typeof(AudioClip).IsAssignableFrom(binaryAssetItem.Type)) - { - var clip = FlaxEngine.Content.LoadAsync(item.ID); - var actor = new AudioSource - { - Name = item.ShortName, - Clip = clip - }; - Spawn(actor, ref hitLocation); - return; - } - if (typeof(Prefab).IsAssignableFrom(binaryAssetItem.Type)) - { - var prefab = FlaxEngine.Content.LoadAsync(item.ID); - var actor = PrefabManager.SpawnPrefab(prefab, null); - actor.Name = item.ShortName; - Spawn(actor, ref hitLocation); - return; - } } - if (item is VisualScriptItem visualScriptItem && new ScriptType(typeof(Actor)).IsAssignableFrom(visualScriptItem.ScriptType) && visualScriptItem.ScriptType.CanCreateInstance) { - var actor = (Actor)visualScriptItem.ScriptType.CreateInstance(); + var actor = item.OnEditorDrop(this); actor.Name = item.ShortName; Spawn(actor, ref hitLocation); - return; } } From 64a8408600c87962bf7a33e3f1090296eaad7cf4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 24 May 2021 18:42:15 +0200 Subject: [PATCH 028/198] Add focus to game window on play --- Source/Editor/Modules/SimulationModule.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Modules/SimulationModule.cs b/Source/Editor/Modules/SimulationModule.cs index c1f2a3e7b..3faf5cd7e 100644 --- a/Source/Editor/Modules/SimulationModule.cs +++ b/Source/Editor/Modules/SimulationModule.cs @@ -203,6 +203,7 @@ namespace FlaxEditor.Modules else if (!gameWin.IsSelected) { gameWin.SelectTab(false); + gameWin.RootWindow?.Window?.Focus(); FlaxEngine.GUI.RootControl.GameRoot.Focus(); } } From 3031e3937c2a4d0c3aafebd6197f7316aab58b06 Mon Sep 17 00:00:00 2001 From: herocrab Date: Tue, 25 May 2021 10:25:25 -0600 Subject: [PATCH 029/198] Add how-to context for building Windows + Vulkan. --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index be785107d..1c11dec82 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,16 @@ Flax Visual Studio extension provides better programming workflow, C# scripts de * Compile Flax project (hit F7 or CTRL+Shift+B) * Run Flax (hit F5 key) +--- + +**Note** + +If building on Windows to support Vulkan rendering, first install the Vulkan SDK then set an environment variable to provide the path to the SDK prior to running GenerateProjectFiles.bat: + +    set VULKAN_SDK=C:\VulkanSDK\version\ + +--- + ## Linux * Install Visual Studio Code From a6ed117088a3962e61660124dc96582d4970e686 Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Wed, 26 May 2021 19:56:39 +0300 Subject: [PATCH 030/198] Add support for Rider 2021, use latest detected version of Rider Handles additional registry location where installation paths are stored for Rider 2021. The detected installations are now sorted by version, so the latest detected version is always used when opening the scripts project. --- .../Scripting/CodeEditors/RiderCodeEditor.cpp | 97 +++++++++++++++---- 1 file changed, 77 insertions(+), 20 deletions(-) diff --git a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp index 4299b8865..2c5265a06 100644 --- a/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp +++ b/Source/Editor/Scripting/CodeEditors/RiderCodeEditor.cpp @@ -10,12 +10,24 @@ #if PLATFORM_WINDOWS +#include "Engine/Core/Collections/Sorting.h" #include "Engine/Platform/File.h" #include "Engine/Platform/Win32/IncludeWindowsHeaders.h" #include "Engine/Serialization/Json.h" namespace { + struct RiderInstallation + { + String path; + String version; + + RiderInstallation(const String& path_, const String& version_) + : path(path_), version(version_) + { + } + }; + bool FindRegistryKeyItems(HKEY hKey, Array& results) { Char nameBuffer[256]; @@ -31,7 +43,7 @@ namespace return true; } - void SearchDirectory(Array* output, const String& directory) + void SearchDirectory(Array* installations, const String& directory) { if (!FileSystem::DirectoryExists(directory)) return; @@ -46,24 +58,29 @@ namespace if (document.HasParseError()) return; + // Find version + auto versionMember = document.FindMember("version"); + if (versionMember == document.MemberEnd()) + return; + // Find executable file path auto launchMember = document.FindMember("launch"); - if (launchMember != document.MemberEnd() && launchMember->value.IsArray() && launchMember->value.Size() > 0) - { - auto launcherPathMember = launchMember->value[0].FindMember("launcherPath"); - if (launcherPathMember != launchMember->value[0].MemberEnd()) - { - auto launcherPath = launcherPathMember->value.GetText(); - auto exePath = directory / launcherPath; - if (launcherPath.HasChars() && FileSystem::FileExists(exePath)) - { - output->Add(New(exePath)); - } - } - } + if (launchMember == document.MemberEnd() || !launchMember->value.IsArray() || launchMember->value.Size() == 0) + return; + + auto launcherPathMember = launchMember->value[0].FindMember("launcherPath"); + if (launcherPathMember == launchMember->value[0].MemberEnd()) + return; + + auto launcherPath = launcherPathMember->value.GetText(); + auto exePath = directory / launcherPath; + if (!launcherPath.HasChars() || !FileSystem::FileExists(exePath)) + return; + + installations->Add(New(exePath, versionMember->value.GetText())); } - void SearchRegistry(Array* output, HKEY root, const Char* key) + void SearchRegistry(Array* installations, HKEY root, const Char* key, const Char* valueName = TEXT("")) { // Open key HKEY keyH; @@ -83,14 +100,14 @@ namespace // Read subkey value DWORD type; DWORD cbData; - if (RegQueryValueExW(subKeyH, TEXT(""), nullptr, &type, nullptr, &cbData) != ERROR_SUCCESS || type != REG_SZ) + if (RegQueryValueExW(subKeyH, valueName, nullptr, &type, nullptr, &cbData) != ERROR_SUCCESS || type != REG_SZ) { RegCloseKey(subKeyH); continue; } Array data; data.Resize((int32)cbData / sizeof(Char)); - if (RegQueryValueExW(subKeyH, TEXT(""), nullptr, nullptr, reinterpret_cast(data.Get()), &cbData) != ERROR_SUCCESS) + if (RegQueryValueExW(subKeyH, valueName, nullptr, nullptr, reinterpret_cast(data.Get()), &cbData) != ERROR_SUCCESS) { RegCloseKey(subKeyH); continue; @@ -98,7 +115,7 @@ namespace // Check if it's a valid installation path String path(data.Get(), data.Count() - 1); - SearchDirectory(output, path); + SearchDirectory(installations, path); RegCloseKey(subKeyH); } @@ -108,6 +125,31 @@ namespace } } +bool sortInstallations(RiderInstallation* const& i1, RiderInstallation* const& i2) +{ + Array values1, values2; + i1->version.Split('.', values1); + i2->version.Split('.', values2); + + int32 version1[3] = { 0 }; + int32 version2[3] = { 0 }; + StringUtils::Parse(values1[0].Get(), &version1[0]); + StringUtils::Parse(values1[1].Get(), &version1[1]); + StringUtils::Parse(values1[2].Get(), &version1[2]); + StringUtils::Parse(values2[0].Get(), &version2[0]); + StringUtils::Parse(values2[1].Get(), &version2[1]); + StringUtils::Parse(values2[2].Get(), &version2[2]); + + // Compare by MAJOR.MINOR.BUILD + if (version1[0] == version2[0]) + { + if (version1[1] == version2[1]) + return version1[2] > version2[2]; + return version1[1] > version2[1]; + } + return version1[0] > version2[0]; +} + #endif RiderCodeEditor::RiderCodeEditor(const String& execPath) @@ -119,8 +161,23 @@ RiderCodeEditor::RiderCodeEditor(const String& execPath) void RiderCodeEditor::FindEditors(Array* output) { #if PLATFORM_WINDOWS - SearchRegistry(output, HKEY_CURRENT_USER, TEXT("SOFTWARE\\WOW6432Node\\JetBrains\\JetBrains Rider")); - SearchRegistry(output, HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\WOW6432Node\\JetBrains\\JetBrains Rider")); + Array installations; + + // For versions 2021 or later + SearchRegistry(&installations, HKEY_CURRENT_USER, TEXT("SOFTWARE\\JetBrains\\Rider"), TEXT("InstallDir")); + + // For versions 2020 or earlier + SearchRegistry(&installations, HKEY_CURRENT_USER, TEXT("SOFTWARE\\WOW6432Node\\JetBrains\\JetBrains Rider")); + SearchRegistry(&installations, HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\WOW6432Node\\JetBrains\\JetBrains Rider")); + + // Sort found installations by version number + Sorting::QuickSort(installations.Get(), installations.Count(), &sortInstallations); + + for (RiderInstallation* installation : installations) + { + output->Add(New(installation->path)); + Delete(installation); + } #endif } From d1fbe42443dfbdfaa9ded46fcd39f3cb93c93167 Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Wed, 26 May 2021 22:00:45 +0300 Subject: [PATCH 031/198] Use verbatim strings in tooltip descriptions Additionally escapes backslashes in the tooltip string. Only character that needs to be manually escaped is the double quotation marks. --- Source/Engine/Input/KeyboardKeys.h | 2 +- Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Input/KeyboardKeys.h b/Source/Engine/Input/KeyboardKeys.h index fe6daa12b..12f79ea1f 100644 --- a/Source/Engine/Input/KeyboardKeys.h +++ b/Source/Engine/Input/KeyboardKeys.h @@ -745,7 +745,7 @@ API_ENUM() enum class KeyboardKeys LeftBracket = 0xDB, /// - /// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard the '\\|' key + /// Used for miscellaneous characters; it can vary by keyboard. For the US standard keyboard the '\|' key /// Backslash = 0xDC, diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 26fa55231..cff6be5a5 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -469,9 +469,8 @@ namespace Flax.Build.Bindings if (comment[i - 1].StartsWith("/// ")) tooltip += " " + comment[i - 1].Substring(4); } - if (tooltip.IndexOf('\"') != -1) - tooltip = tooltip.Replace("\"", "\\\""); - contents.Append(indent).Append("[Tooltip(\"").Append(tooltip).Append("\")]").AppendLine(); + tooltip = tooltip.Replace("\"", "\"\""); + contents.Append(indent).Append("[Tooltip(@\"").Append(tooltip).Append("\")]").AppendLine(); } } if (writeDefaultValue) From 2b7b9e4d5ac9a27d3255c13cc8eecabd8d3a4b93 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 May 2021 22:40:09 +0200 Subject: [PATCH 032/198] Fix typo in wheel type name --- Source/Engine/Physics/Actors/WheeledVehicle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.h b/Source/Engine/Physics/Actors/WheeledVehicle.h index bb947d68d..1941038bf 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.h +++ b/Source/Engine/Physics/Actors/WheeledVehicle.h @@ -156,7 +156,7 @@ public: // Left wheel of the rear axle. RearLeft, // Right wheel of the rear axle. - ReadRight, + RearRight, // Non-drivable wheel. NoDrive, }; From 42dfc56bae8e016b98f557c6256c230a8db1df2f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 May 2021 23:24:16 +0200 Subject: [PATCH 033/198] Fix checking paths when opening Visual Studio project items to prevent dummy files creation --- .../VisualStudio/VisualStudioConnection.cpp | 30 ++----------------- .../VisualStudio/VisualStudioEditor.cpp | 5 +++- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp index 54ba119f2..ffb4ff7b3 100644 --- a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp +++ b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioConnection.cpp @@ -152,34 +152,10 @@ public: namespace VisualStudio { - bool SameFile(HANDLE h1, HANDLE h2) - { - BY_HANDLE_FILE_INFORMATION bhfi1 = { 0 }; - BY_HANDLE_FILE_INFORMATION bhfi2 = { 0 }; - - if (::GetFileInformationByHandle(h1, &bhfi1) && ::GetFileInformationByHandle(h2, &bhfi2)) - { - return ((bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh) && (bhfi1.nFileIndexLow == bhfi2.nFileIndexLow) && (bhfi1.dwVolumeSerialNumber == bhfi2.dwVolumeSerialNumber)); - } - - return false; - } - bool AreFilePathsEqual(const wchar_t* path1, const wchar_t* path2) - { - if (wcscmp(path1, path2) == 0) - return true; - - HANDLE file1 = CreateFileW(path1, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - HANDLE file2 = CreateFileW(path2, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - - bool result = SameFile(file1, file2); - - CloseHandle(file1); - CloseHandle(file2); - - return result; - } + { + return _wcsicmp(path1, path2) == 0; + } class ConnectionInternal { diff --git a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.cpp b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.cpp index dde2455a7..e8b221a5d 100644 --- a/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.cpp +++ b/Source/Editor/Scripting/CodeEditors/VisualStudio/VisualStudioEditor.cpp @@ -44,6 +44,7 @@ VisualStudioEditor::VisualStudioEditor(VisualStudioVersion version, const String break; } _solutionPath = Globals::ProjectFolder / Editor::Project->Name + TEXT(".sln"); + _solutionPath.Replace('/', '\\'); // Use Windows-style path separators } void VisualStudioEditor::FindEditors(Array* output) @@ -145,7 +146,9 @@ void VisualStudioEditor::OpenFile(const String& path, int32 line) // Open file const VisualStudio::Connection connection(*_CLSID, *_solutionPath); - const auto result = connection.OpenFile(*path, line); + String tmp = path; + tmp.Replace('/', '\\'); // Use Windows-style path separators + const auto result = connection.OpenFile(*tmp, line); if (result.Failed()) { LOG(Warning, "Cannot open file \'{0}\':{1}. {2}.", path, line, String(result.Message.c_str())); From 2a6f1df76e098af599a5ee18136237d1e6bb9da2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 26 May 2021 23:26:41 +0200 Subject: [PATCH 034/198] Fix undo errors in editor on Margin property serialization --- Source/Engine/Serialization/JsonConverters.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Engine/Serialization/JsonConverters.cs b/Source/Engine/Serialization/JsonConverters.cs index 2ebf16a79..149bdc241 100644 --- a/Source/Engine/Serialization/JsonConverters.cs +++ b/Source/Engine/Serialization/JsonConverters.cs @@ -123,6 +123,13 @@ namespace FlaxEngine.Json writer.WriteStartObject(); { +#if FLAX_EDITOR + if ((serializer.TypeNameHandling & TypeNameHandling.Objects) == TypeNameHandling.Objects) + { + writer.WritePropertyName("$type"); + writer.WriteValue("FlaxEngine.Margin, FlaxEngine.CSharp"); + } +#endif writer.WritePropertyName("Left"); writer.WriteValue(valueMargin.Left); writer.WritePropertyName("Right"); From b3d9ec687dcc8bf306a29b4f6c827af3990d3076 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 27 May 2021 23:34:55 +0200 Subject: [PATCH 035/198] Refactor base types initialization to be done during API processing --- .../Tools/Flax.Build/Bindings/ApiTypeInfo.cs | 7 + .../Bindings/BindingsGenerator.Api.cs | 13 +- .../Bindings/BindingsGenerator.CSharp.cs | 8 +- .../Bindings/BindingsGenerator.Cache.cs | 2 +- .../Bindings/BindingsGenerator.Cpp.cs | 10 +- .../Bindings/BindingsGenerator.Parsing.cs | 44 ++--- .../Flax.Build/Bindings/BindingsGenerator.cs | 150 +----------------- Source/Tools/Flax.Build/Bindings/ClassInfo.cs | 85 ++++++++-- .../Flax.Build/Bindings/ClassStructInfo.cs | 33 ++-- Source/Tools/Flax.Build/Bindings/FileInfo.cs | 13 ++ .../Flax.Build/Bindings/StructureInfo.cs | 27 +++- Source/Tools/Flax.Build/Bindings/TypeInfo.cs | 3 +- 12 files changed, 185 insertions(+), 210 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs b/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs index 3e2ad1b31..6667aab9f 100644 --- a/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ApiTypeInfo.cs @@ -71,6 +71,13 @@ namespace Flax.Build.Bindings } } + public void EnsureInited(Builder.BuildData buildData) + { + if (IsInited) + return; + Init(buildData); + } + public virtual void Init(Builder.BuildData buildData) { IsInited = true; diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs index 27c902a5c..ded954e5a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs @@ -261,7 +261,7 @@ namespace Flax.Build.Bindings { if (typeInfo == null) return null; - var result = FindApiTypeInfoInner(typeInfo, caller); + var result = FindApiTypeInfoInner(buildData, typeInfo, caller); if (result != null) return result; if (buildData.TypeCache.TryGetValue(typeInfo, out result)) @@ -274,7 +274,7 @@ namespace Flax.Build.Bindings // Find across all loaded modules for this build foreach (var e in buildData.ModulesInfo) { - result = FindApiTypeInfoInner(typeInfo, e.Value); + result = FindApiTypeInfoInner(buildData, typeInfo, e.Value); if (result != null) { buildData.TypeCache.Add(typeInfo, result); @@ -291,7 +291,7 @@ namespace Flax.Build.Bindings { if (result == null) return null; - result = FindApiTypeInfoInner(new TypeInfo { Type = nesting[i], }, result); + result = FindApiTypeInfoInner(buildData, new TypeInfo { Type = nesting[i], }, result); } return result; } @@ -301,14 +301,17 @@ namespace Flax.Build.Bindings return null; } - private static ApiTypeInfo FindApiTypeInfoInner(TypeInfo typeInfo, ApiTypeInfo parent) + private static ApiTypeInfo FindApiTypeInfoInner(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo parent) { foreach (var child in parent.Children) { if (child.Name == typeInfo.Type) + { + child.EnsureInited(buildData); return child; + } - var result = FindApiTypeInfoInner(typeInfo, child); + var result = FindApiTypeInfoInner(buildData, typeInfo, child); if (result != null) return result; } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 26fa55231..cf30bfae3 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -14,7 +14,7 @@ namespace Flax.Build.Bindings private static readonly HashSet CSharpUsedNamespaces = new HashSet(); private static readonly List CSharpUsedNamespacesSorted = new List(); - private static readonly Dictionary CSharpNativeToManagedBasicTypes = new Dictionary() + internal static readonly Dictionary CSharpNativeToManagedBasicTypes = new Dictionary() { // Language types { "int8", "sbyte" }, @@ -32,7 +32,7 @@ namespace Flax.Build.Bindings { "double", "double" }, }; - private static readonly Dictionary CSharpNativeToManagedDefault = new Dictionary() + internal static readonly Dictionary CSharpNativeToManagedDefault = new Dictionary() { // Engine types { "String", "string" }, @@ -527,7 +527,7 @@ namespace Flax.Build.Bindings contents.Append("abstract "); contents.Append("unsafe partial class ").Append(classInfo.Name); if (classInfo.BaseType != null && !classInfo.IsBaseTypeHidden) - contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, classInfo.BaseType, classInfo)); + contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, new TypeInfo { Type = classInfo.BaseType.Name }, classInfo)); contents.AppendLine(); contents.Append(indent + "{"); indent += " "; @@ -861,7 +861,7 @@ namespace Flax.Build.Bindings contents.Append("private "); contents.Append("unsafe partial struct ").Append(structureInfo.Name); if (structureInfo.BaseType != null && structureInfo.IsPod) - contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, structureInfo.BaseType, structureInfo)); + contents.Append(" : ").Append(GenerateCSharpNativeToManaged(buildData, new TypeInfo { Type = structureInfo.BaseType.Name }, structureInfo)); contents.AppendLine(); contents.Append(indent + "{"); indent += " "; diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs index 8fc7d18f2..0bb1f4cc7 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cache.cs @@ -19,7 +19,7 @@ namespace Flax.Build.Bindings partial class BindingsGenerator { private static readonly Dictionary TypeCache = new Dictionary(); - private const int CacheVersion = 7; + private const int CacheVersion = 8; internal static void Write(BinaryWriter writer, string e) { diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 78fd3df73..a2fffddd4 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1095,7 +1095,7 @@ namespace Flax.Build.Bindings var baseType = classInfo?.BaseType ?? structureInfo?.BaseType; if (classInfo != null && classInfo.IsBaseTypeHidden) baseType = null; - if (baseType != null && (baseType.Type == "PersistentScriptingObject" || baseType.Type == "ScriptingObject")) + if (baseType != null && (baseType.Name == "PersistentScriptingObject" || baseType.Name == "ScriptingObject")) baseType = null; CppAutoSerializeFields.Clear(); CppAutoSerializeProperties.Clear(); @@ -1105,7 +1105,7 @@ namespace Flax.Build.Bindings contents.Append($"void {typeNameNative}::Serialize(SerializeStream& stream, const void* otherObj)").AppendLine(); contents.Append('{').AppendLine(); if (baseType != null) - contents.Append($" {baseType}::Serialize(stream, otherObj);").AppendLine(); + contents.Append($" {baseType.FullNameNative}::Serialize(stream, otherObj);").AppendLine(); contents.Append($" SERIALIZE_GET_OTHER_OBJ({typeNameNative});").AppendLine(); if (classInfo != null) @@ -1161,7 +1161,7 @@ namespace Flax.Build.Bindings contents.Append($"void {typeNameNative}::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)").AppendLine(); contents.Append('{').AppendLine(); if (baseType != null) - contents.Append($" {baseType}::Deserialize(stream, modifier);").AppendLine(); + contents.Append($" {baseType.FullNameNative}::Deserialize(stream, modifier);").AppendLine(); foreach (var fieldInfo in CppAutoSerializeFields) { @@ -1554,7 +1554,7 @@ namespace Flax.Build.Bindings else contents.Append($"(ScriptingType::SpawnHandler)&{classTypeNameNative}::Spawn, "); if (classInfo.BaseType != null && useScripting) - contents.Append($"&{classInfo.BaseType}::TypeInitializer, "); + contents.Append($"&{classInfo.BaseType.FullNameNative}::TypeInitializer, "); else contents.Append("nullptr, "); contents.Append(setupScriptVTable); @@ -1566,7 +1566,7 @@ namespace Flax.Build.Bindings else contents.Append($"&{classTypeNameInternal}Internal::Ctor, &{classTypeNameInternal}Internal::Dtor, "); if (classInfo.BaseType != null) - contents.Append($"&{classInfo.BaseType}::TypeInitializer"); + contents.Append($"&{classInfo.BaseType.FullNameNative}::TypeInitializer"); else contents.Append("nullptr"); } diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index 67e90efa9..8ed4a1634 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -416,38 +416,38 @@ namespace Flax.Build.Bindings token = accessToken; break; } - - var baseTypeInfo = new TypeInfo + var inheritType = new TypeInfo { Type = token.Value, }; - if (token.Value.Length > 2 && token.Value[0] == 'I' && char.IsUpper(token.Value[1])) - { - // Interface - if (desc.InterfaceNames == null) - desc.InterfaceNames = new List(); - desc.InterfaceNames.Add(baseTypeInfo); - token = context.Tokenizer.NextToken(); - continue; - } - - if (desc.BaseType != null) - { - // Allow for multiple base classes, just the first one needs to be a valid base type - break; - throw new Exception($"Invalid '{desc.Name}' inheritance (only single base class is allowed for scripting types, excluding interfaces)."); - } - desc.BaseType = baseTypeInfo; + if (desc.Inheritance == null) + desc.Inheritance = new List(); + desc.Inheritance.Add(inheritType); token = context.Tokenizer.NextToken(); if (token.Type == TokenType.LeftCurlyBrace) { break; } + if (token.Type == TokenType.Colon) + { + token = context.Tokenizer.ExpectToken(TokenType.Colon); + token = context.Tokenizer.NextToken(); + inheritType.Type = token.Value; + token = context.Tokenizer.NextToken(); + continue; + } + if (token.Type == TokenType.DoubleColon) + { + token = context.Tokenizer.NextToken(); + inheritType.Type += token.Value; + token = context.Tokenizer.NextToken(); + continue; + } if (token.Type == TokenType.LeftAngleBracket) { var genericType = context.Tokenizer.ExpectToken(TokenType.Identifier); token = context.Tokenizer.ExpectToken(TokenType.RightAngleBracket); - desc.BaseType.GenericArgs = new List + inheritType.GenericArgs = new List { new TypeInfo { @@ -456,9 +456,9 @@ namespace Flax.Build.Bindings }; // TODO: find better way to resolve this (custom base type attribute?) - if (desc.BaseType.Type == "ShaderAssetTypeBase") + if (inheritType.Type == "ShaderAssetTypeBase") { - desc.BaseType = desc.BaseType.GenericArgs[0]; + desc.Inheritance[desc.Inheritance.Count - 1] = inheritType.GenericArgs[0]; } token = context.Tokenizer.NextToken(); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs index f8d26314f..fa219477a 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Flax.Build.NativeCpp; @@ -109,13 +108,6 @@ namespace Flax.Build.Bindings if (LoadCache(ref moduleInfo, moduleOptions, headerFiles)) { buildData.ModulesInfo[module] = moduleInfo; - - // Initialize API - using (new ProfileEventScope("Init")) - { - moduleInfo.Init(buildData); - } - return moduleInfo; } } @@ -164,12 +156,6 @@ namespace Flax.Build.Bindings } } - // Initialize API - using (new ProfileEventScope("Init")) - { - moduleInfo.Init(buildData); - } - return moduleInfo; } @@ -533,23 +519,10 @@ namespace Flax.Build.Bindings if (moduleInfo.IsFromCache) return; - // Process parsed API - using (new ProfileEventScope("Process")) + // Initialize parsed API + using (new ProfileEventScope("Init")) { - foreach (var child in moduleInfo.Children) - { - try - { - foreach (var apiTypeInfo in child.Children) - ProcessAndValidate(buildData, apiTypeInfo); - } - catch (Exception) - { - if (child is FileInfo fileInfo) - Log.Error($"Failed to validate '{fileInfo.Name}' file to generate bindings."); - throw; - } - } + moduleInfo.Init(buildData); } // Generate bindings for scripting @@ -583,122 +556,5 @@ namespace Flax.Build.Bindings } } } - - private static void ProcessAndValidate(BuildData buildData, ApiTypeInfo apiTypeInfo) - { - if (apiTypeInfo is ClassInfo classInfo) - ProcessAndValidate(buildData, classInfo); - else if (apiTypeInfo is StructureInfo structureInfo) - ProcessAndValidate(buildData, structureInfo); - - foreach (var child in apiTypeInfo.Children) - ProcessAndValidate(buildData, child); - } - - private static void ProcessAndValidate(BuildData buildData, ClassInfo classInfo) - { - if (classInfo.UniqueFunctionNames == null) - classInfo.UniqueFunctionNames = new HashSet(); - - foreach (var fieldInfo in classInfo.Fields) - { - if (fieldInfo.Access == AccessLevel.Private) - continue; - - fieldInfo.Getter = new FunctionInfo - { - Name = "Get" + fieldInfo.Name, - Comment = fieldInfo.Comment, - IsStatic = fieldInfo.IsStatic, - Access = fieldInfo.Access, - Attributes = fieldInfo.Attributes, - ReturnType = fieldInfo.Type, - Parameters = new List(), - IsVirtual = false, - IsConst = true, - Glue = new FunctionInfo.GlueInfo() - }; - ProcessAndValidate(classInfo, fieldInfo.Getter); - fieldInfo.Getter.Name = fieldInfo.Name; - - if (!fieldInfo.IsReadOnly) - { - fieldInfo.Setter = new FunctionInfo - { - Name = "Set" + fieldInfo.Name, - Comment = fieldInfo.Comment, - IsStatic = fieldInfo.IsStatic, - Access = fieldInfo.Access, - Attributes = fieldInfo.Attributes, - ReturnType = new TypeInfo - { - Type = "void", - }, - Parameters = new List - { - new FunctionInfo.ParameterInfo - { - Name = "value", - Type = fieldInfo.Type, - }, - }, - IsVirtual = false, - IsConst = true, - Glue = new FunctionInfo.GlueInfo() - }; - ProcessAndValidate(classInfo, fieldInfo.Setter); - fieldInfo.Setter.Name = fieldInfo.Name; - } - } - - foreach (var propertyInfo in classInfo.Properties) - { - if (propertyInfo.Getter != null) - ProcessAndValidate(classInfo, propertyInfo.Getter); - if (propertyInfo.Setter != null) - ProcessAndValidate(classInfo, propertyInfo.Setter); - } - - foreach (var functionInfo in classInfo.Functions) - ProcessAndValidate(classInfo, functionInfo); - } - - private static void ProcessAndValidate(BuildData buildData, StructureInfo structureInfo) - { - foreach (var fieldInfo in structureInfo.Fields) - { - if (fieldInfo.Type.IsBitField) - throw new NotImplementedException($"TODO: support bit-fields in structure fields (found field {fieldInfo} in structure {structureInfo.Name})"); - - // Pointers are fine - if (fieldInfo.Type.IsPtr) - continue; - - // In-build types - if (CSharpNativeToManagedBasicTypes.ContainsKey(fieldInfo.Type.Type)) - continue; - if (CSharpNativeToManagedDefault.ContainsKey(fieldInfo.Type.Type)) - continue; - - // Find API type info for this field type - var apiType = FindApiTypeInfo(buildData, fieldInfo.Type, structureInfo); - if (apiType != null) - continue; - - throw new Exception($"Unknown field type '{fieldInfo.Type} {fieldInfo.Name}' in structure '{structureInfo.Name}'."); - } - } - - private static void ProcessAndValidate(ClassInfo classInfo, FunctionInfo functionInfo) - { - // Ensure that methods have unique names for bindings - if (classInfo.UniqueFunctionNames == null) - classInfo.UniqueFunctionNames = new HashSet(); - int idx = 1; - functionInfo.UniqueName = functionInfo.Name; - while (classInfo.UniqueFunctionNames.Contains(functionInfo.UniqueName)) - functionInfo.UniqueName = functionInfo.Name + idx++; - classInfo.UniqueFunctionNames.Add(functionInfo.UniqueName); - } } } diff --git a/Source/Tools/Flax.Build/Bindings/ClassInfo.cs b/Source/Tools/Flax.Build/Bindings/ClassInfo.cs index c68ff1a59..ff0bb077b 100644 --- a/Source/Tools/Flax.Build/Bindings/ClassInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ClassInfo.cs @@ -57,22 +57,87 @@ namespace Flax.Build.Bindings _isScriptingObject = true; else if (BaseType == null) _isScriptingObject = false; - else if (InBuildScriptingObjectTypes.Contains(BaseType.Type)) + else if (InBuildScriptingObjectTypes.Contains(BaseType.Name)) _isScriptingObject = true; else + _isScriptingObject = BaseType != null && BaseType.IsScriptingObject; + + if (UniqueFunctionNames == null) + UniqueFunctionNames = new HashSet(); + + foreach (var fieldInfo in Fields) { - var baseApiTypeInfo = BindingsGenerator.FindApiTypeInfo(buildData, BaseType, this); - if (baseApiTypeInfo != null) + if (fieldInfo.Access == AccessLevel.Private) + continue; + + fieldInfo.Getter = new FunctionInfo { - if (!baseApiTypeInfo.IsInited) - baseApiTypeInfo.Init(buildData); - _isScriptingObject = baseApiTypeInfo.IsScriptingObject; - } - else + Name = "Get" + fieldInfo.Name, + Comment = fieldInfo.Comment, + IsStatic = fieldInfo.IsStatic, + Access = fieldInfo.Access, + Attributes = fieldInfo.Attributes, + ReturnType = fieldInfo.Type, + Parameters = new List(), + IsVirtual = false, + IsConst = true, + Glue = new FunctionInfo.GlueInfo() + }; + ProcessAndValidate(fieldInfo.Getter); + fieldInfo.Getter.Name = fieldInfo.Name; + + if (!fieldInfo.IsReadOnly) { - _isScriptingObject = false; + fieldInfo.Setter = new FunctionInfo + { + Name = "Set" + fieldInfo.Name, + Comment = fieldInfo.Comment, + IsStatic = fieldInfo.IsStatic, + Access = fieldInfo.Access, + Attributes = fieldInfo.Attributes, + ReturnType = new TypeInfo + { + Type = "void", + }, + Parameters = new List + { + new FunctionInfo.ParameterInfo + { + Name = "value", + Type = fieldInfo.Type, + }, + }, + IsVirtual = false, + IsConst = true, + Glue = new FunctionInfo.GlueInfo() + }; + ProcessAndValidate(fieldInfo.Setter); + fieldInfo.Setter.Name = fieldInfo.Name; } } + + foreach (var propertyInfo in Properties) + { + if (propertyInfo.Getter != null) + ProcessAndValidate(propertyInfo.Getter); + if (propertyInfo.Setter != null) + ProcessAndValidate(propertyInfo.Setter); + } + + foreach (var functionInfo in Functions) + ProcessAndValidate(functionInfo); + } + + private void ProcessAndValidate(FunctionInfo functionInfo) + { + // Ensure that methods have unique names for bindings + if (UniqueFunctionNames == null) + UniqueFunctionNames = new HashSet(); + int idx = 1; + functionInfo.UniqueName = functionInfo.Name; + while (UniqueFunctionNames.Contains(functionInfo.UniqueName)) + functionInfo.UniqueName = functionInfo.Name + idx++; + UniqueFunctionNames.Add(functionInfo.UniqueName); } public override void Write(BinaryWriter writer) @@ -113,7 +178,7 @@ namespace Flax.Build.Bindings { if (_scriptVTableSize == -1) { - if (BindingsGenerator.FindApiTypeInfo(buildData, BaseType, this) is ClassInfo baseApiTypeInfo) + if (BaseType is ClassInfo baseApiTypeInfo) { _scriptVTableOffset = baseApiTypeInfo.GetScriptVTableSize(buildData, out _); } diff --git a/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs b/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs index 2f0dad003..ca1bb9257 100644 --- a/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/ClassStructInfo.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System; using System.Collections.Generic; using System.IO; @@ -12,29 +13,37 @@ namespace Flax.Build.Bindings { public AccessLevel Access; public AccessLevel BaseTypeInheritance; - public TypeInfo BaseType; - public List Interfaces; // Optional - public List InterfaceNames; // Optional + public ClassStructInfo BaseType; + public List Interfaces; + public List Inheritance; // Data from parsing, used to interfaces and base type construct in Init public override void Init(Builder.BuildData buildData) { base.Init(buildData); - if (Interfaces == null && InterfaceNames != null && InterfaceNames.Count != 0) + if (BaseType == null && Interfaces == null && Inheritance != null) { - Interfaces = new List(); - for (var i = 0; i < InterfaceNames.Count; i++) + // Extract base class and interfaces from inheritance info + for (int i = 0; i < Inheritance.Count; i++) { - var interfaceName = InterfaceNames[i]; - var apiTypeInfo = BindingsGenerator.FindApiTypeInfo(buildData, interfaceName, this); + var apiTypeInfo = BindingsGenerator.FindApiTypeInfo(buildData, Inheritance[i], Parent); if (apiTypeInfo is InterfaceInfo interfaceInfo) { + if (Interfaces == null) + Interfaces = new List(); Interfaces.Add(interfaceInfo); } + else if (apiTypeInfo is ClassStructInfo otherInfo) + { + if (otherInfo == this) + throw new Exception($"Type '{Name}' inherits from itself."); + if (BaseType != null) + throw new Exception($"Invalid '{Name}' inheritance (only single base class is allowed for scripting types, excluding interfaces)."); + BaseType = otherInfo; + } } - if (Interfaces.Count == 0) - Interfaces = null; } + BaseType?.EnsureInited(buildData); } public override void Write(BinaryWriter writer) @@ -42,7 +51,7 @@ namespace Flax.Build.Bindings writer.Write((byte)Access); writer.Write((byte)BaseTypeInheritance); BindingsGenerator.Write(writer, BaseType); - BindingsGenerator.Write(writer, InterfaceNames); + BindingsGenerator.Write(writer, Inheritance); base.Write(writer); } @@ -52,7 +61,7 @@ namespace Flax.Build.Bindings Access = (AccessLevel)reader.ReadByte(); BaseTypeInheritance = (AccessLevel)reader.ReadByte(); BaseType = BindingsGenerator.Read(reader, BaseType); - InterfaceNames = BindingsGenerator.Read(reader, InterfaceNames); + Inheritance = BindingsGenerator.Read(reader, Inheritance); base.Read(reader); } diff --git a/Source/Tools/Flax.Build/Bindings/FileInfo.cs b/Source/Tools/Flax.Build/Bindings/FileInfo.cs index 6c4b4dcfe..7a38c8d87 100644 --- a/Source/Tools/Flax.Build/Bindings/FileInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/FileInfo.cs @@ -17,6 +17,19 @@ namespace Flax.Build.Bindings base.AddChild(apiTypeInfo); } + public override void Init(Builder.BuildData buildData) + { + try + { + base.Init(buildData); + } + catch (Exception) + { + Log.Error($"Failed to init '{Name}' file scripting API."); + throw; + } + } + public int CompareTo(FileInfo other) { return Name.CompareTo(other.Name); diff --git a/Source/Tools/Flax.Build/Bindings/StructureInfo.cs b/Source/Tools/Flax.Build/Bindings/StructureInfo.cs index 5b664cc09..7e6888d91 100644 --- a/Source/Tools/Flax.Build/Bindings/StructureInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/StructureInfo.cs @@ -26,14 +26,14 @@ namespace Flax.Build.Bindings { base.Init(buildData); - if (ForceNoPod || (InterfaceNames != null && InterfaceNames.Count != 0)) + if (ForceNoPod || (Interfaces != null && Interfaces.Count != 0)) { _isPod = false; return; } // Structure is POD (plain old data) only if all of it's fields are (and has no base type ro base type is also POD) - _isPod = BaseType == null || (BindingsGenerator.FindApiTypeInfo(buildData, BaseType, Parent)?.IsPod ?? false); + _isPod = BaseType == null || (BaseType?.IsPod ?? false); for (int i = 0; _isPod && i < Fields.Count; i++) { var field = Fields[i]; @@ -42,6 +42,29 @@ namespace Flax.Build.Bindings _isPod = false; } } + + foreach (var fieldInfo in Fields) + { + if (fieldInfo.Type.IsBitField) + throw new NotImplementedException($"TODO: support bit-fields in structure fields (found field {fieldInfo} in structure {Name})"); + + // Pointers are fine + if (fieldInfo.Type.IsPtr) + continue; + + // In-build types + if (BindingsGenerator.CSharpNativeToManagedBasicTypes.ContainsKey(fieldInfo.Type.Type)) + continue; + if (BindingsGenerator.CSharpNativeToManagedDefault.ContainsKey(fieldInfo.Type.Type)) + continue; + + // Find API type info for this field type + var apiType = BindingsGenerator.FindApiTypeInfo(buildData, fieldInfo.Type, this); + if (apiType != null) + continue; + + throw new Exception($"Unknown field type '{fieldInfo.Type} {fieldInfo.Name}' in structure '{Name}'."); + } } public override void Write(BinaryWriter writer) diff --git a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs index 443b0687a..9268e5998 100644 --- a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs @@ -36,8 +36,7 @@ namespace Flax.Build.Bindings var apiType = BindingsGenerator.FindApiTypeInfo(buildData, this, caller); if (apiType != null) { - if (!apiType.IsInited) - apiType.Init(buildData); + apiType.EnsureInited(buildData); return apiType.IsPod; } From 810623be61c2d6e5f9dea6d10eb01e8420a9b06a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 27 May 2021 23:36:07 +0200 Subject: [PATCH 036/198] Add automatic namespoace usings collecting when generating scripting bindings glue code for csharp --- .../Bindings/BindingsGenerator.CSharp.cs | 58 ++++++------------- 1 file changed, 19 insertions(+), 39 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index cf30bfae3..37e5dda23 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -201,6 +201,8 @@ namespace Flax.Build.Bindings var apiType = FindApiTypeInfo(buildData, typeInfo, caller); if (apiType != null) { + CSharpUsedNamespaces.Add(apiType.Namespace); + if (apiType.IsScriptingObject) return typeInfo.Type.Replace("::", "."); @@ -1048,30 +1050,6 @@ namespace Flax.Build.Bindings return true; } - private static void GenerateCSharpCollectNamespaces(BuildData buildData, ApiTypeInfo apiType, HashSet usedNamespaces) - { - if (apiType is ClassInfo classInfo) - { - foreach (var field in classInfo.Fields) - { - var fieldInfo = FindApiTypeInfo(buildData, field.Type, classInfo); - if (fieldInfo != null && !string.IsNullOrWhiteSpace(fieldInfo.Namespace) && fieldInfo.Namespace != apiType.Namespace) - usedNamespaces.Add(fieldInfo.Namespace); - } - } - else if (apiType is StructureInfo structureInfo) - { - foreach (var field in structureInfo.Fields) - { - var fieldInfo = FindApiTypeInfo(buildData, field.Type, structureInfo); - if (fieldInfo != null && !string.IsNullOrWhiteSpace(fieldInfo.Namespace) && fieldInfo.Namespace != apiType.Namespace) - usedNamespaces.Add(fieldInfo.Namespace); - } - } - foreach (var child in apiType.Children) - GenerateCSharpCollectNamespaces(buildData, child, usedNamespaces); - } - private static void GenerateCSharp(BuildData buildData, ModuleInfo moduleInfo, ref BindingsResult bindings) { var contents = new StringBuilder(); @@ -1084,29 +1062,17 @@ namespace Flax.Build.Bindings contents.Append("true").AppendLine(); contents.AppendLine("// This code was auto-generated. Do not modify it."); contents.AppendLine(); + var headerPos = contents.Length; - // Using declarations CSharpUsedNamespaces.Clear(); + CSharpUsedNamespaces.Add(null); + CSharpUsedNamespaces.Add(string.Empty); CSharpUsedNamespaces.Add("System"); CSharpUsedNamespaces.Add("System.ComponentModel"); CSharpUsedNamespaces.Add("System.Globalization"); CSharpUsedNamespaces.Add("System.Runtime.CompilerServices"); CSharpUsedNamespaces.Add("System.Runtime.InteropServices"); CSharpUsedNamespaces.Add("FlaxEngine"); - foreach (var e in moduleInfo.Children) - { - foreach (var apiTypeInfo in e.Children) - { - GenerateCSharpCollectNamespaces(buildData, apiTypeInfo, CSharpUsedNamespaces); - } - } - CSharpUsedNamespacesSorted.Clear(); - CSharpUsedNamespacesSorted.AddRange(CSharpUsedNamespaces); - CSharpUsedNamespacesSorted.Sort(); - foreach (var e in CSharpUsedNamespacesSorted) - contents.AppendLine($"using {e};"); - // TODO: custom using declarations support - // TODO: generate using declarations based on references modules (eg. using FlaxEngine, using Plugin1 in game API) // Process all API types from the file var useBindings = false; @@ -1121,7 +1087,21 @@ namespace Flax.Build.Bindings if (!useBindings) return; + { + var header = new StringBuilder(); + + // Using declarations + CSharpUsedNamespacesSorted.Clear(); + CSharpUsedNamespacesSorted.AddRange(CSharpUsedNamespaces); + CSharpUsedNamespacesSorted.Sort(); + for (var i = 2; i < CSharpUsedNamespacesSorted.Count; i++) + header.AppendLine($"using {CSharpUsedNamespacesSorted[i]};"); + + contents.Insert(headerPos, header.ToString()); + } + // Save generated file + contents.AppendLine(); contents.AppendLine("#endif"); Utilities.WriteFileIfChanged(bindings.GeneratedCSharpFilePath, contents.ToString()); } From 9a9b97998c82a3de5d3ac2ddb22ebff66eec5bfd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 28 May 2021 00:12:54 +0200 Subject: [PATCH 037/198] Fix loading native dependencies for binary modules from other folders than engine binaries or project root --- .../Platform/Windows/WindowsPlatform.cpp | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 00a5c0d0f..2086cd8dc 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -4,6 +4,7 @@ #include "Engine/Platform/Platform.h" #include "Engine/Platform/Window.h" +#include "Engine/Platform/FileSystem.h" #include "Engine/Platform/CreateWindowSettings.h" #include "Engine/Platform/WindowsManager.h" #include "Engine/Platform/MemoryStats.h" @@ -1118,6 +1119,19 @@ void* WindowsPlatform::LoadLibrary(const Char* filename) { ASSERT(filename); + // Add folder to search path to load dependency libraries + StringView folder = StringUtils::GetDirectoryName(filename); + if (folder.HasChars() && FileSystem::IsRelative(folder)) + folder = StringView::Empty; + if (folder.HasChars()) + { + Char& end = ((Char*)folder.Get())[folder.Length()]; + const Char c = end; + end = 0; + SetDllDirectoryW(*folder); + end = c; + } + // Avoiding windows dialog boxes if missing const DWORD errorMode = SEM_NOOPENFILEERRORBOX; DWORD prevErrorMode = 0; @@ -1134,12 +1148,15 @@ void* WindowsPlatform::LoadLibrary(const Char* filename) { SetThreadErrorMode(prevErrorMode, nullptr); } + if (folder.HasChars()) + { + SetDllDirectoryW(nullptr); + } #if CRASH_LOG_ENABLE // Refresh modules info during next stack trace collecting to have valid debug symbols information SymLocker.Lock(); - const auto folder = StringUtils::GetDirectoryName(filename); - if (!SymbolsPath.Contains(folder)) + if (folder.HasChars() && !SymbolsPath.Contains(folder)) { SymbolsPath.Add(folder); OnSymbolsPathModified(); From d5a129cb1bf6bb783381b7ecee13f22ade9473bd Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 28 May 2021 09:12:15 +0200 Subject: [PATCH 038/198] Add mono building for Switch --- Source/ThirdParty/mono-2.0/mono.Build.cs | 2 +- .../Flax.Build/Deps/Dependencies/mono.cs | 10 +++++- Source/Tools/Flax.Build/Deps/Dependency.cs | 31 +++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/Source/ThirdParty/mono-2.0/mono.Build.cs b/Source/ThirdParty/mono-2.0/mono.Build.cs index 9fa592f35..873f368f4 100644 --- a/Source/ThirdParty/mono-2.0/mono.Build.cs +++ b/Source/ThirdParty/mono-2.0/mono.Build.cs @@ -87,7 +87,7 @@ public class mono : DepsModule options.Libraries.Add(Path.Combine(depsRoot, "libmonosgen-2.0.so")); break; case TargetPlatform.Switch: - // TODO: mono for Switch + options.OutputFiles.Add(Path.Combine(depsRoot, "libmonosgen-2.0.a")); break; default: throw new InvalidPlatformException(options.Platform.Target); } diff --git a/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs b/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs index 7cd7649b1..595d7ac0e 100644 --- a/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs +++ b/Source/Tools/Flax.Build/Deps/Dependencies/mono.cs @@ -33,6 +33,7 @@ namespace Flax.Deps.Dependencies TargetPlatform.XboxOne, TargetPlatform.PS4, TargetPlatform.XboxScarlett, + TargetPlatform.Switch, }; case TargetPlatform.Linux: return new[] @@ -614,7 +615,7 @@ namespace Flax.Deps.Dependencies { "ANDROID_API", apiLevel }, { "ANDROID_API_VERSION", apiLevel }, { "ANDROID_NATIVE_API_LEVEL", apiLevel }, - + { "CC", Path.Combine(ndkBin, archName + apiLevel + "-clang") }, { "CXX", Path.Combine(ndkBin, archName + apiLevel + "-clang++") }, { "AR", Path.Combine(ndkBin, archName + "-ar") }, @@ -705,6 +706,13 @@ namespace Flax.Deps.Dependencies Utilities.DirectoryDelete(Path.Combine(bclLibMonoOutput, "2.1", "Facades")); break; } + case TargetPlatform.Switch: + { + var type = Type.GetType("Flax.Build.Platforms.Switch.mono"); + var method = type.GetMethod("Build"); + method.Invoke(null, new object[] { root, options }); + break; + } } } } diff --git a/Source/Tools/Flax.Build/Deps/Dependency.cs b/Source/Tools/Flax.Build/Deps/Dependency.cs index c19687c2a..17d158d42 100644 --- a/Source/Tools/Flax.Build/Deps/Dependency.cs +++ b/Source/Tools/Flax.Build/Deps/Dependency.cs @@ -263,5 +263,36 @@ namespace Flax.Deps Utilities.Run("cmake", cmdLine, null, path, Utilities.RunOptions.None, envVars); } + + /// + /// Runs the bash script via Cygwin tool (native bash on platforms other than Windows). + /// + /// The path. + /// The workspace folder. + /// Custom environment variables to pass to the child process. + public static void RunCygwin(string path, string workspace = null, Dictionary envVars = null) + { + string app; + switch (BuildPlatform) + { + case TargetPlatform.Windows: + { + var cygwinFolder = Environment.GetEnvironmentVariable("CYGWIN"); + if (string.IsNullOrEmpty(cygwinFolder) || !Directory.Exists(cygwinFolder)) + { + cygwinFolder = "C:\\cygwin"; + if (!Directory.Exists(cygwinFolder)) + throw new Exception("Missing Cygwin. Install Cygwin64 to C:\\cygwin or set CYGWIN env variable to install location folder."); + } + app = Path.Combine(cygwinFolder, "bin\\bash.exe"); + break; + } + case TargetPlatform.Linux: + app = "bash"; + break; + default: throw new InvalidPlatformException(BuildPlatform); + } + Utilities.Run(app, path, null, workspace, Utilities.RunOptions.None, envVars); + } } } From 969db604e5ff6b4ccdafaabf3d17bb3ab11e7698 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 28 May 2021 11:01:13 +0200 Subject: [PATCH 039/198] Fix gizmo in top down view on Z-axis movement (and other combination of perpendicular views) Fix gizmo stability when using single axis #530 --- Source/Editor/Gizmo/TransformGizmoBase.cs | 59 +++++++++++++++-------- 1 file changed, 39 insertions(+), 20 deletions(-) diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs index e123bd3f6..170bcb5b1 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.cs @@ -204,24 +204,43 @@ namespace FlaxEditor.Gizmo switch (_activeAxis) { - case Axis.XY: case Axis.X: { - var plane = new Plane(Vector3.Backward, Vector3.Transform(Position, invRotationMatrix).Z); + var plane = new Plane(Position, Vector3.Normalize(ray.Position - Position)); if (ray.Intersects(ref plane, out float intersection)) { _intersectPosition = ray.Position + ray.Direction * intersection; if (_lastIntersectionPosition != Vector3.Zero) _tDelta = _intersectPosition - _lastIntersectionPosition; - delta = _activeAxis == Axis.X - ? new Vector3(_tDelta.X, 0, 0) - : new Vector3(_tDelta.X, _tDelta.Y, 0); + delta = new Vector3(_tDelta.X, 0, 0); + } + break; + } + case Axis.Y: + { + var plane = new Plane(Position, Vector3.Normalize(ray.Position - Position)); + if (ray.Intersects(ref plane, out float intersection)) + { + _intersectPosition = ray.Position + ray.Direction * intersection; + if (_lastIntersectionPosition != Vector3.Zero) + _tDelta = _intersectPosition - _lastIntersectionPosition; + delta = new Vector3(0, _tDelta.Y, 0); } break; } case Axis.Z: + { + var plane = new Plane(Position, Vector3.Normalize(ray.Position - Position)); + if (ray.Intersects(ref plane, out float intersection)) + { + _intersectPosition = ray.Position + ray.Direction * intersection; + if (_lastIntersectionPosition != Vector3.Zero) + _tDelta = _intersectPosition - _lastIntersectionPosition; + delta = new Vector3(0, 0, _tDelta.Z); + } + break; + } case Axis.YZ: - case Axis.Y: { var plane = new Plane(Vector3.Left, Vector3.Transform(Position, invRotationMatrix).X); if (ray.Intersects(ref plane, out float intersection)) @@ -229,18 +248,19 @@ namespace FlaxEditor.Gizmo _intersectPosition = ray.Position + ray.Direction * intersection; if (_lastIntersectionPosition != Vector3.Zero) _tDelta = _intersectPosition - _lastIntersectionPosition; - switch (_activeAxis) - { - case Axis.Y: - delta = new Vector3(0, _tDelta.Y, 0); - break; - case Axis.Z: - delta = new Vector3(0, 0, _tDelta.Z); - break; - default: - delta = new Vector3(0, _tDelta.Y, _tDelta.Z); - break; - } + delta = new Vector3(0, _tDelta.Y, _tDelta.Z); + } + break; + } + case Axis.XY: + { + var plane = new Plane(Vector3.Backward, Vector3.Transform(Position, invRotationMatrix).Z); + if (ray.Intersects(ref plane, out float intersection)) + { + _intersectPosition = ray.Position + ray.Direction * intersection; + if (_lastIntersectionPosition != Vector3.Zero) + _tDelta = _intersectPosition - _lastIntersectionPosition; + delta = new Vector3(_tDelta.X, _tDelta.Y, 0); } break; } @@ -271,12 +291,11 @@ namespace FlaxEditor.Gizmo } } + // Modifiers if (isScaling) delta *= 0.01f; - if (Owner.IsAltKeyDown) delta *= 0.5f; - if ((isScaling ? ScaleSnapEnabled : TranslationSnapEnable) || Owner.UseSnapping) { float snapValue = isScaling ? ScaleSnapValue : TranslationSnapValue; From 29860b838eb46086b8eadbaf97c6b37377688b36 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 28 May 2021 11:30:28 +0200 Subject: [PATCH 040/198] Fix camera position when changing Editor view EditorViewport #531 --- Source/Editor/Viewport/EditorViewport.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 2dee43614..6e2288979 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -661,12 +661,12 @@ namespace FlaxEditor.Viewport { if (ViewportCamera is FPSCamera fpsCamera) { - var pos = Vector3.Zero + Vector3.Backward * orientation * 2000.0f; + var pos = ViewPosition + Vector3.Backward * orientation * 2000.0f; fpsCamera.MoveViewport(pos, orientation); } else { - ViewportCamera.SetArcBallView(orientation, Vector3.Zero, 2000.0f); + ViewportCamera.SetArcBallView(orientation, ViewPosition, 2000.0f); } } From 77f9010b065aec351f93b572ed6077f27f5d75b5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 28 May 2021 11:54:11 +0200 Subject: [PATCH 041/198] Fix exception in Editor UI is model has invalid material slot index assigned --- .../Editors/ModelInstanceEntryEditor.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs index 6ecac91ee..f150eec88 100644 --- a/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ModelInstanceEntryEditor.cs @@ -38,8 +38,12 @@ namespace FlaxEditor.CustomEditors.Editors var model = staticModel.Model; if (model && model.IsLoaded) { - _group.Panel.HeaderText = "Entry " + model.MaterialSlots[entryIndex].Name; - _updateName = false; + var slots = model.MaterialSlots; + if (slots != null && slots.Length > entryIndex) + { + _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name; + _updateName = false; + } } } else if (ParentEditor.ParentEditor.Values[0] is AnimatedModel animatedModel) @@ -47,8 +51,12 @@ namespace FlaxEditor.CustomEditors.Editors var model = animatedModel.SkinnedModel; if (model && model.IsLoaded) { - _group.Panel.HeaderText = "Entry " + model.MaterialSlots[entryIndex].Name; - _updateName = false; + var slots = model.MaterialSlots; + if (slots != null && slots.Length > entryIndex) + { + _group.Panel.HeaderText = "Entry " + slots[entryIndex].Name; + _updateName = false; + } } } } From 94ee82b58df03f098febb2fe040a435f6c7ecc8a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 28 May 2021 12:09:26 +0200 Subject: [PATCH 042/198] Fix Editor UI tree control click to handle selection update before #533 --- Source/Editor/GUI/Tree/TreeNode.cs | 18 ++++++++++++------ .../Windows/Assets/PrefabWindow.Actions.cs | 2 ++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Source/Editor/GUI/Tree/TreeNode.cs b/Source/Editor/GUI/Tree/TreeNode.cs index f7e5f115a..7fdc1c746 100644 --- a/Source/Editor/GUI/Tree/TreeNode.cs +++ b/Source/Editor/GUI/Tree/TreeNode.cs @@ -723,12 +723,6 @@ namespace FlaxEditor.GUI.Tree /// public override bool OnMouseUp(Vector2 location, MouseButton button) { - // Check if mouse hits bar - if (button == MouseButton.Right && TestHeaderHit(ref location)) - { - ParentTree.OnRightClickInternal(this, ref location); - } - // Clear flag for left button if (button == MouseButton.Left) { @@ -773,11 +767,23 @@ namespace FlaxEditor.GUI.Tree Expand(); } + // Check if mouse hits bar + if (button == MouseButton.Right && TestHeaderHit(ref location)) + { + ParentTree.OnRightClickInternal(this, ref location); + } + // Handled Focus(); return true; } + // Check if mouse hits bar + if (button == MouseButton.Right && TestHeaderHit(ref location)) + { + ParentTree.OnRightClickInternal(this, ref location); + } + // Base return base.OnMouseUp(location, button); } diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs b/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs index af568cd3f..6687bc548 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Actions.cs @@ -41,6 +41,8 @@ namespace FlaxEditor.Windows.Assets var newRoot = Object.Find(ref newRootId); _window.Graph.MainActor = null; + _window.Viewport.Instance = null; + if (SceneGraphFactory.Nodes.TryGetValue(oldRootId, out var oldRootNode)) oldRootNode.Dispose(); if (SceneGraphFactory.Nodes.TryGetValue(newRootId, out var newRootNode)) From e41dfe738b585199a3eef7a22f2018ae9b8140d3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 28 May 2021 14:24:19 +0200 Subject: [PATCH 043/198] Add support for moving object with transform gizmo when mouse moves outside the viewport during usage --- Source/Editor/Gizmo/GizmoBase.cs | 5 +++++ Source/Editor/Gizmo/TransformGizmoBase.cs | 20 +++++++++++++++++-- .../Editor/Tools/Foliage/PaintFoliageGizmo.cs | 3 +++ .../Editor/Tools/Terrain/PaintTerrainGizmo.cs | 3 +++ .../Tools/Terrain/SculptTerrainGizmo.cs | 3 +++ Source/Editor/Tools/VertexPainting.cs | 3 +++ Source/Editor/Viewport/EditorGizmoViewport.cs | 3 +++ Source/Editor/Viewport/EditorViewport.cs | 18 +++++++++++++++-- .../Editor/Viewport/PrefabWindowViewport.cs | 3 +++ 9 files changed, 57 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Gizmo/GizmoBase.cs b/Source/Editor/Gizmo/GizmoBase.cs index 6159c7673..1d8ccd233 100644 --- a/Source/Editor/Gizmo/GizmoBase.cs +++ b/Source/Editor/Gizmo/GizmoBase.cs @@ -22,6 +22,11 @@ namespace FlaxEditor.Gizmo /// public bool IsActive => Owner.Gizmos.Active == this; + /// + /// Gets a value indicating whether this gizmo is using mouse currently (eg. user moving objects). + /// + public virtual bool IsControllingMouse => false; + /// /// Initializes a new instance of the class. /// diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs index 170bcb5b1..9df9b858b 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System; using System.Collections.Generic; using FlaxEngine; @@ -65,6 +66,16 @@ namespace FlaxEditor.Gizmo /// public Transform LastDelta { get; private set; } + /// + /// Occurs when transforming selection started. + /// + public event Action TransformingStarted; + + /// + /// Occurs when transforming selection ended. + /// + public event Action TransformingEnded; + /// /// Initializes a new instance of the class. /// @@ -118,10 +129,10 @@ namespace FlaxEditor.Gizmo return; // End action - OnEndTransforming(); - _startTransforms.Clear(); _isTransforming = false; _isDuplicating = false; + OnEndTransforming(); + _startTransforms.Clear(); } private void UpdateGizmoPosition() @@ -366,6 +377,9 @@ namespace FlaxEditor.Gizmo } } + /// + public override bool IsControllingMouse => _isTransforming; + /// public override void Update(float dt) { @@ -531,6 +545,7 @@ namespace FlaxEditor.Gizmo /// protected virtual void OnStartTransforming() { + TransformingStarted?.Invoke(); } /// @@ -548,6 +563,7 @@ namespace FlaxEditor.Gizmo /// protected virtual void OnEndTransforming() { + TransformingEnded?.Invoke(); } /// diff --git a/Source/Editor/Tools/Foliage/PaintFoliageGizmo.cs b/Source/Editor/Tools/Foliage/PaintFoliageGizmo.cs index b24f503aa..9e6d78920 100644 --- a/Source/Editor/Tools/Foliage/PaintFoliageGizmo.cs +++ b/Source/Editor/Tools/Foliage/PaintFoliageGizmo.cs @@ -169,6 +169,9 @@ namespace FlaxEditor.Tools.Foliage PaintEnded?.Invoke(); } + /// + public override bool IsControllingMouse => IsPainting; + /// public override void Update(float dt) { diff --git a/Source/Editor/Tools/Terrain/PaintTerrainGizmo.cs b/Source/Editor/Tools/Terrain/PaintTerrainGizmo.cs index 51372f290..de95e4005 100644 --- a/Source/Editor/Tools/Terrain/PaintTerrainGizmo.cs +++ b/Source/Editor/Tools/Terrain/PaintTerrainGizmo.cs @@ -135,6 +135,9 @@ namespace FlaxEditor.Tools.Terrain PaintEnded?.Invoke(); } + /// + public override bool IsControllingMouse => IsPainting; + /// public override void Update(float dt) { diff --git a/Source/Editor/Tools/Terrain/SculptTerrainGizmo.cs b/Source/Editor/Tools/Terrain/SculptTerrainGizmo.cs index 88cd83d46..5900bc601 100644 --- a/Source/Editor/Tools/Terrain/SculptTerrainGizmo.cs +++ b/Source/Editor/Tools/Terrain/SculptTerrainGizmo.cs @@ -135,6 +135,9 @@ namespace FlaxEditor.Tools.Terrain PaintEnded?.Invoke(); } + /// + public override bool IsControllingMouse => IsPainting; + /// public override void Update(float dt) { diff --git a/Source/Editor/Tools/VertexPainting.cs b/Source/Editor/Tools/VertexPainting.cs index 29d9daedc..ad2a93251 100644 --- a/Source/Editor/Tools/VertexPainting.cs +++ b/Source/Editor/Tools/VertexPainting.cs @@ -540,6 +540,9 @@ namespace FlaxEditor.Tools _paintUpdateCount = 0; } + /// + public override bool IsControllingMouse => IsPainting; + /// public override void Update(float dt) { diff --git a/Source/Editor/Viewport/EditorGizmoViewport.cs b/Source/Editor/Viewport/EditorGizmoViewport.cs index 93b2c7fbe..5c9fcf034 100644 --- a/Source/Editor/Viewport/EditorGizmoViewport.cs +++ b/Source/Editor/Viewport/EditorGizmoViewport.cs @@ -73,6 +73,9 @@ namespace FlaxEditor.Viewport /// public Undo Undo { get; } + /// + protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false; + /// protected override void AddUpdateCallbacks(RootControl root) { diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 6e2288979..9c4dcc253 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -136,7 +136,7 @@ namespace FlaxEditor.Viewport // Input - private bool _isControllingMouse; + private bool _isControllingMouse, _isViewportControllingMouse; private int _deltaFilteringStep; private Vector2 _startPos; private Vector2 _mouseDeltaLast; @@ -653,6 +653,11 @@ namespace FlaxEditor.Viewport task.Begin += OnRenderBegin; } + /// + /// Gets a value indicating whether this viewport is using mouse currently (eg. user moving objects). + /// + protected virtual bool IsControllingMouse => false; + /// /// Orients the viewport. /// @@ -972,7 +977,16 @@ namespace FlaxEditor.Viewport // Update input { // Get input buttons and keys (skip if viewport has no focus or mouse is over a child control) - bool useMouse = Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height); + var isViewportControllingMouse = IsControllingMouse; + if (isViewportControllingMouse != _isViewportControllingMouse) + { + _isViewportControllingMouse = isViewportControllingMouse; + if (isViewportControllingMouse) + StartMouseCapture(); + else + EndMouseCapture(); + } + bool useMouse = IsControllingMouse || (Mathf.IsInRange(_viewMousePos.X, 0, Width) && Mathf.IsInRange(_viewMousePos.Y, 0, Height)); _prevInput = _input; var hit = GetChildAt(_viewMousePos, c => c.Visible && !(c is CanvasRootControl)); if (ContainsFocus && hit == null) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index e189a8e62..3cdd6ae0e 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -356,6 +356,9 @@ namespace FlaxEditor.Viewport /// public Undo Undo { get; } + /// + protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false; + /// protected override void AddUpdateCallbacks(RootControl root) { From 845198ebe36b764d8a8459c634e4a7f1de185102 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 28 May 2021 15:51:28 +0200 Subject: [PATCH 044/198] Refactor focusing on selection logic in Editor --- Source/Editor/Gizmo/GizmoBase.cs | 5 ++++ Source/Editor/Modules/ContentFindingModule.cs | 4 +-- Source/Editor/Viewport/Cameras/FPSCamera.cs | 13 +++++++-- .../Viewport/MainEditorGizmoViewport.cs | 23 +++++++++++---- .../Editor/Viewport/PrefabWindowViewport.cs | 28 +++++++++++++++---- .../Windows/Assets/PrefabWindow.Selection.cs | 2 ++ Source/Editor/Windows/Assets/PrefabWindow.cs | 3 +- Source/Editor/Windows/EditGameWindow.cs | 16 ----------- Source/Editor/Windows/SceneTreeWindow.cs | 2 +- 9 files changed, 62 insertions(+), 34 deletions(-) diff --git a/Source/Editor/Gizmo/GizmoBase.cs b/Source/Editor/Gizmo/GizmoBase.cs index 1d8ccd233..a44c677dd 100644 --- a/Source/Editor/Gizmo/GizmoBase.cs +++ b/Source/Editor/Gizmo/GizmoBase.cs @@ -27,6 +27,11 @@ namespace FlaxEditor.Gizmo /// public virtual bool IsControllingMouse => false; + /// + /// Gets the custom world-space bounds for current gizmo mode focus for used (eg. selected object part bounds). Returns if not used. + /// + public virtual BoundingSphere FocusBounds => BoundingSphere.Empty; + /// /// Initializes a new instance of the class. /// diff --git a/Source/Editor/Modules/ContentFindingModule.cs b/Source/Editor/Modules/ContentFindingModule.cs index 45748f7f4..b8f7f74a0 100644 --- a/Source/Editor/Modules/ContentFindingModule.cs +++ b/Source/Editor/Modules/ContentFindingModule.cs @@ -287,11 +287,11 @@ namespace FlaxEditor.Modules break; case ActorNode actorNode: Editor.Instance.SceneEditing.Select(actorNode.Actor); - Editor.Instance.Windows.EditWin.ShowSelectedActors(); + Editor.Instance.Windows.EditWin.Viewport.FocusSelection(); break; case Actor actor: Editor.Instance.SceneEditing.Select(actor); - Editor.Instance.Windows.EditWin.ShowSelectedActors(); + Editor.Instance.Windows.EditWin.Viewport.FocusSelection(); break; } } diff --git a/Source/Editor/Viewport/Cameras/FPSCamera.cs b/Source/Editor/Viewport/Cameras/FPSCamera.cs index 22abccf69..b6cd14273 100644 --- a/Source/Editor/Viewport/Cameras/FPSCamera.cs +++ b/Source/Editor/Viewport/Cameras/FPSCamera.cs @@ -143,13 +143,22 @@ namespace FlaxEditor.Viewport.Cameras ShowSphere(ref mergesSphere, ref orientation); } - private void ShowSphere(ref BoundingSphere sphere) + /// + /// Moves the camera to visualize given world area defined by the sphere. + /// + /// The sphere. + public void ShowSphere(ref BoundingSphere sphere) { var q = new Quaternion(0.424461186f, -0.0940724313f, 0.0443938486f, 0.899451137f); ShowSphere(ref sphere, ref q); } - private void ShowSphere(ref BoundingSphere sphere, ref Quaternion orientation) + /// + /// Moves the camera to visualize given world area defined by the sphere. + /// + /// The sphere. + /// The camera orientation. + public void ShowSphere(ref BoundingSphere sphere, ref Quaternion orientation) { Vector3 position; if (Viewport.UseOrthographicProjection) diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index ea63a598b..8bc6b4c02 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -361,7 +361,7 @@ namespace FlaxEditor.Viewport InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate); InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate); InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale); - InputActions.Add(options => options.FocusSelection, () => _editor.Windows.EditWin.ShowSelectedActors()); + InputActions.Add(options => options.FocusSelection, FocusSelection); InputActions.Add(options => options.Delete, _editor.SceneEditing.Delete); } @@ -652,6 +652,22 @@ namespace FlaxEditor.Viewport Gizmos.ForEach(x => x.OnSelectionChanged(selection)); } + /// + /// Focuses the viewport on the current selection of the gizmo. + /// + public void FocusSelection() + { + if (TransformGizmo.SelectedParents.Count == 0) + return; + + var orientation = ViewOrientation; + var gizmoBounds = Gizmos.Active.FocusBounds; + if (gizmoBounds != BoundingSphere.Empty) + ((FPSCamera)ViewportCamera).ShowSphere(ref gizmoBounds, ref orientation); + else + ((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orientation); + } + /// /// Applies the transform to the collection of scene graph nodes. /// @@ -709,10 +725,7 @@ namespace FlaxEditor.Viewport /// protected override void OrientViewport(ref Quaternion orientation) { - if (TransformGizmo.SelectedParents.Count != 0) - { - ((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orientation); - } + FocusSelection(); base.OrientViewport(ref orientation); } diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 3cdd6ae0e..1c337cc25 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Security.Policy; using FlaxEditor.Content; using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; @@ -317,7 +316,8 @@ namespace FlaxEditor.Viewport /// public void ShowSelectedActors() { - ((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents); + var orient = Viewport.ViewOrientation; + ((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orient); } /// @@ -356,6 +356,9 @@ namespace FlaxEditor.Viewport /// public Undo Undo { get; } + /// + public EditorViewport Viewport => this; + /// protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false; @@ -780,13 +783,26 @@ namespace FlaxEditor.Viewport Spawn(actor, ref hitLocation); } + /// + /// Focuses the viewport on the current selection of the gizmo. + /// + public void FocusSelection() + { + if (TransformGizmo.SelectedParents.Count == 0) + return; + + var orientation = ViewOrientation; + var gizmoBounds = Gizmos.Active.FocusBounds; + if (gizmoBounds != BoundingSphere.Empty) + ((FPSCamera)ViewportCamera).ShowSphere(ref gizmoBounds, ref orientation); + else + ((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orientation); + } + /// protected override void OrientViewport(ref Quaternion orientation) { - if (TransformGizmo.SelectedParents.Count != 0) - { - ((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orientation); - } + FocusSelection(); base.OrientViewport(ref orientation); } diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs b/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs index be4bf3070..5b501d8cc 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Selection.cs @@ -3,9 +3,11 @@ using System; using System.Collections.Generic; using System.Linq; +using FlaxEditor.Gizmo; using FlaxEditor.GUI.Tree; using FlaxEditor.SceneGraph; using FlaxEditor.SceneGraph.GUI; +using FlaxEditor.Viewport.Cameras; using FlaxEngine; namespace FlaxEditor.Windows.Assets diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index bec9bf7fe..85a9a2cb9 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -10,7 +10,6 @@ using FlaxEditor.SceneGraph; using FlaxEditor.Viewport; using FlaxEngine; using FlaxEngine.GUI; -using Object = FlaxEngine.Object; namespace FlaxEditor.Windows.Assets { @@ -185,6 +184,7 @@ namespace FlaxEditor.Windows.Assets InputActions.Add(options => options.Paste, Paste); InputActions.Add(options => options.Duplicate, Duplicate); InputActions.Add(options => options.Delete, Delete); + InputActions.Add(options => options.FocusSelection, _viewport.FocusSelection); } private void OnSearchBoxTextChanged() @@ -428,7 +428,6 @@ namespace FlaxEditor.Windows.Assets /// public override void OnLayoutDeserialize(XmlElement node) { - if (float.TryParse(node.GetAttribute("Split1"), out float value1)) _split1.SplitterValue = value1; diff --git a/Source/Editor/Windows/EditGameWindow.cs b/Source/Editor/Windows/EditGameWindow.cs index 4025f68ab..eb9dd8034 100644 --- a/Source/Editor/Windows/EditGameWindow.cs +++ b/Source/Editor/Windows/EditGameWindow.cs @@ -6,9 +6,7 @@ using System.Linq; using System.Xml; using FlaxEditor.SceneGraph; using FlaxEditor.SceneGraph.Actors; -using FlaxEditor.States; using FlaxEditor.Viewport; -using FlaxEditor.Viewport.Cameras; using FlaxEditor.Viewport.Widgets; using FlaxEngine; using FlaxEngine.GUI; @@ -238,20 +236,6 @@ namespace FlaxEditor.Windows } } - /// - /// Moves the viewport to visualize selected actors. - /// - public void ShowSelectedActors() - { - if (Viewport.UseOrthographicProjection) - { - var orient = Viewport.ViewOrientation; - ((FPSCamera)Viewport.ViewportCamera).ShowActors(Viewport.TransformGizmo.SelectedParents, ref orient); - } - else - ((FPSCamera)Viewport.ViewportCamera).ShowActors(Viewport.TransformGizmo.SelectedParents); - } - /// /// Updates the camera previews. /// diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index e33d658e6..afe6d977e 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -68,7 +68,7 @@ namespace FlaxEditor.Windows InputActions.Add(options => options.TranslateMode, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate); InputActions.Add(options => options.RotateMode, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate); InputActions.Add(options => options.ScaleMode, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale); - InputActions.Add(options => options.FocusSelection, () => Editor.Windows.EditWin.ShowSelectedActors()); + InputActions.Add(options => options.FocusSelection, () => Editor.Windows.EditWin.Viewport.FocusSelection()); } private void OnSearchBoxTextChanged() From b1572ecb918f2a7ae86c4ede0171c93e8753c0e9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 28 May 2021 15:54:54 +0200 Subject: [PATCH 045/198] Add support for focusing on foliage instance bounds when editing it --- .../Editor/Tools/Foliage/EditFoliageGizmo.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Source/Editor/Tools/Foliage/EditFoliageGizmo.cs b/Source/Editor/Tools/Foliage/EditFoliageGizmo.cs index 3e3730769..7443b52ad 100644 --- a/Source/Editor/Tools/Foliage/EditFoliageGizmo.cs +++ b/Source/Editor/Tools/Foliage/EditFoliageGizmo.cs @@ -33,6 +33,25 @@ namespace FlaxEditor.Tools.Foliage GizmoMode = mode; } + /// + public override BoundingSphere FocusBounds + { + get + { + var foliage = GizmoMode.SelectedFoliage; + if (foliage) + { + var instanceIndex = GizmoMode.SelectedInstanceIndex; + if (instanceIndex >= 0 && instanceIndex < foliage.InstancesCount) + { + var instance = foliage.GetInstance(instanceIndex); + return instance.Bounds; + } + } + return base.FocusBounds; + } + } + /// protected override int SelectionCount { From 518e2484944bf79bf53adfb1f77815e60ca0f79a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 28 May 2021 15:57:40 +0200 Subject: [PATCH 046/198] Add support for focusing on model when painting its vertices --- Source/Editor/Tools/VertexPainting.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Editor/Tools/VertexPainting.cs b/Source/Editor/Tools/VertexPainting.cs index ad2a93251..715f075ca 100644 --- a/Source/Editor/Tools/VertexPainting.cs +++ b/Source/Editor/Tools/VertexPainting.cs @@ -543,6 +543,9 @@ namespace FlaxEditor.Tools /// public override bool IsControllingMouse => IsPainting; + /// + public override BoundingSphere FocusBounds => _selectedModel?.Sphere ?? base.FocusBounds; + /// public override void Update(float dt) { From beb79f61070da235c505dc5c7495285fe6cc08e9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 28 May 2021 19:43:15 +0200 Subject: [PATCH 047/198] Use network for UWP from Win32 impl --- Source/Engine/Platform/Types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Platform/Types.h b/Source/Engine/Platform/Types.h index f90be1129..e7ec0308c 100644 --- a/Source/Engine/Platform/Types.h +++ b/Source/Engine/Platform/Types.h @@ -45,8 +45,8 @@ class Win32Thread; typedef Win32Thread Thread; class UWPWindow; typedef UWPWindow Window; -class NetworkBase; -typedef NetworkBase Network; +class Win32Network; +typedef Win32Network Network; #elif PLATFORM_LINUX From bf32034d9148750826b5684fe18314b2fe679bc0 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 29 May 2021 10:14:17 +0200 Subject: [PATCH 048/198] Fix Margin serialization --- Source/Engine/Serialization/JsonConverters.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Serialization/JsonConverters.cs b/Source/Engine/Serialization/JsonConverters.cs index 149bdc241..902593ac2 100644 --- a/Source/Engine/Serialization/JsonConverters.cs +++ b/Source/Engine/Serialization/JsonConverters.cs @@ -127,7 +127,7 @@ namespace FlaxEngine.Json if ((serializer.TypeNameHandling & TypeNameHandling.Objects) == TypeNameHandling.Objects) { writer.WritePropertyName("$type"); - writer.WriteValue("FlaxEngine.Margin, FlaxEngine.CSharp"); + writer.WriteValue("FlaxEngine.GUI.Margin, FlaxEngine.CSharp"); } #endif writer.WritePropertyName("Left"); From 44433bf3b4ffc0fee16db8e79f0b0b442d135cb8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 29 May 2021 13:48:00 +0200 Subject: [PATCH 049/198] Add 9-slicking support for UI to draw sprites and textures #155 --- .../CustomEditors/Editors/IBrushEditor.cs | 1 + Source/Engine/Core/Math/Vector4.cs | 13 ++ Source/Engine/Render2D/Render2D.cpp | 116 ++++++++++++++++-- Source/Engine/Render2D/Render2D.h | 45 ++++++- Source/Engine/UI/GUI/Brushes/SpriteBrush.cs | 72 +++++++++++ Source/Engine/UI/GUI/Brushes/TextureBrush.cs | 85 ++++++++++++- 6 files changed, 318 insertions(+), 14 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/IBrushEditor.cs b/Source/Editor/CustomEditors/Editors/IBrushEditor.cs index f0c194eef..8174a40a6 100644 --- a/Source/Editor/CustomEditors/Editors/IBrushEditor.cs +++ b/Source/Editor/CustomEditors/Editors/IBrushEditor.cs @@ -22,6 +22,7 @@ namespace FlaxEditor.CustomEditors.Editors new OptionType("Material", typeof(MaterialBrush)), new OptionType("Solid Color", typeof(SolidColorBrush)), new OptionType("Linear Gradient", typeof(LinearGradientBrush)), + new OptionType("Texture 9-Slicing", typeof(Texture9SlicingBrush)), }; } } diff --git a/Source/Engine/Core/Math/Vector4.cs b/Source/Engine/Core/Math/Vector4.cs index 2a62df067..6d1521e85 100644 --- a/Source/Engine/Core/Math/Vector4.cs +++ b/Source/Engine/Core/Math/Vector4.cs @@ -153,6 +153,19 @@ namespace FlaxEngine W = w; } + /// + /// Initializes a new instance of the struct. + /// + /// A vector containing the values with which to initialize the X and Y components. + /// A vector containing the values with which to initialize the Z and W components. + public Vector4(Vector2 xy, Vector2 zw) + { + X = xy.X; + Y = xy.Y; + Z = zw.X; + W = zw.Y; + } + /// /// Initializes a new instance of the struct. /// diff --git a/Source/Engine/Render2D/Render2D.cpp b/Source/Engine/Render2D/Render2D.cpp index a7602dce1..281b05ebc 100644 --- a/Source/Engine/Render2D/Render2D.cpp +++ b/Source/Engine/Render2D/Render2D.cpp @@ -25,19 +25,14 @@ #include "Engine/Engine/EngineService.h" #if USE_EDITOR - -// TODO: maybe we could also provide some stack trace for that? #define RENDER2D_CHECK_RENDERING_STATE \ if (!Render2D::IsRendering()) \ { \ LOG(Error, "Calling Render2D is only valid during rendering."); \ return; \ } - #else - #define RENDER2D_CHECK_RENDERING_STATE - #endif #if USE_EDITOR @@ -353,6 +348,57 @@ FORCE_INLINE void WriteRect(const Rectangle& rect, const Color& color) WriteRect(rect, color, Vector2::Zero, Vector2::One); } +void Write9SlicingRect(const Rectangle& rect, const Color& color, const Vector4& border, const Vector4& borderUVs) +{ + const Rectangle upperLeft(rect.Location.X, rect.Location.Y, border.X, border.Z); + const Rectangle upperRight(rect.Location.X + rect.Size.X - border.Y, rect.Location.Y, border.Y, border.Z); + const Rectangle bottomLeft(rect.Location.X, rect.Location.Y + rect.Size.Y - border.W, border.X, border.W); + const Rectangle bottomRight(rect.Location.X + rect.Size.X - border.Y, rect.Location.Y + rect.Size.Y - border.W, border.Y, border.W); + + const Vector2 upperLeftUV(borderUVs.X, borderUVs.Z); + const Vector2 upperRightUV(1.0f - borderUVs.Y, borderUVs.Z); + const Vector2 bottomLeftUV(borderUVs.X, 1.0f - borderUVs.W); + const Vector2 bottomRightUV(1.0f - borderUVs.Y, 1.0f - borderUVs.W); + + WriteRect(upperLeft, color, Vector2::Zero, upperLeftUV); + WriteRect(upperRight, color, Vector2(upperRightUV.X, 0), Vector2(1, upperLeftUV.Y)); + WriteRect(bottomLeft, color, Vector2(0, bottomLeftUV.Y), Vector2(bottomLeftUV.X, 1)); + WriteRect(bottomRight, color, bottomRightUV, Vector2::One); + + WriteRect(Rectangle(upperLeft.GetUpperRight(), upperRight.GetBottomLeft() - upperLeft.GetUpperRight()), color, Vector2(upperLeftUV.X, 0), upperRightUV); + WriteRect(Rectangle(upperLeft.GetBottomLeft(), bottomLeft.GetUpperRight() - upperLeft.GetBottomLeft()), color, Vector2(0, upperLeftUV.Y), bottomLeftUV); + WriteRect(Rectangle(bottomLeft.GetUpperRight(), bottomRight.GetBottomLeft() - bottomLeft.GetUpperRight()), color, bottomLeftUV, Vector2(bottomRightUV.X, 1)); + WriteRect(Rectangle(upperRight.GetBottomLeft(), bottomRight.GetUpperRight() - upperRight.GetBottomLeft()), color, upperRightUV, Vector2(1, bottomRightUV.Y)); + + WriteRect(Rectangle(upperLeft.GetBottomRight(), bottomRight.GetUpperLeft() - upperLeft.GetBottomRight()), color, upperRightUV, bottomRightUV); +} + +void Write9SlicingRect(const Rectangle& rect, const Color& color, const Vector4& border, const Vector4& borderUVs, const Vector2& uvLocation, const Vector2& uvSize) +{ + const Rectangle upperLeft(rect.Location.X, rect.Location.Y, border.X, border.Z); + const Rectangle upperRight(rect.Location.X + rect.Size.X - border.Y, rect.Location.Y, border.Y, border.Z); + const Rectangle bottomLeft(rect.Location.X, rect.Location.Y + rect.Size.Y - border.W, border.X, border.W); + const Rectangle bottomRight(rect.Location.X + rect.Size.X - border.Y, rect.Location.Y + rect.Size.Y - border.W, border.Y, border.W); + + const Vector2 upperLeftUV = Vector2(borderUVs.X, borderUVs.Z) * uvSize + uvLocation; + const Vector2 upperRightUV = Vector2(1.0f - borderUVs.Y, borderUVs.Z) * uvSize + uvLocation; + const Vector2 bottomLeftUV = Vector2(borderUVs.X, 1.0f - borderUVs.W) * uvSize + uvLocation; + const Vector2 bottomRightUV = Vector2(1.0f - borderUVs.Y, 1.0f - borderUVs.W) * uvSize + uvLocation; + const Vector2 uvEnd = uvLocation + uvSize; + + WriteRect(upperLeft, color, uvLocation, upperLeftUV); + WriteRect(upperRight, color, Vector2(upperRightUV.X, uvLocation.Y), Vector2(uvEnd.X, upperLeftUV.Y)); + WriteRect(bottomLeft, color, Vector2(uvLocation.X, bottomLeftUV.Y), Vector2(bottomLeftUV.X, uvEnd.Y)); + WriteRect(bottomRight, color, bottomRightUV, uvEnd); + + WriteRect(Rectangle(upperLeft.GetUpperRight(), upperRight.GetBottomLeft() - upperLeft.GetUpperRight()), color, Vector2(upperLeftUV.X, uvLocation.Y), upperRightUV); + WriteRect(Rectangle(upperLeft.GetBottomLeft(), bottomLeft.GetUpperRight() - upperLeft.GetBottomLeft()), color, Vector2(uvLocation.X, upperLeftUV.Y), bottomLeftUV); + WriteRect(Rectangle(bottomLeft.GetUpperRight(), bottomRight.GetBottomLeft() - bottomLeft.GetUpperRight()), color, bottomLeftUV, Vector2(bottomRightUV.X, uvEnd.Y)); + WriteRect(Rectangle(upperRight.GetBottomLeft(), bottomRight.GetUpperRight() - upperRight.GetBottomLeft()), color, upperRightUV, Vector2(uvEnd.X, bottomRightUV.Y)); + + WriteRect(Rectangle(upperLeft.GetBottomRight(), bottomRight.GetUpperLeft() - upperLeft.GetBottomRight()), color, upperRightUV, bottomRightUV); +} + typedef bool (*CanDrawCallCallback)(const Render2DDrawCall&, const Render2DDrawCall&); bool CanDrawCallCallbackTrue(const Render2DDrawCall& d1, const Render2DDrawCall& d2) @@ -1548,7 +1594,7 @@ void Render2D::DrawSprite(const SpriteHandle& spriteHandle, const Rectangle& rec drawCall.Type = DrawCallType::FillTexture; drawCall.StartIB = IBIndex; drawCall.CountIB = 6; - drawCall.AsTexture.Ptr = spriteHandle.GetAtlasTexture(); + drawCall.AsTexture.Ptr = spriteHandle.Atlas->GetTexture(); WriteRect(rect, color, sprite->Area.GetUpperLeft(), sprite->Area.GetBottomRight()); } @@ -1575,10 +1621,66 @@ void Render2D::DrawSpritePoint(const SpriteHandle& spriteHandle, const Rectangle drawCall.Type = DrawCallType::FillTexturePoint; drawCall.StartIB = IBIndex; drawCall.CountIB = 6; - drawCall.AsTexture.Ptr = spriteHandle.GetAtlasTexture(); + drawCall.AsTexture.Ptr = spriteHandle.Atlas->GetTexture(); WriteRect(rect, color, sprite->Area.GetUpperLeft(), sprite->Area.GetBottomRight()); } +void Render2D::Draw9SlicingTexture(TextureBase* t, const Rectangle& rect, const Vector4& border, const Vector4& borderUVs, const Color& color) +{ + RENDER2D_CHECK_RENDERING_STATE; + + Render2DDrawCall drawCall; + drawCall.Type = DrawCallType::FillTexture; + drawCall.StartIB = IBIndex; + drawCall.CountIB = 6 * 9; + drawCall.AsTexture.Ptr = t ? t->GetTexture() : nullptr; + DrawCalls.Add(drawCall); + Write9SlicingRect(rect, color, border, borderUVs); +} + +void Render2D::Draw9SlicingTexturePoint(TextureBase* t, const Rectangle& rect, const Vector4& border, const Vector4& borderUVs, const Color& color) +{ + RENDER2D_CHECK_RENDERING_STATE; + + Render2DDrawCall drawCall; + drawCall.Type = DrawCallType::FillTexturePoint; + drawCall.StartIB = IBIndex; + drawCall.CountIB = 6 * 9; + drawCall.AsTexture.Ptr = t ? t->GetTexture() : nullptr; + DrawCalls.Add(drawCall); + Write9SlicingRect(rect, color, border, borderUVs); +} + +void Render2D::Draw9SlicingSprite(const SpriteHandle& spriteHandle, const Rectangle& rect, const Vector4& border, const Vector4& borderUVs, const Color& color) +{ + RENDER2D_CHECK_RENDERING_STATE; + if (spriteHandle.Index == INVALID_INDEX || !spriteHandle.Atlas || !spriteHandle.Atlas->GetTexture()->HasResidentMip()) + return; + + Sprite* sprite = &spriteHandle.Atlas->Sprites.At(spriteHandle.Index); + Render2DDrawCall& drawCall = DrawCalls.AddOne(); + drawCall.Type = DrawCallType::FillTexture; + drawCall.StartIB = IBIndex; + drawCall.CountIB = 6 * 9; + drawCall.AsTexture.Ptr = spriteHandle.Atlas->GetTexture(); + Write9SlicingRect(rect, color, border, borderUVs, sprite->Area.Location, sprite->Area.Size); +} + +void Render2D::Draw9SlicingSpritePoint(const SpriteHandle& spriteHandle, const Rectangle& rect, const Vector4& border, const Vector4& borderUVs, const Color& color) +{ + RENDER2D_CHECK_RENDERING_STATE; + if (spriteHandle.Index == INVALID_INDEX || !spriteHandle.Atlas || !spriteHandle.Atlas->GetTexture()->HasResidentMip()) + return; + + Sprite* sprite = &spriteHandle.Atlas->Sprites.At(spriteHandle.Index); + Render2DDrawCall& drawCall = DrawCalls.AddOne(); + drawCall.Type = DrawCallType::FillTexturePoint; + drawCall.StartIB = IBIndex; + drawCall.CountIB = 6 * 9; + drawCall.AsTexture.Ptr = spriteHandle.Atlas->GetTexture(); + Write9SlicingRect(rect, color, border, borderUVs, sprite->Area.Location, sprite->Area.Size); +} + void Render2D::DrawCustom(GPUTexture* t, const Rectangle& rect, GPUPipelineState* ps, const Color& color) { RENDER2D_CHECK_RENDERING_STATE; diff --git a/Source/Engine/Render2D/Render2D.h b/Source/Engine/Render2D/Render2D.h index 36a5bcfa2..f1c9c816b 100644 --- a/Source/Engine/Render2D/Render2D.h +++ b/Source/Engine/Render2D/Render2D.h @@ -27,7 +27,6 @@ class TextureBase; API_CLASS(Static) class FLAXENGINE_API Render2D { DECLARE_SCRIPTING_TYPE_NO_SPAWN(Render2D); -public: /// /// The rendering features and options flags. @@ -50,13 +49,11 @@ public: /// /// Checks if interface is during rendering phrase (Draw calls may be performed without failing). /// - /// True if is during rendering, otherwise false/ static bool IsRendering(); /// /// Gets the current rendering viewport. /// - /// The viewport static const Viewport& GetViewport(); /// @@ -64,8 +61,6 @@ public: /// API_FIELD() static RenderingFeatures Features; -public: - /// /// Called when frame rendering begins by the graphics device. /// @@ -302,6 +297,46 @@ public: /// The color to multiply all texture pixels. API_FUNCTION() static void DrawSpritePoint(const SpriteHandle& spriteHandle, const Rectangle& rect, const Color& color = Color::White); + /// + /// Draws the texture using 9-slicing. + /// + /// The texture to draw. + /// The rectangle to draw. + /// The borders for 9-slicing (inside rectangle, ordered: left, right, top, bottom). + /// The borders UVs for 9-slicing (inside rectangle UVs, ordered: left, right, top, bottom). + /// The color to multiply all texture pixels. + API_FUNCTION() static void Draw9SlicingTexture(TextureBase* t, const Rectangle& rect, const Vector4& border, const Vector4& borderUVs, const Color& color = Color::White); + + /// + /// Draws the texture using 9-slicing (uses point sampler). + /// + /// The texture to draw. + /// The rectangle to draw. + /// The borders for 9-slicing (inside rectangle, ordered: left, right, top, bottom). + /// The borders UVs for 9-slicing (inside rectangle UVs, ordered: left, right, top, bottom). + /// The color to multiply all texture pixels. + API_FUNCTION() static void Draw9SlicingTexturePoint(TextureBase* t, const Rectangle& rect, const Vector4& border, const Vector4& borderUVs, const Color& color = Color::White); + + /// + /// Draws a sprite using 9-slicing. + /// + /// The sprite to draw. + /// The rectangle to draw. + /// The borders for 9-slicing (inside rectangle, ordered: left, right, top, bottom). + /// The borders UVs for 9-slicing (inside rectangle UVs, ordered: left, right, top, bottom). + /// The color to multiply all texture pixels. + API_FUNCTION() static void Draw9SlicingSprite(const SpriteHandle& spriteHandle, const Rectangle& rect, const Vector4& border, const Vector4& borderUVs, const Color& color = Color::White); + + /// + /// Draws a sprite using 9-slicing (uses point sampler). + /// + /// The sprite to draw. + /// The rectangle to draw. + /// The borders for 9-slicing (inside rectangle, ordered: left, right, top, bottom). + /// The borders UVs for 9-slicing (inside rectangle UVs, ordered: left, right, top, bottom). + /// The color to multiply all texture pixels. + API_FUNCTION() static void Draw9SlicingSpritePoint(const SpriteHandle& spriteHandle, const Rectangle& rect, const Vector4& border, const Vector4& borderUVs, const Color& color = Color::White); + /// /// Performs custom rendering. /// diff --git a/Source/Engine/UI/GUI/Brushes/SpriteBrush.cs b/Source/Engine/UI/GUI/Brushes/SpriteBrush.cs index 05c0bb2e2..eb426eac8 100644 --- a/Source/Engine/UI/GUI/Brushes/SpriteBrush.cs +++ b/Source/Engine/UI/GUI/Brushes/SpriteBrush.cs @@ -48,4 +48,76 @@ namespace FlaxEngine.GUI Render2D.DrawSprite(Sprite, rect, color); } } + + /// + /// Implementation of for using 9-slicing. + /// + /// + public sealed class Sprite9SlicingBrush : IBrush + { + /// + /// The sprite. + /// + [ExpandGroups, EditorOrder(0), Tooltip("The sprite.")] + public SpriteHandle Sprite; + + /// + /// The texture sampling filter mode. + /// + [ExpandGroups, EditorOrder(1), Tooltip("The texture sampling filter mode.")] + public BrushFilter Filter = BrushFilter.Linear; + + /// + /// The border size. + /// + [ExpandGroups, EditorOrder(2), Limit(0), Tooltip("The border size.")] + public float BorderSize = 10.0f; + + /// + /// The sprite borders (in texture space, range 0-1). + /// + [ExpandGroups, EditorOrder(3), Limit(0, 1), Tooltip("The sprite borders (in texture space, range 0-1).")] + public Margin Border = new Margin(0.1f); + +#if FLAX_EDITOR + /// + /// Displays borders (editor only). + /// + [NoSerialize, EditorOrder(4), Tooltip("Displays borders (editor only).")] + public bool ShowBorders; +#endif + + /// + /// Initializes a new instance of the class. + /// + public Sprite9SlicingBrush() + { + } + + /// + public Vector2 Size => Sprite.IsValid ? Sprite.Size : Vector2.Zero; + + /// + public unsafe void Draw(Rectangle rect, Color color) + { + var border = Border; + var borderUV = *(Vector4*)&border; + var borderSize = borderUV * new Vector4(BorderSize, BorderSize, BorderSize, BorderSize); + if (Filter == BrushFilter.Point) + Render2D.Draw9SlicingSpritePoint(Sprite, rect, borderSize, borderUV, color); + else + Render2D.Draw9SlicingSprite(Sprite, rect, borderSize, borderUV, color); +#if FLAX_EDITOR + if (ShowBorders) + { + var bordersRect = rect; + bordersRect.Location.X += borderSize.X; + bordersRect.Location.Y += borderSize.Z; + bordersRect.Size.X -= borderSize.X + borderSize.Y; + bordersRect.Size.Y -= borderSize.Z + borderSize.W; + Render2D.DrawRectangle(bordersRect, Color.YellowGreen, 2.0f); + } +#endif + } + } } diff --git a/Source/Engine/UI/GUI/Brushes/TextureBrush.cs b/Source/Engine/UI/GUI/Brushes/TextureBrush.cs index 1f772e0a6..7e63e75f2 100644 --- a/Source/Engine/UI/GUI/Brushes/TextureBrush.cs +++ b/Source/Engine/UI/GUI/Brushes/TextureBrush.cs @@ -1,7 +1,5 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. -using System; - namespace FlaxEngine.GUI { /// @@ -50,4 +48,87 @@ namespace FlaxEngine.GUI Render2D.DrawTexture(Texture, rect, color); } } + + /// + /// Implementation of for using 9-slicing. + /// + /// + public sealed class Texture9SlicingBrush : IBrush + { + /// + /// The texture. + /// + [ExpandGroups, EditorOrder(0), Tooltip("The texture asset.")] + public Texture Texture; + + /// + /// The texture sampling filter mode. + /// + [ExpandGroups, EditorOrder(1), Tooltip("The texture sampling filter mode.")] + public BrushFilter Filter = BrushFilter.Linear; + + /// + /// The border size. + /// + [ExpandGroups, EditorOrder(2), Limit(0), Tooltip("The border size.")] + public float BorderSize = 10.0f; + + /// + /// The texture borders (in texture space, range 0-1). + /// + [ExpandGroups, EditorOrder(3), Limit(0, 1), Tooltip("The texture borders (in texture space, range 0-1).")] + public Margin Border = new Margin(0.1f); + +#if FLAX_EDITOR + /// + /// Displays borders (editor only). + /// + [NoSerialize, EditorOrder(4), Tooltip("Displays borders (editor only).")] + public bool ShowBorders; +#endif + + /// + /// Initializes a new instance of the class. + /// + public Texture9SlicingBrush() + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The texture. + public Texture9SlicingBrush(Texture texture) + { + Texture = texture; + } + + /// + public Vector2 Size => Texture?.Size ?? Vector2.Zero; + + /// + public unsafe void Draw(Rectangle rect, Color color) + { + if (Texture == null) + return; + var border = Border; + var borderUV = *(Vector4*)&border; + var borderSize = borderUV * new Vector4(BorderSize, BorderSize, BorderSize, BorderSize); + if (Filter == BrushFilter.Point) + Render2D.Draw9SlicingTexturePoint(Texture, rect, borderSize, borderUV, color); + else + Render2D.Draw9SlicingTexture(Texture, rect, borderSize, borderUV, color); +#if FLAX_EDITOR + if (ShowBorders) + { + var bordersRect = rect; + bordersRect.Location.X += borderSize.X; + bordersRect.Location.Y += borderSize.Z; + bordersRect.Size.X -= borderSize.X + borderSize.Y; + bordersRect.Size.Y -= borderSize.Z + borderSize.W; + Render2D.DrawRectangle(bordersRect, Color.YellowGreen, 2.0f); + } +#endif + } + } } From 2bdebdb2ef8cbd1c2a659596d49f9853c6106512 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 29 May 2021 13:48:26 +0200 Subject: [PATCH 050/198] Improve Margin editing --- .../CustomEditors/Editors/FloatEditor.cs | 5 +++ .../CustomEditors/Editors/IBrushEditor.cs | 1 + .../CustomEditors/Editors/MarginEditor.cs | 35 +++++++++++++++++++ .../Elements/IFloatValueEditor.cs | 8 +++++ .../CustomEditors/Elements/SliderElement.cs | 9 +++++ Source/Editor/GUI/Input/SliderControl.cs | 11 ++++++ 6 files changed, 69 insertions(+) create mode 100644 Source/Editor/CustomEditors/Editors/MarginEditor.cs diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs index 523db92cf..63ef35ca9 100644 --- a/Source/Editor/CustomEditors/Editors/FloatEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FloatEditor.cs @@ -15,6 +15,11 @@ namespace FlaxEditor.CustomEditors.Editors { private IFloatValueEditor _element; + /// + /// Gets the element. + /// + public IFloatValueEditor Element => _element; + /// public override DisplayStyle Style => DisplayStyle.Inline; diff --git a/Source/Editor/CustomEditors/Editors/IBrushEditor.cs b/Source/Editor/CustomEditors/Editors/IBrushEditor.cs index 8174a40a6..24f144aed 100644 --- a/Source/Editor/CustomEditors/Editors/IBrushEditor.cs +++ b/Source/Editor/CustomEditors/Editors/IBrushEditor.cs @@ -23,6 +23,7 @@ namespace FlaxEditor.CustomEditors.Editors new OptionType("Solid Color", typeof(SolidColorBrush)), new OptionType("Linear Gradient", typeof(LinearGradientBrush)), new OptionType("Texture 9-Slicing", typeof(Texture9SlicingBrush)), + new OptionType("Sprite 9-Slicing", typeof(Sprite9SlicingBrush)), }; } } diff --git a/Source/Editor/CustomEditors/Editors/MarginEditor.cs b/Source/Editor/CustomEditors/Editors/MarginEditor.cs new file mode 100644 index 000000000..758264330 --- /dev/null +++ b/Source/Editor/CustomEditors/Editors/MarginEditor.cs @@ -0,0 +1,35 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using System.Linq; +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.CustomEditors.Editors +{ + /// + /// Default implementation of the inspector used to edit Version value type properties. + /// + [CustomEditor(typeof(Margin)), DefaultEditor] + public class MarginEditor : GenericEditor + { + /// + public override void Initialize(LayoutElementsContainer layout) + { + base.Initialize(layout); + + var attributes = Values.GetAttributes(); + if (attributes != null) + { + var limit = (LimitAttribute)attributes.FirstOrDefault(x => x is LimitAttribute); + if (limit != null) + { + for (var i = 0; i < ChildrenEditors.Count; i++) + { + if (ChildrenEditors[i] is FloatEditor floatEditor) + floatEditor.Element.SetLimits(limit); + } + } + } + } + } +} diff --git a/Source/Editor/CustomEditors/Elements/IFloatValueEditor.cs b/Source/Editor/CustomEditors/Elements/IFloatValueEditor.cs index 2e67cb5f7..59c0b7a8c 100644 --- a/Source/Editor/CustomEditors/Elements/IFloatValueEditor.cs +++ b/Source/Editor/CustomEditors/Elements/IFloatValueEditor.cs @@ -1,5 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using FlaxEngine; + namespace FlaxEditor.CustomEditors.Elements { /// @@ -16,5 +18,11 @@ namespace FlaxEditor.CustomEditors.Elements /// Gets a value indicating whether user is using a slider. /// bool IsSliding { get; } + + /// + /// Sets the editor limits from member . + /// + /// The limit. + void SetLimits(LimitAttribute limit); } } diff --git a/Source/Editor/CustomEditors/Elements/SliderElement.cs b/Source/Editor/CustomEditors/Elements/SliderElement.cs index 6e981a257..988c86a69 100644 --- a/Source/Editor/CustomEditors/Elements/SliderElement.cs +++ b/Source/Editor/CustomEditors/Elements/SliderElement.cs @@ -76,5 +76,14 @@ namespace FlaxEditor.CustomEditors.Elements /// public bool IsSliding => Slider.IsSliding; + + /// + public void SetLimits(LimitAttribute limit) + { + if (limit != null) + { + Slider.SetLimits(limit); + } + } } } diff --git a/Source/Editor/GUI/Input/SliderControl.cs b/Source/Editor/GUI/Input/SliderControl.cs index 77e8a5da8..d1a2e26d1 100644 --- a/Source/Editor/GUI/Input/SliderControl.cs +++ b/Source/Editor/GUI/Input/SliderControl.cs @@ -410,6 +410,17 @@ namespace FlaxEditor.GUI.Input Value = Value; } + /// + /// Sets the limits from the attribute. + /// + /// The limits. + public void SetLimits(LimitAttribute limits) + { + _min = limits.Min; + _max = Mathf.Max(_min, limits.Max); + Value = Value; + } + /// /// Updates the text of the textbox. /// From e6074149e52303d26b1a988dcdfbcdfa28969b44 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 29 May 2021 13:48:38 +0200 Subject: [PATCH 051/198] Fix Margin loading --- Source/Engine/Serialization/JsonConverters.cs | 35 ++++++++++++------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/Source/Engine/Serialization/JsonConverters.cs b/Source/Engine/Serialization/JsonConverters.cs index 902593ac2..3130b597f 100644 --- a/Source/Engine/Serialization/JsonConverters.cs +++ b/Source/Engine/Serialization/JsonConverters.cs @@ -184,22 +184,31 @@ namespace FlaxEngine.Json case JsonToken.PropertyName: { var propertyName = (string)reader.Value; - var propertyValue = (float)reader.ReadAsDouble(); - switch (propertyName) + reader.Read(); + switch (reader.TokenType) { - case "Left": - value.Left = propertyValue; - break; - case "Right": - value.Right = propertyValue; - break; - case "Top": - value.Top = propertyValue; - break; - case "Bottom": - value.Bottom = propertyValue; + case JsonToken.Integer: + case JsonToken.Float: + { + var propertyValue = Convert.ToSingle(reader.Value); + switch (propertyName) + { + case "Left": + value.Left = propertyValue; + break; + case "Right": + value.Right = propertyValue; + break; + case "Top": + value.Top = propertyValue; + break; + case "Bottom": + value.Bottom = propertyValue; + break; + } break; } + } break; } case JsonToken.Comment: break; From 241238d2d754a6103e737df027e5caeec687956f Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 29 May 2021 18:49:56 +0200 Subject: [PATCH 052/198] Add viewpoint shortcuts options. --- Source/Editor/Options/InputOptions.cs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Source/Editor/Options/InputOptions.cs b/Source/Editor/Options/InputOptions.cs index 8f690454e..b6b716fb5 100644 --- a/Source/Editor/Options/InputOptions.cs +++ b/Source/Editor/Options/InputOptions.cs @@ -151,6 +151,30 @@ namespace FlaxEditor.Options [DefaultValue(typeof(InputBinding), "Q")] [EditorDisplay("Viewport"), EditorOrder(1550)] public InputBinding Down = new InputBinding(KeyboardKeys.Q); + + [DefaultValue(typeof(InputBinding), "Numpad0")] + [EditorDisplay("Viewport"), EditorOrder(1600)] + public InputBinding ViewpointFront = new InputBinding(KeyboardKeys.Numpad0); + + [DefaultValue(typeof(InputBinding), "Numpad5")] + [EditorDisplay("Viewport"), EditorOrder(1610)] + public InputBinding ViewpointBack = new InputBinding(KeyboardKeys.Numpad5); + + [DefaultValue(typeof(InputBinding), "Numpad4")] + [EditorDisplay("Viewport"), EditorOrder(1620)] + public InputBinding ViewpointLeft = new InputBinding(KeyboardKeys.Numpad4); + + [DefaultValue(typeof(InputBinding), "Numpad6")] + [EditorDisplay("Viewport"), EditorOrder(1630)] + public InputBinding ViewpointRight = new InputBinding(KeyboardKeys.Numpad6); + + [DefaultValue(typeof(InputBinding), "Numpad8")] + [EditorDisplay("Viewport"), EditorOrder(1640)] + public InputBinding ViewpointTop = new InputBinding(KeyboardKeys.Numpad8); + + [DefaultValue(typeof(InputBinding), "Numpad2")] + [EditorDisplay("Viewport"), EditorOrder(1650)] + public InputBinding ViewpointBottom = new InputBinding(KeyboardKeys.Numpad2); #endregion From 03014dcf08d632f7b28a887f7ca5d7edc7b9cf46 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 29 May 2021 18:51:03 +0200 Subject: [PATCH 053/198] Register viewpoint shortcuts binding in EditViewport. --- Source/Editor/Viewport/EditorViewport.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Source/Editor/Viewport/EditorViewport.cs b/Source/Editor/Viewport/EditorViewport.cs index 9c4dcc253..9a9ae9d7d 100644 --- a/Source/Editor/Viewport/EditorViewport.cs +++ b/Source/Editor/Viewport/EditorViewport.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; +using System.Linq; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Input; using FlaxEditor.Options; @@ -649,6 +650,13 @@ namespace FlaxEditor.Viewport } } + InputActions.Add(options => options.ViewpointTop, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Top").Orientation))); + InputActions.Add(options => options.ViewpointBottom, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Bottom").Orientation))); + InputActions.Add(options => options.ViewpointFront, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Front").Orientation))); + InputActions.Add(options => options.ViewpointBack, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Back").Orientation))); + InputActions.Add(options => options.ViewpointRight, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Right").Orientation))); + InputActions.Add(options => options.ViewpointLeft, () => OrientViewport(Quaternion.Euler(EditorViewportCameraViewpointValues.First(vp => vp.Name == "Left").Orientation))); + // Link for task event task.Begin += OnRenderBegin; } @@ -658,6 +666,16 @@ namespace FlaxEditor.Viewport /// protected virtual bool IsControllingMouse => false; + /// + /// Orients the viewport. + /// + /// The orientation. + protected void OrientViewport(Quaternion orientation) + { + var quat = orientation; + OrientViewport(ref quat); + } + /// /// Orients the viewport. /// From ccef5f0d73a4a57ac845465f8acceb706d2f67de Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Perrier Date: Sat, 29 May 2021 18:53:30 +0200 Subject: [PATCH 054/198] Fix viewpoints. --- .../Editor/Viewport/MainEditorGizmoViewport.cs | 18 ++++++++++++++---- Source/Editor/Viewport/PrefabWindowViewport.cs | 18 ++++++++++++++---- 2 files changed, 28 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index 8bc6b4c02..6202a8d6e 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -656,11 +656,20 @@ namespace FlaxEditor.Viewport /// Focuses the viewport on the current selection of the gizmo. /// public void FocusSelection() + { + var orientation = ViewOrientation; + FocusSelection(ref orientation); + } + + /// + /// Focuses the viewport on the current selection of the gizmo. + /// + /// The target view orientation. + public void FocusSelection(ref Quaternion orientation) { if (TransformGizmo.SelectedParents.Count == 0) return; - var orientation = ViewOrientation; var gizmoBounds = Gizmos.Active.FocusBounds; if (gizmoBounds != BoundingSphere.Empty) ((FPSCamera)ViewportCamera).ShowSphere(ref gizmoBounds, ref orientation); @@ -725,9 +734,10 @@ namespace FlaxEditor.Viewport /// protected override void OrientViewport(ref Quaternion orientation) { - FocusSelection(); - - base.OrientViewport(ref orientation); + if (TransformGizmo.SelectedParents.Count != 0) + FocusSelection(ref orientation); + else + base.OrientViewport(ref orientation); } /// diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 1c337cc25..946cf3620 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -787,11 +787,20 @@ namespace FlaxEditor.Viewport /// Focuses the viewport on the current selection of the gizmo. /// public void FocusSelection() + { + var orientation = ViewOrientation; + FocusSelection(ref orientation); + } + + /// + /// Focuses the viewport on the current selection of the gizmo. + /// + /// The target view orientation. + public void FocusSelection(ref Quaternion orientation) { if (TransformGizmo.SelectedParents.Count == 0) return; - var orientation = ViewOrientation; var gizmoBounds = Gizmos.Active.FocusBounds; if (gizmoBounds != BoundingSphere.Empty) ((FPSCamera)ViewportCamera).ShowSphere(ref gizmoBounds, ref orientation); @@ -802,9 +811,10 @@ namespace FlaxEditor.Viewport /// protected override void OrientViewport(ref Quaternion orientation) { - FocusSelection(); - - base.OrientViewport(ref orientation); + if (TransformGizmo.SelectedParents.Count != 0) + FocusSelection(ref orientation); + else + base.OrientViewport(ref orientation); } /// From c01a14007710961add7498b8fab9c15a4430e20c Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Sat, 29 May 2021 19:34:03 +0300 Subject: [PATCH 055/198] Refactor Windows version logging, log Windows build number --- .../Platform/Windows/WindowsPlatform.cpp | 208 +++++++++--------- 1 file changed, 108 insertions(+), 100 deletions(-) diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 2086cd8dc..1c87c19cb 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -30,11 +30,12 @@ void* WindowsPlatform::Instance = nullptr; namespace { - String UserLocale, ComputerName, UserName; + String UserLocale, ComputerName, UserName, WindowsName; HANDLE EngineMutex = nullptr; Rectangle VirtualScreenBounds = Rectangle(0.0f, 0.0f, 0.0f, 0.0f); int32 VersionMajor = 0; int32 VersionMinor = 0; + int32 VersionBuild = 0; int32 SystemDpi = 96; #if CRASH_LOG_ENABLE CriticalSection SymLocker; @@ -126,6 +127,102 @@ LONG GetDWORDRegKey(HKEY hKey, const Char* strValueName, DWORD& nValue, DWORD nD return nError; } +void GetWindowsVersion(String& windowsName, int32& versionMajor, int32& versionMinor, int32& versionBuild) +{ + // Get OS version + + HKEY hKey; + LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), 0, KEY_READ, &hKey); + if (lRes == ERROR_SUCCESS) + { + GetStringRegKey(hKey, TEXT("ProductName"), windowsName, TEXT("Windows")); + + DWORD currentMajorVersionNumber; + DWORD currentMinorVersionNumber; + String currentBuildNumber; + GetDWORDRegKey(hKey, TEXT("CurrentMajorVersionNumber"), currentMajorVersionNumber, 0); + GetDWORDRegKey(hKey, TEXT("CurrentMinorVersionNumber"), currentMinorVersionNumber, 0); + GetStringRegKey(hKey, TEXT("CurrentBuildNumber"), currentBuildNumber, TEXT("0")); + VersionMajor = currentMajorVersionNumber; + VersionMinor = currentMinorVersionNumber; + StringUtils::Parse(currentBuildNumber.Get(), &VersionBuild); + + if (StringUtils::Compare(windowsName.Get(), TEXT("Windows 7"), 9) == 0) + { + VersionMajor = 6; + VersionMinor = 2; + } + + if (VersionMajor == 0 && VersionMinor == 0) + { + String windowsVersion; + GetStringRegKey(hKey, TEXT("CurrentVersion"), windowsVersion, TEXT("")); + + if (windowsVersion.HasChars()) + { + const int32 dot = windowsVersion.Find('.'); + if (dot != -1) + { + StringUtils::Parse(windowsVersion.Substring(0, dot).Get(), &VersionMajor); + StringUtils::Parse(windowsVersion.Substring(dot + 1).Get(), &VersionMinor); + } + } + } + } + else + { + if (IsWindowsServer()) + { + windowsName = TEXT("Windows Server"); + versionMajor = 6; + versionMinor = 3; + } + else if (IsWindows8Point1OrGreater()) + { + windowsName = TEXT("Windows 8.1"); + versionMajor = 6; + versionMinor = 3; + } + else if (IsWindows8OrGreater()) + { + windowsName = TEXT("Windows 8"); + versionMajor = 6; + versionMinor = 2; + } + else if (IsWindows7SP1OrGreater()) + { + windowsName = TEXT("Windows 7 SP1"); + versionMajor = 6; + versionMinor = 2; + } + else if (IsWindows7OrGreater()) + { + windowsName = TEXT("Windows 7"); + versionMajor = 6; + versionMinor = 1; + } + else if (IsWindowsVistaSP2OrGreater()) + { + windowsName = TEXT("Windows Vista SP2"); + versionMajor = 6; + versionMinor = 1; + } + else if (IsWindowsVistaSP1OrGreater()) + { + windowsName = TEXT("Windows Vista SP1"); + versionMajor = 6; + versionMinor = 1; + } + else if (IsWindowsVistaOrGreater()) + { + windowsName = TEXT("Windows Vista"); + versionMajor = 6; + versionMinor = 0; + } + } + RegCloseKey(hKey); +} + LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { // Find window to process that message @@ -414,6 +511,15 @@ void WindowsPlatform::PreInit(void* hInstance) OnSymbolsPathModified(); SymLocker.Unlock(); #endif + + GetWindowsVersion(WindowsName, VersionMajor, VersionMinor, VersionBuild); + + // Validate platform + if (VersionMajor < 6) + { + Error(TEXT("Not supported operating system version.")); + exit(-1); + } } bool WindowsPlatform::IsWindows10() @@ -503,105 +609,7 @@ void WindowsPlatform::LogInfo() { Win32Platform::LogInfo(); - // Get OS version - { - String windowsName; - HKEY hKey; - LONG lRes = RegOpenKeyExW(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), 0, KEY_READ, &hKey); - if (lRes == ERROR_SUCCESS) - { - GetStringRegKey(hKey, TEXT("ProductName"), windowsName, TEXT("Windows")); - - DWORD currentMajorVersionNumber; - DWORD currentMinorVersionNumber; - GetDWORDRegKey(hKey, TEXT("CurrentMajorVersionNumber"), currentMajorVersionNumber, 0); - GetDWORDRegKey(hKey, TEXT("CurrentMinorVersionNumber"), currentMinorVersionNumber, 0); - VersionMajor = currentMajorVersionNumber; - VersionMinor = currentMinorVersionNumber; - - if (StringUtils::Compare(windowsName.Get(), TEXT("Windows 7"), 9) == 0) - { - VersionMajor = 6; - VersionMinor = 2; - } - - if (VersionMajor == 0 && VersionMinor == 0) - { - String windowsVersion; - GetStringRegKey(hKey, TEXT("CurrentVersion"), windowsVersion, TEXT("")); - - if (windowsVersion.HasChars()) - { - const int32 dot = windowsVersion.Find('.'); - if (dot != -1) - { - StringUtils::Parse(windowsVersion.Substring(0, dot).Get(), &VersionMajor); - StringUtils::Parse(windowsVersion.Substring(dot + 1).Get(), &VersionMinor); - } - } - } - } - else - { - if (IsWindowsServer()) - { - windowsName = TEXT("Windows Server"); - VersionMajor = 6; - VersionMinor = 3; - } - else if (IsWindows8Point1OrGreater()) - { - windowsName = TEXT("Windows 8.1"); - VersionMajor = 6; - VersionMinor = 3; - } - else if (IsWindows8OrGreater()) - { - windowsName = TEXT("Windows 8"); - VersionMajor = 6; - VersionMinor = 2; - } - else if (IsWindows7SP1OrGreater()) - { - windowsName = TEXT("Windows 7 SP1"); - VersionMajor = 6; - VersionMinor = 2; - } - else if (IsWindows7OrGreater()) - { - windowsName = TEXT("Windows 7"); - VersionMajor = 6; - VersionMinor = 1; - } - else if (IsWindowsVistaSP2OrGreater()) - { - windowsName = TEXT("Windows Vista SP2"); - VersionMajor = 6; - VersionMinor = 1; - } - else if (IsWindowsVistaSP1OrGreater()) - { - windowsName = TEXT("Windows Vista SP1"); - VersionMajor = 6; - VersionMinor = 1; - } - else if (IsWindowsVistaOrGreater()) - { - windowsName = TEXT("Windows Vista"); - VersionMajor = 6; - VersionMinor = 0; - } - } - RegCloseKey(hKey); - LOG(Info, "Microsoft {0} {1}-bit ({2}.{3})", windowsName, Platform::Is64BitPlatform() ? TEXT("64") : TEXT("32"), VersionMajor, VersionMinor); - } - - // Validate platform - if (VersionMajor < 6) - { - LOG(Error, "Not supported operating system version."); - exit(0); - } + LOG(Info, "Microsoft {0} {1}-bit ({2}.{3}.{4})", WindowsName, Platform::Is64BitPlatform() ? TEXT("64") : TEXT("32"), VersionMajor, VersionMinor, VersionBuild); // Check minimum amount of RAM auto memStats = Platform::GetMemoryStats(); From cc60814334667ff724cbbe167c8ef44cbdc34f0b Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Sat, 29 May 2021 19:46:37 +0300 Subject: [PATCH 056/198] Increase accuracy of Windows Sleep function Windows 10 version 1803 (build 17134) and later versions support high-resolution waitable timer, which offers much better accuracy over Sleep function without the need of increasing the global timer resolution. For older versions of Windows, we reduce the timer resolution to 1ms and use normal waitable timer for sleeping. --- Source/Engine/Platform/Win32/Win32Platform.cpp | 16 +++++++++++++++- .../Engine/Platform/Windows/WindowsPlatform.cpp | 7 +++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Platform/Win32/Win32Platform.cpp b/Source/Engine/Platform/Win32/Win32Platform.cpp index 31c0f4a36..9d2041547 100644 --- a/Source/Engine/Platform/Win32/Win32Platform.cpp +++ b/Source/Engine/Platform/Win32/Win32Platform.cpp @@ -456,7 +456,21 @@ void Win32Platform::SetThreadAffinityMask(uint64 affinityMask) void Win32Platform::Sleep(int32 milliseconds) { - ::Sleep(static_cast(milliseconds)); + static thread_local HANDLE timer = NULL; + if (timer == NULL) + { + // Attempt to create high-resolution timer for each thread (Windows 10 build 17134 or later) + timer = CreateWaitableTimerEx(NULL, NULL, CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, TIMER_ALL_ACCESS); + if (timer == NULL) // fallback for older versions of Windows + timer = CreateWaitableTimer(NULL, TRUE, NULL); + } + + // Negative value is relative to current time, minimum waitable time is 10 microseconds + LARGE_INTEGER dueTime; + dueTime.QuadPart = -int64_t(milliseconds) * 10000; + + SetWaitableTimerEx(timer, &dueTime, 0, NULL, NULL, NULL, 0); + WaitForSingleObject(timer, INFINITE); } double Win32Platform::GetTimeSeconds() diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 1c87c19cb..89e050267 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -18,6 +18,7 @@ #include "../Win32/IncludeWindowsHeaders.h" #include #include +#include #include #include #if CRASH_LOG_ENABLE @@ -579,6 +580,12 @@ bool WindowsPlatform::Init() return true; } + // Set lowest possible timer resolution for previous Windows versions + if (VersionMajor < 10 || (VersionMajor == 10 && VersionBuild < 17134)) + { + timeBeginPeriod(1); + } + DWORD tmp; Char buffer[256]; From e06200926f474597e433b3f24d8f18dcdf507439 Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Sat, 29 May 2021 20:15:29 +0300 Subject: [PATCH 057/198] Sleep between frames to save CPU cycles --- Source/Engine/Engine/Engine.cpp | 37 ++++++++------------ Source/Engine/Engine/Time.cpp | 61 ++++++++++++++++++++------------- Source/Engine/Engine/Time.h | 14 +++++--- 3 files changed, 62 insertions(+), 50 deletions(-) diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index 24d691c56..0bc79573e 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -146,26 +146,22 @@ int32 Engine::Main(const Char* cmdLine) EngineImpl::IsReady = true; // Main engine loop - bool canDraw = true; // Prevent drawing 2 or more frames in a row without update or fixed update (nothing will change) + const bool useSleep = true; // TODO: this should probably be a platform setting while (!ShouldExit()) { -#if 0 - // TODO: test it more and maybe use in future to reduce CPU usage - // Reduce CPU usage by introducing idle time if the engine is running very fast and has enough time to spend - { - float tickFps; - auto tick = Time->GetHighestFrequency(tickFps); - double tickTargetStepTime = 1.0 / tickFps; - double nextTick = tick->LastEnd + tickTargetStepTime; - double timeToTick = nextTick - Platform::GetTimeSeconds(); - int32 sleepTimeMs = Math::Min(4, Math::FloorToInt(timeToTick * (1000.0 * 0.8))); // Convert seconds to milliseconds and apply adjustment with limit - if (!Device->WasVSyncUsed() && sleepTimeMs > 0) - { - PROFILE_CPU_NAMED("Idle"); - Platform::Sleep(sleepTimeMs); - } - } -#endif + // Reduce CPU usage by introducing idle time if the engine is running very fast and has enough time to spend + if ((useSleep && Time::UpdateFPS > 0) || !Platform::GetHasFocus()) + { + double nextTick = Time::GetNextTick(); + double timeToTick = nextTick - Platform::GetTimeSeconds(); + + // Sleep less than needed, some platforms may sleep slightly more than requested + if (timeToTick > 0.002) + { + PROFILE_CPU_NAMED("Idle"); + Platform::Sleep(1); + } + } // App paused logic if (Platform::GetIsPaused()) @@ -187,7 +183,6 @@ int32 Engine::Main(const Char* cmdLine) OnUpdate(); OnLateUpdate(); Time::OnEndUpdate(); - canDraw = true; } // Start physics simulation @@ -195,16 +190,14 @@ int32 Engine::Main(const Char* cmdLine) { OnFixedUpdate(); Time::OnEndPhysics(); - canDraw = true; } // Draw frame - if (canDraw && Time::OnBeginDraw()) + if (Time::OnBeginDraw()) { OnDraw(); Time::OnEndDraw(); FrameMark; - canDraw = false; } // Collect physics simulation results (does nothing if Simulate hasn't been called in the previous loop step) diff --git a/Source/Engine/Engine/Time.cpp b/Source/Engine/Engine/Time.cpp index 2dd348207..36db6a236 100644 --- a/Source/Engine/Engine/Time.cpp +++ b/Source/Engine/Engine/Time.cpp @@ -70,6 +70,7 @@ void Time::TickData::OnBeforeRun(float targetFps, double currentTime) LastLength = static_cast(DeltaTime.Ticks) / Constants::TicksPerSecond; LastBegin = currentTime - LastLength; LastEnd = currentTime; + NextBegin = targetFps > ZeroTolerance ? LastBegin + (1.0f / targetFps) : 0.0; } void Time::TickData::OnReset(float targetFps, double currentTime) @@ -91,10 +92,18 @@ bool Time::TickData::OnTickBegin(float targetFps, float maxDeltaTime) } else { - deltaTime = Math::Clamp(time - LastBegin, 0.0, (double)maxDeltaTime); - const double minDeltaTime = targetFps > ZeroTolerance ? 1.0 / (double)targetFps : 0.0; - if (deltaTime < minDeltaTime) + if (time < NextBegin) return false; + + deltaTime = Math::Max((time - LastBegin), 0.0); + if (deltaTime > maxDeltaTime) + { + deltaTime = (double)maxDeltaTime; + NextBegin = time; + } + + if (targetFps > ZeroTolerance) + NextBegin += (1.0 / targetFps); } // Update data @@ -135,10 +144,19 @@ bool Time::FixedStepTickData::OnTickBegin(float targetFps, float maxDeltaTime) } else { - deltaTime = Math::Clamp(time - LastBegin, 0.0, (double)maxDeltaTime); - minDeltaTime = targetFps > ZeroTolerance ? 1.0 / (double)targetFps : 0.0; - if (deltaTime < minDeltaTime) + if (time < NextBegin) return false; + + minDeltaTime = targetFps > ZeroTolerance ? 1.0 / targetFps : 0.0; + deltaTime = Math::Max((time - LastBegin), 0.0); + if (deltaTime > maxDeltaTime) + { + deltaTime = (double)maxDeltaTime; + NextBegin = time; + } + + if (targetFps > ZeroTolerance) + NextBegin += (1.0 / targetFps); } Samples.Add(deltaTime); @@ -158,26 +176,23 @@ bool Time::FixedStepTickData::OnTickBegin(float targetFps, float maxDeltaTime) return true; } -Time::TickData* Time::GetHighestFrequency(float& fps) +double Time::GetNextTick() { - const auto updateFps = UpdateFPS; - const auto drawFps = DrawFPS; - const auto physicsFps = PhysicsFPS; + const double nextUpdate = Time::Update.NextBegin; + const double nextPhysics = Time::Physics.NextBegin; + const double nextDraw = Time::Draw.NextBegin; - if (physicsFps >= drawFps && physicsFps >= updateFps) - { - fps = physicsFps; - return &Physics; - } + double nextTick = MAX_double; + if (UpdateFPS > 0 && nextUpdate < nextTick) + nextTick = nextUpdate; + if (PhysicsFPS > 0 && nextPhysics < nextTick) + nextTick = nextPhysics; + if (DrawFPS > 0 && nextDraw < nextTick) + nextTick = nextDraw; - if (drawFps >= physicsFps && drawFps >= updateFps) - { - fps = drawFps; - return &Draw; - } - - fps = updateFps; - return &Update; + if (nextTick == MAX_double) + return 0.0; + return nextTick; } void Time::SetGamePaused(bool value) diff --git a/Source/Engine/Engine/Time.h b/Source/Engine/Engine/Time.h index af0545184..d4cf673e9 100644 --- a/Source/Engine/Engine/Time.h +++ b/Source/Engine/Engine/Time.h @@ -48,6 +48,11 @@ public: /// double LastLength; + /// + /// The next tick start time. + /// + double NextBegin; + /// /// The delta time. /// @@ -167,11 +172,10 @@ public: } /// - /// Gets the tick data that uses the highest frequency for the ticking. + /// Gets the time of next upcoming tick data of ticking group with defined update frequency. /// - /// The FPS rate of the highest frequency ticking group. - /// The tick data. - static TickData* GetHighestFrequency(float& fps); + /// The time of next tick. + static double GetNextTick(); /// /// Gets the value indicating whenever game logic is paused (physics, script updates, etc.). @@ -185,7 +189,7 @@ public: /// /// Sets the value indicating whenever game logic is paused (physics, script updates, etc.). /// - /// >True if pause game logic, otherwise false. + /// True if pause game logic, otherwise false. API_PROPERTY() static void SetGamePaused(bool value); /// From 97de7a627e91a55202154e64d3d48a40a4006e8c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 30 May 2021 10:53:34 +0200 Subject: [PATCH 058/198] Add order for Margin properties --- Source/Engine/UI/GUI/Margin.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Engine/UI/GUI/Margin.cs b/Source/Engine/UI/GUI/Margin.cs index 01c0ac3ab..cc6ba7ce9 100644 --- a/Source/Engine/UI/GUI/Margin.cs +++ b/Source/Engine/UI/GUI/Margin.cs @@ -29,21 +29,25 @@ namespace FlaxEngine.GUI /// /// Holds the margin to the left. /// + [EditorOrder(0)] public float Left; /// /// Holds the margin to the right. /// + [EditorOrder(1)] public float Right; /// /// Holds the margin to the top. /// + [EditorOrder(2)] public float Top; /// /// Holds the margin to the bottom. /// + [EditorOrder(3)] public float Bottom; /// From 1bd109395a6ef5fad41fcfb457e91c40501c52ab Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 30 May 2021 14:09:24 +0200 Subject: [PATCH 059/198] Optimize managed memory allocations --- Source/Editor/GUI/MainMenu.cs | 9 ++++----- Source/Engine/Engine/Engine.cpp | 7 ------- Source/Engine/Graphics/RenderTask.cs | 12 ++--------- .../InternalCalls/EngineInternalCalls.cpp | 10 ++++++++++ Source/Engine/Utilities/Utils.cs | 20 ++++++++----------- 5 files changed, 24 insertions(+), 34 deletions(-) diff --git a/Source/Editor/GUI/MainMenu.cs b/Source/Editor/GUI/MainMenu.cs index bf375a358..650f7737a 100644 --- a/Source/Editor/GUI/MainMenu.cs +++ b/Source/Editor/GUI/MainMenu.cs @@ -19,6 +19,7 @@ namespace FlaxEditor.GUI private Button _closeButton; private Button _minimizeButton; private Button _maximizeButton; + private LocalizedString _charChromeRestore, _charChromeMaximize; private Window _window; #endif private MainMenuButton _selected; @@ -151,14 +152,12 @@ namespace FlaxEditor.GUI _maximizeButton.Clicked += () => { if (_window.IsMaximized) - { _window.Restore(); - } else - { _window.Maximize(); - } }; + _charChromeRestore = ((char)EditorAssets.SegMDL2Icons.ChromeRestore).ToString(); + _charChromeMaximize = ((char)EditorAssets.SegMDL2Icons.ChromeMaximize).ToString(); } else #endif @@ -175,7 +174,7 @@ namespace FlaxEditor.GUI if (_maximizeButton != null) { - _maximizeButton.Text = ((char)(_window.IsMaximized ? EditorAssets.SegMDL2Icons.ChromeRestore : EditorAssets.SegMDL2Icons.ChromeMaximize)).ToString(); + _maximizeButton.Text = _window.IsMaximized ? _charChromeRestore : _charChromeMaximize; } } diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index 0bc79573e..a5fd460fb 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -10,18 +10,11 @@ #include "FlaxEngine.Gen.h" #include "Engine/Core/Core.h" #include "Engine/Core/Log.h" -#include "Engine/Core/Utilities.h" #include "Engine/Core/ObjectsRemovalService.h" #include "Engine/Core/Types/String.h" #include "Engine/Platform/Platform.h" -#include "Engine/Platform/CPUInfo.h" -#include "Engine/Platform/MemoryStats.h" #include "Engine/Platform/Window.h" #include "Engine/Platform/FileSystem.h" -#include "Engine/Serialization/FileWriteStream.h" -#include "Engine/Serialization/FileReadStream.h" -#include "Engine/Level/Level.h" -#include "Engine/Renderer/Renderer.h" #include "Engine/Physics/Physics.h" #include "Engine/Threading/Threading.h" #include "Engine/Threading/MainThreadTask.h" diff --git a/Source/Engine/Graphics/RenderTask.cs b/Source/Engine/Graphics/RenderTask.cs index 5b6e2a1a7..11517c097 100644 --- a/Source/Engine/Graphics/RenderTask.cs +++ b/Source/Engine/Graphics/RenderTask.cs @@ -60,15 +60,7 @@ namespace FlaxEngine private static List _postFx; private static IntPtr[] _postFxPtr; - private static readonly PostFxComparer _postFxComparer = new PostFxComparer(); - - internal sealed class PostFxComparer : IComparer - { - public int Compare(PostProcessEffect x, PostProcessEffect y) - { - return x.Order - y.Order; - } - } + private static Comparison _postFxComparison = (x, y) => x.Order - y.Order; internal IntPtr[] GetPostFx(out int count) { @@ -103,7 +95,7 @@ namespace FlaxEngine } // Sort postFx - _postFx.Sort(_postFxComparer); + _postFx.Sort(_postFxComparison); // Convert into unmanaged objects count = _postFx.Count; diff --git a/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp b/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp index 144eb6c86..050fbca3a 100644 --- a/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp +++ b/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp @@ -12,6 +12,15 @@ namespace UtilsInternal { Platform::MemoryCopy(destination, source, length); } + + MonoObject* ExtractArrayFromList(MonoObject* obj) + { + auto klass = mono_object_get_class(obj); + auto field = mono_class_get_field_from_name(klass, "_items"); + MonoObject* o; + mono_field_get_value(obj, field, &o); + return o; + } } namespace DebugLogHandlerInternal @@ -70,6 +79,7 @@ void registerFlaxEngineInternalCalls() { AnimGraphExecutor::initRuntime(); ADD_INTERNAL_CALL("FlaxEngine.Utils::MemoryCopy", &UtilsInternal::MemoryCopy); + ADD_INTERNAL_CALL("FlaxEngine.Utils::Internal_ExtractArrayFromList", &UtilsInternal::ExtractArrayFromList); ADD_INTERNAL_CALL("FlaxEngine.DebugLogHandler::Internal_LogWrite", &DebugLogHandlerInternal::LogWrite); ADD_INTERNAL_CALL("FlaxEngine.DebugLogHandler::Internal_Log", &DebugLogHandlerInternal::Log); ADD_INTERNAL_CALL("FlaxEngine.DebugLogHandler::Internal_LogException", &DebugLogHandlerInternal::LogException); diff --git a/Source/Engine/Utilities/Utils.cs b/Source/Engine/Utilities/Utils.cs index ab4fdf17b..52b800f90 100644 --- a/Source/Engine/Utilities/Utils.cs +++ b/Source/Engine/Utilities/Utils.cs @@ -180,16 +180,12 @@ namespace FlaxEngine internal static T[] ExtractArrayFromList(List list) { - T[] result = null; - if (list != null) - { - // TODO: move it to the native code - var field = list.GetType().GetField("_items", BindingFlags.Instance | BindingFlags.NonPublic); - result = (T[])field.GetValue(list); - } - return result; + return list != null ? (T[])Internal_ExtractArrayFromList(list) : null; } + [MethodImpl(MethodImplOptions.InternalCall)] + internal static extern Array Internal_ExtractArrayFromList(object list); + /// /// Reads the color from the binary stream. /// @@ -249,7 +245,7 @@ namespace FlaxEngine { return new Int2(stream.ReadInt32(), stream.ReadInt32()); } - + /// /// Reads the Int3 from the binary stream. /// @@ -259,7 +255,7 @@ namespace FlaxEngine { return new Int3(stream.ReadInt32(), stream.ReadInt32(), stream.ReadInt32()); } - + /// /// Reads the Int4 from the binary stream. /// @@ -269,7 +265,7 @@ namespace FlaxEngine { return new Int4(stream.ReadInt32(), stream.ReadInt32(), stream.ReadInt32(), stream.ReadInt32()); } - + /// /// Reads the Quaternion from the binary stream. /// @@ -402,7 +398,7 @@ namespace FlaxEngine stream.Write(value.X); stream.Write(value.Y); } - + /// /// Writes the Int3 to the binary stream. /// From 9c66ac4656f20f3498e01ccfbbba0ca7f45a247e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 30 May 2021 14:11:25 +0200 Subject: [PATCH 060/198] Add MemoryCompare, MemoryClear and MemoryCopy for direct memory access in C# --- .../Terrain/Undo/EditTerrainMapAction.cs | 4 +- Source/Engine/Animations/AnimationGraph.cs | 2 +- .../InternalCalls/EngineInternalCalls.cpp | 9 ++--- .../Scripting/ManagedCLR/MCore.Mono.cpp | 24 ++++++------ .../Serialization/UnmanagedMemoryStream.cs | 4 +- Source/Engine/Utilities/Utils.cs | 39 +++++++++++++++++-- 6 files changed, 56 insertions(+), 26 deletions(-) diff --git a/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs b/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs index f9a48d659..6a90bc61d 100644 --- a/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs +++ b/Source/Editor/Tools/Terrain/Undo/EditTerrainMapAction.cs @@ -124,7 +124,7 @@ namespace FlaxEditor.Tools.Terrain.Undo { var data = Marshal.AllocHGlobal(_heightmapDataSize); var source = GetData(ref patchCoord, tag); - Utils.MemoryCopy(source, data, _heightmapDataSize); + Utils.MemoryCopy(data, source, (ulong)_heightmapDataSize); _patches.Add(new PatchData { PatchCoord = patchCoord, @@ -150,7 +150,7 @@ namespace FlaxEditor.Tools.Terrain.Undo var data = Marshal.AllocHGlobal(_heightmapDataSize); var source = GetData(ref patch.PatchCoord, patch.Tag); - Utils.MemoryCopy(source, data, _heightmapDataSize); + Utils.MemoryCopy(data, source, (ulong)_heightmapDataSize); patch.After = data; _patches[i] = patch; } diff --git a/Source/Engine/Animations/AnimationGraph.cs b/Source/Engine/Animations/AnimationGraph.cs index 1a5c9133d..0bdc95708 100644 --- a/Source/Engine/Animations/AnimationGraph.cs +++ b/Source/Engine/Animations/AnimationGraph.cs @@ -193,7 +193,7 @@ namespace FlaxEngine throw new ArgumentNullException(nameof(destination)); destination->NodesCount = source->NodesCount; destination->Unused = source->Unused; - Utils.MemoryCopy(new IntPtr(source->Nodes), new IntPtr(destination->Nodes), source->NodesCount * sizeof(Transform)); + Utils.MemoryCopy(new IntPtr(destination->Nodes), new IntPtr(source->Nodes), (ulong)(source->NodesCount * sizeof(Transform))); destination->RootMotionTranslation = source->RootMotionTranslation; destination->RootMotionRotation = source->RootMotionRotation; destination->Position = source->Position; diff --git a/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp b/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp index 050fbca3a..814b20dcd 100644 --- a/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp +++ b/Source/Engine/Scripting/InternalCalls/EngineInternalCalls.cpp @@ -8,11 +8,6 @@ namespace UtilsInternal { - void MemoryCopy(void* source, void* destination, int32 length) - { - Platform::MemoryCopy(destination, source, length); - } - MonoObject* ExtractArrayFromList(MonoObject* obj) { auto klass = mono_object_get_class(obj); @@ -78,7 +73,9 @@ namespace FlaxLogWriterInternal void registerFlaxEngineInternalCalls() { AnimGraphExecutor::initRuntime(); - ADD_INTERNAL_CALL("FlaxEngine.Utils::MemoryCopy", &UtilsInternal::MemoryCopy); + ADD_INTERNAL_CALL("FlaxEngine.Utils::MemoryCopy", &Platform::MemoryCopy); + ADD_INTERNAL_CALL("FlaxEngine.Utils::MemoryClear", &Platform::MemoryClear); + ADD_INTERNAL_CALL("FlaxEngine.Utils::MemoryCompare", &Platform::MemoryCompare); ADD_INTERNAL_CALL("FlaxEngine.Utils::Internal_ExtractArrayFromList", &UtilsInternal::ExtractArrayFromList); ADD_INTERNAL_CALL("FlaxEngine.DebugLogHandler::Internal_LogWrite", &DebugLogHandlerInternal::LogWrite); ADD_INTERNAL_CALL("FlaxEngine.DebugLogHandler::Internal_Log", &DebugLogHandlerInternal::Log); diff --git a/Source/Engine/Scripting/ManagedCLR/MCore.Mono.cpp b/Source/Engine/Scripting/ManagedCLR/MCore.Mono.cpp index 64abca055..5e4703d2d 100644 --- a/Source/Engine/Scripting/ManagedCLR/MCore.Mono.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MCore.Mono.cpp @@ -169,18 +169,20 @@ void OnGCAllocation(MonoProfiler* profiler, MonoObject* obj) //LOG(Info, "GC new: {0}.{1} ({2} bytes)", name_space, name, size); #if 0 - if (ProfilerCPU::IsProfilingCurrentThread()) - { - static int details = 0; - if (details) - { - StackWalkDataResult stackTrace; - stackTrace.Buffer.SetCapacity(1024); - mono_stack_walk(&OnStackWalk, &stackTrace); + if (ProfilerCPU::IsProfilingCurrentThread()) + { + static int details = 0; + if (details) + { + StackWalkDataResult stackTrace; + stackTrace.Buffer.SetCapacity(1024); + mono_stack_walk(&OnStackWalk, &stackTrace); - LOG(Info, "GC new: {0}.{1} ({2} bytes). Stack Trace:\n{3}", String(name_space), String(name), size, stackTrace.Buffer.ToStringView()); - } - } + const auto msg = String::Format(TEXT("GC new: {0}.{1} ({2} bytes). Stack Trace:\n{3}"), String(name_space), String(name), size, stackTrace.Buffer.ToStringView()); + Platform::Log(*msg); + //LOG_STR(Info, msg); + } + } #endif #if COMPILE_WITH_PROFILER diff --git a/Source/Engine/Serialization/UnmanagedMemoryStream.cs b/Source/Engine/Serialization/UnmanagedMemoryStream.cs index 12b7c4634..8e0067839 100644 --- a/Source/Engine/Serialization/UnmanagedMemoryStream.cs +++ b/Source/Engine/Serialization/UnmanagedMemoryStream.cs @@ -84,7 +84,7 @@ namespace FlaxEngine.Json if (toRead <= 0) return 0; fixed (byte* bufferPtr = buffer) - Utils.MemoryCopy(new IntPtr(_ptr + _pos), new IntPtr(bufferPtr), toRead); + Utils.MemoryCopy(new IntPtr(bufferPtr), new IntPtr(_ptr + _pos), (ulong)toRead); _pos += toRead; return toRead; } @@ -135,7 +135,7 @@ namespace FlaxEngine.Json if (newPos > _length) _length = newPos; fixed (byte* bufferPtr = buffer) - Utils.MemoryCopy(new IntPtr(_pos + _pos), new IntPtr(bufferPtr), count); + Utils.MemoryCopy(new IntPtr(bufferPtr), new IntPtr(_pos + _pos), (ulong)count); _pos = newPos; } diff --git a/Source/Engine/Utilities/Utils.cs b/Source/Engine/Utilities/Utils.cs index 52b800f90..f0c9d4452 100644 --- a/Source/Engine/Utilities/Utils.cs +++ b/Source/Engine/Utilities/Utils.cs @@ -17,14 +17,45 @@ namespace FlaxEngine /// /// Copies data from one memory location to another using an unmanaged memory pointers. /// - /// - /// Uses low-level memcpy call. - /// + /// Uses low-level platform impl. + /// The source location. + /// The destination location. + /// The length (amount of bytes to copy). + [Obsolete("Use MemoryCopy with long length and source/destination swapped to match C++ API.")] + public static void MemoryCopy(IntPtr source, IntPtr destination, int length) + { + // [Deprecated on 30.05.2021, expires on 30.05.2022] + MemoryCopy(destination, source, (ulong)length); + } + + /// + /// Copies data from one memory location to another using an unmanaged memory pointers. + /// + /// Uses low-level platform impl. /// The source location. /// The destination location. /// The length (amount of bytes to copy). [MethodImpl(MethodImplOptions.InternalCall)] - public static extern void MemoryCopy(IntPtr source, IntPtr destination, int length); + public static extern void MemoryCopy(IntPtr destination, IntPtr source, ulong length); + + /// + /// Clears the memory region with zeros. + /// + /// Uses low-level platform impl. + /// Destination memory address + /// Size of the memory to clear in bytes + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern void MemoryClear(IntPtr dst, ulong size); + + /// + /// Compares two blocks of the memory. + /// + /// Uses low-level platform impl. + /// The first buffer address. + /// The second buffer address. + /// Size of the memory to compare in bytes. + [MethodImpl(MethodImplOptions.InternalCall)] + public static extern int MemoryCompare(IntPtr buf1, IntPtr buf2, ulong size); /// /// Rounds the floating point value up to 1 decimal place. From 2a3a15533ce3cad13d93ee988898dadfd5b2be54 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 30 May 2021 14:11:49 +0200 Subject: [PATCH 061/198] Add ignoring memory allocations from profiling tools in Editor Profiler --- Source/Editor/Windows/Profiler/CPU.cs | 11 +++++++++++ Source/Editor/Windows/Profiler/Memory.cs | 2 ++ 2 files changed, 13 insertions(+) diff --git a/Source/Editor/Windows/Profiler/CPU.cs b/Source/Editor/Windows/Profiler/CPU.cs index d33325fa3..c0cbc0a16 100644 --- a/Source/Editor/Windows/Profiler/CPU.cs +++ b/Source/Editor/Windows/Profiler/CPU.cs @@ -24,6 +24,17 @@ namespace FlaxEngine } } } + + public unsafe bool NameStartsWith(string prefix) + { + fixed (char* name = &Name0) + { + fixed (char* p = prefix) + { + return Utils.MemoryCompare(new IntPtr(name), new IntPtr(p), (ulong)(prefix.Length * 2)) == 0; + } + } + } } } } diff --git a/Source/Editor/Windows/Profiler/Memory.cs b/Source/Editor/Windows/Profiler/Memory.cs index 4cc48ce73..3105996f7 100644 --- a/Source/Editor/Windows/Profiler/Memory.cs +++ b/Source/Editor/Windows/Profiler/Memory.cs @@ -72,6 +72,8 @@ namespace FlaxEditor.Windows.Profiler for (int j = 0; j < ee.Length; j++) { ref var e = ref ee[j]; + if (e.NameStartsWith("ProfilerWindow")) + continue; nativeMemoryAllocation += e.NativeMemoryAllocation; managedMemoryAllocation += e.ManagedMemoryAllocation; } From 3c72abad3772d07ffb3eaf2b573de57f68665bd8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 30 May 2021 14:12:17 +0200 Subject: [PATCH 062/198] Refactor FileSystemWatcher interface to make it simpler --- .../Platform/Base/FileSystemWatcherBase.h | 67 ++++++------------- .../Windows/WindowsFileSystemWatcher.cpp | 4 +- 2 files changed, 22 insertions(+), 49 deletions(-) diff --git a/Source/Engine/Platform/Base/FileSystemWatcherBase.h b/Source/Engine/Platform/Base/FileSystemWatcherBase.h index 395f0a340..6bfb38baf 100644 --- a/Source/Engine/Platform/Base/FileSystemWatcherBase.h +++ b/Source/Engine/Platform/Base/FileSystemWatcherBase.h @@ -22,61 +22,34 @@ enum class FileSystemAction /// class FLAXENGINE_API FileSystemWatcherBase : public NonCopyable { -protected: - - bool _isEnabled; - bool _withSubDirs; - String _directory; - public: FileSystemWatcherBase(const String& directory, bool withSubDirs) - : _isEnabled(true) - , _withSubDirs(withSubDirs) - , _directory(directory) + : Directory(directory) + , WithSubDirs(withSubDirs) + , Enabled(true) { } +public: + + /// + /// The watcher directory path. + /// + const String Directory; + + /// + /// The value whenever watcher is tracking changes in subdirectories. + /// + const bool WithSubDirs; + + /// + /// The current watcher enable state. + /// + bool Enabled; + /// /// Action fired when directory or file gets changed. Can be invoked from main or other thread depending on the platform. /// Delegate OnEvent; - -public: - - /// - /// Gets the watcher directory string. - /// - /// The target directory path. - const String& GetDirectory() const - { - return _directory; - } - - /// - /// Gets the value whenever watcher is tracking changes in subdirectories. - /// - /// True if watcher is tracking changes in subdirectories, otherwise false. - bool WithSubDirs() const - { - return _withSubDirs; - } - - /// - /// Gets the current watcher enable state. - /// - /// True if watcher is enabled, otherwise false. - bool GetEnabled() const - { - return _isEnabled; - } - - /// - /// Sets the current enable state. - /// - /// A state to assign. - void SetEnabled(bool value) - { - _isEnabled = value; - } }; diff --git a/Source/Engine/Platform/Windows/WindowsFileSystemWatcher.cpp b/Source/Engine/Platform/Windows/WindowsFileSystemWatcher.cpp index 832fd9bb2..466ace2f6 100644 --- a/Source/Engine/Platform/Windows/WindowsFileSystemWatcher.cpp +++ b/Source/Engine/Platform/Windows/WindowsFileSystemWatcher.cpp @@ -87,7 +87,7 @@ VOID CALLBACK NotificationCompletion(DWORD dwErrorCode, DWORD dwNumberOfBytesTra { // Build path String path(notify->FileName, notify->FileNameLength / sizeof(WCHAR)); - path = watcher->GetDirectory() / path; + path = watcher->Directory / path; // Send event watcher->OnEvent(path, action); @@ -106,7 +106,7 @@ BOOL RefreshWatch(WindowsFileSystemWatcher* watcher) watcher->DirectoryHandle, watcher->Buffer[watcher->CurrentBuffer], FileSystemWatcher::BufferSize, - watcher->WithSubDirs() ? TRUE : FALSE, + watcher->WithSubDirs ? TRUE : FALSE, FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_FILE_NAME, &dwBytesReturned, (OVERLAPPED*)&watcher->Overlapped, From d2f339dba129fd564222386f7f1535347f60f082 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 30 May 2021 14:12:41 +0200 Subject: [PATCH 063/198] Optimize scripting object data deserialziation --- Source/Engine/Level/SceneObject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Level/SceneObject.cpp b/Source/Engine/Level/SceneObject.cpp index aaf31a4f6..9267f171f 100644 --- a/Source/Engine/Level/SceneObject.cpp +++ b/Source/Engine/Level/SceneObject.cpp @@ -149,7 +149,7 @@ void SceneObject::Deserialize(DeserializeStream& stream, ISerializeModifier* mod // Handle C# objects data serialization if (Flags & ObjectFlags::IsManagedType) { - auto* const v = stream.FindMember("V"); + auto* const v = SERIALIZE_FIND_MEMBER(stream, "V"); if (v != stream.MemberEnd() && v->value.IsObject() && v->value.MemberCount() != 0) { ManagedSerialization::Deserialize(v->value, GetOrCreateManagedInstance()); @@ -159,7 +159,7 @@ void SceneObject::Deserialize(DeserializeStream& stream, ISerializeModifier* mod // Handle custom scripting objects data serialization if (Flags & ObjectFlags::IsCustomScriptingType) { - auto* const v = stream.FindMember("D"); + auto* const v = SERIALIZE_FIND_MEMBER(stream, "D"); if (v != stream.MemberEnd() && v->value.IsObject() && v->value.MemberCount() != 0) { _type.Module->DeserializeObject(v->value, this, modifier); From d8cb1d15c7997e592cf3597394c9e31a5299e6bb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 31 May 2021 09:35:52 +0200 Subject: [PATCH 064/198] Fix warning --- Source/Editor/Windows/Profiler/CPU.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/Profiler/CPU.cs b/Source/Editor/Windows/Profiler/CPU.cs index c0cbc0a16..a93613d13 100644 --- a/Source/Editor/Windows/Profiler/CPU.cs +++ b/Source/Editor/Windows/Profiler/CPU.cs @@ -25,7 +25,7 @@ namespace FlaxEngine } } - public unsafe bool NameStartsWith(string prefix) + internal unsafe bool NameStartsWith(string prefix) { fixed (char* name = &Name0) { From a74f978f895428f90dcf3792ae2c4fe8e6e566b9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 31 May 2021 10:25:55 +0200 Subject: [PATCH 065/198] Fix lag from single axis gizmo usage on large distance --- .../Editor/Gizmo/TransformGizmoBase.Draw.cs | 6 ----- Source/Editor/Gizmo/TransformGizmoBase.cs | 23 +++++++++++-------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs b/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs index 0a11ecd77..8772b04d2 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.Draw.cs @@ -70,8 +70,6 @@ namespace FlaxEditor.Gizmo { if (!_modelTranslateAxis || !_modelTranslateAxis.IsLoaded || !_modelBox || !_modelBox.IsLoaded) break; - - // Cache data Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3); Matrix.Multiply(ref m3, ref _gizmoWorld, out m1); var axisMesh = _modelTranslateAxis.LODs[0].Meshes[0]; @@ -113,8 +111,6 @@ namespace FlaxEditor.Gizmo { if (!_modelCircle || !_modelCircle.IsLoaded || !_modelBox || !_modelBox.IsLoaded) break; - - // Cache data var circleMesh = _modelCircle.LODs[0].Meshes[0]; var boxMesh = _modelBox.LODs[0].Meshes[0]; Matrix.Scaling(8.0f, out m3); @@ -147,8 +143,6 @@ namespace FlaxEditor.Gizmo { if (!_modelScaleAxis || !_modelScaleAxis.IsLoaded || !_modelBox || !_modelBox.IsLoaded) break; - - // Cache data Matrix.Scaling(gizmoModelsScale2RealGizmoSize, out m3); Matrix.Multiply(ref m3, ref _gizmoWorld, out m1); var axisMesh = _modelScaleAxis.LODs[0].Meshes[0]; diff --git a/Source/Editor/Gizmo/TransformGizmoBase.cs b/Source/Editor/Gizmo/TransformGizmoBase.cs index 9df9b858b..768fa0617 100644 --- a/Source/Editor/Gizmo/TransformGizmoBase.cs +++ b/Source/Editor/Gizmo/TransformGizmoBase.cs @@ -213,11 +213,19 @@ namespace FlaxEditor.Gizmo ray.Position = Vector3.Transform(ray.Position, invRotationMatrix); Vector3.TransformNormal(ref ray.Direction, ref invRotationMatrix, out ray.Direction); + var planeXY = new Plane(Vector3.Backward, Vector3.Transform(Position, invRotationMatrix).Z); + var planeYZ = new Plane(Vector3.Left, Vector3.Transform(Position, invRotationMatrix).X); + var planeZX = new Plane(Vector3.Down, Vector3.Transform(Position, invRotationMatrix).Y); + var dir = Vector3.Normalize(ray.Position - Position); + var planeDotXY = Mathf.Abs(Vector3.Dot(planeXY.Normal, dir)); + var planeDotYZ = Mathf.Abs(Vector3.Dot(planeYZ.Normal, dir)); + var planeDotZX = Mathf.Abs(Vector3.Dot(planeZX.Normal, dir)); + switch (_activeAxis) { case Axis.X: { - var plane = new Plane(Position, Vector3.Normalize(ray.Position - Position)); + var plane = planeDotXY > planeDotZX ? planeXY : planeZX; if (ray.Intersects(ref plane, out float intersection)) { _intersectPosition = ray.Position + ray.Direction * intersection; @@ -229,7 +237,7 @@ namespace FlaxEditor.Gizmo } case Axis.Y: { - var plane = new Plane(Position, Vector3.Normalize(ray.Position - Position)); + var plane = planeDotXY > planeDotYZ ? planeXY : planeYZ; if (ray.Intersects(ref plane, out float intersection)) { _intersectPosition = ray.Position + ray.Direction * intersection; @@ -241,7 +249,7 @@ namespace FlaxEditor.Gizmo } case Axis.Z: { - var plane = new Plane(Position, Vector3.Normalize(ray.Position - Position)); + var plane = planeDotZX > planeDotYZ ? planeZX : planeYZ; if (ray.Intersects(ref plane, out float intersection)) { _intersectPosition = ray.Position + ray.Direction * intersection; @@ -253,8 +261,7 @@ namespace FlaxEditor.Gizmo } case Axis.YZ: { - var plane = new Plane(Vector3.Left, Vector3.Transform(Position, invRotationMatrix).X); - if (ray.Intersects(ref plane, out float intersection)) + if (ray.Intersects(ref planeYZ, out float intersection)) { _intersectPosition = ray.Position + ray.Direction * intersection; if (_lastIntersectionPosition != Vector3.Zero) @@ -265,8 +272,7 @@ namespace FlaxEditor.Gizmo } case Axis.XY: { - var plane = new Plane(Vector3.Backward, Vector3.Transform(Position, invRotationMatrix).Z); - if (ray.Intersects(ref plane, out float intersection)) + if (ray.Intersects(ref planeXY, out float intersection)) { _intersectPosition = ray.Position + ray.Direction * intersection; if (_lastIntersectionPosition != Vector3.Zero) @@ -277,8 +283,7 @@ namespace FlaxEditor.Gizmo } case Axis.ZX: { - var plane = new Plane(Vector3.Down, Vector3.Transform(Position, invRotationMatrix).Y); - if (ray.Intersects(ref plane, out float intersection)) + if (ray.Intersects(ref planeZX, out float intersection)) { _intersectPosition = ray.Position + ray.Direction * intersection; if (_lastIntersectionPosition != Vector3.Zero) From 8eadd912f7eaf39cce448aaf81858362fd8a5338 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 31 May 2021 10:26:15 +0200 Subject: [PATCH 066/198] Add `ClosestPointPointLine` for Vector3 to C# --- Source/Engine/Core/Math/CollisionsHelper.cs | 32 +++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/Source/Engine/Core/Math/CollisionsHelper.cs b/Source/Engine/Core/Math/CollisionsHelper.cs index d6e0392c8..7d85d2407 100644 --- a/Source/Engine/Core/Math/CollisionsHelper.cs +++ b/Source/Engine/Core/Math/CollisionsHelper.cs @@ -163,6 +163,38 @@ namespace FlaxEngine } } + /// + /// Determines the closest point between a point and a line. + /// + /// The point to test. + /// The line first point. + /// The line second point. + /// When the method completes, contains the closest point between the two objects. + public static void ClosestPointPointLine(ref Vector3 point, ref Vector3 p0, ref Vector3 p1, out Vector3 result) + { + Vector3 p = point - p0; + Vector3 n = p1 - p0; + float length = n.Length; + if (length < 1e-10f) + { + result = p0; + return; + } + n /= length; + float dot = Vector3.Dot(ref n, ref p); + if (dot <= 0.0f) + { + result = p0; + return; + } + if (dot >= length) + { + result = p1; + return; + } + result = p0 + n * dot; + } + /// /// Determines the closest point between a point and a triangle. /// From d436b71ef5d888e67d0d787596bc6fe31bbb765f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 31 May 2021 13:30:45 +0200 Subject: [PATCH 067/198] Fix CharacterController still colliding after being disabled at runtime --- .../Physics/Colliders/CharacterController.cpp | 75 +++++++++++++++---- .../Physics/Colliders/CharacterController.h | 41 ++++------ 2 files changed, 74 insertions(+), 42 deletions(-) diff --git a/Source/Engine/Physics/Colliders/CharacterController.cpp b/Source/Engine/Physics/Colliders/CharacterController.cpp index ef856bf79..68fd8bfbe 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.cpp +++ b/Source/Engine/Physics/Colliders/CharacterController.cpp @@ -30,6 +30,11 @@ CharacterController::CharacterController(const SpawnParams& params) static_assert(sizeof(_filterData) == sizeof(PxFilterData), "Invalid filter data size."); } +float CharacterController::GetRadius() const +{ + return _radius; +} + void CharacterController::SetRadius(const float value) { if (Math::NearEqual(value, _radius)) @@ -41,6 +46,11 @@ void CharacterController::SetRadius(const float value) UpdateBounds(); } +float CharacterController::GetHeight() const +{ + return _height; +} + void CharacterController::SetHeight(const float value) { if (Math::NearEqual(value, _height)) @@ -52,6 +62,11 @@ void CharacterController::SetHeight(const float value) UpdateBounds(); } +float CharacterController::GetSlopeLimit() const +{ + return _slopeLimit; +} + void CharacterController::SetSlopeLimit(float value) { value = Math::Clamp(value, 0.0f, 89.0f); @@ -100,6 +115,11 @@ void CharacterController::SetUpDirection(const Vector3& up) _upDirection = up; } +float CharacterController::GetMinMoveDistance() const +{ + return _minMoveDistance; +} + Vector3 CharacterController::GetUpDirection() const { return _controller ? P2C(_controller->getUpDirection()) : _upDirection; @@ -115,6 +135,16 @@ Vector3 CharacterController::GetVelocity() const return _controller ? P2C(_controller->getActor()->getLinearVelocity()) : Vector3::Zero; } +bool CharacterController::IsGrounded() const +{ + return (static_cast(_lastFlags) & static_cast(CollisionFlags::Below)) != 0; +} + +CharacterController::CollisionFlags CharacterController::GetFlags() const +{ + return _lastFlags; +} + CharacterController::CollisionFlags CharacterController::SimpleMove(const Vector3& speed) { const float deltaTime = Time::GetCurrentSafe()->DeltaTime.GetTotalSeconds(); @@ -178,7 +208,7 @@ void CharacterController::OnDebugDrawSelected() #endif -void CharacterController::CreateActor() +void CharacterController::CreateController() { ASSERT(_controller == nullptr && _shape == nullptr); @@ -218,6 +248,18 @@ void CharacterController::CreateActor() UpdateBounds(); } +void CharacterController::DeleteController() +{ + if (_controller) + { + _shape->userData = nullptr; + _controller->getActor()->userData = nullptr; + _controller->release(); + _controller = nullptr; + } + _shape = nullptr; +} + void CharacterController::UpdateSize() const { if (_controller) @@ -313,7 +355,8 @@ void CharacterController::UpdateLayerBits() void CharacterController::BeginPlay(SceneBeginData* data) { - CreateActor(); + if (IsActiveInHierarchy()) + CreateController(); // Skip collider base Actor::BeginPlay(data); @@ -325,26 +368,28 @@ void CharacterController::EndPlay() Actor::EndPlay(); // Remove controller - if (_controller) - { - _shape->userData = nullptr; - _controller->getActor()->userData = nullptr; - _controller->release(); - _controller = nullptr; - } - _shape = nullptr; + DeleteController(); } void CharacterController::OnActiveInTreeChanged() { // Skip collider base Actor::OnActiveInTreeChanged(); +} - // Clear velocities and the forces on disabled - if (!IsActiveInHierarchy() && _controller) - { - // TODO: sleep actor? clear forces? - } +void CharacterController::OnEnable() +{ + if (_controller == nullptr) + CreateController(); + + Collider::OnEnable(); +} + +void CharacterController::OnDisable() +{ + Collider::OnDisable(); + + DeleteController(); } void CharacterController::OnParentChanged() diff --git a/Source/Engine/Physics/Colliders/CharacterController.h b/Source/Engine/Physics/Colliders/CharacterController.h index 5b4dd3f49..5fece3195 100644 --- a/Source/Engine/Physics/Colliders/CharacterController.h +++ b/Source/Engine/Physics/Colliders/CharacterController.h @@ -71,15 +71,11 @@ private: uint32 _filterData[4]; public: - /// /// Gets the radius of the sphere, measured in the object's local space. The sphere radius will be scaled by the actor's world scale. /// API_PROPERTY(Attributes="EditorOrder(100), DefaultValue(50.0f), EditorDisplay(\"Collider\")") - FORCE_INLINE float GetRadius() const - { - return _radius; - } + float GetRadius() const; /// /// Sets the radius of the sphere, measured in the object's local space. The sphere radius will be scaled by the actor's world scale. @@ -90,10 +86,7 @@ public: /// Gets the height of the capsule, measured in the object's local space. The capsule height will be scaled by the actor's world scale. /// API_PROPERTY(Attributes="EditorOrder(110), DefaultValue(150.0f), EditorDisplay(\"Collider\")") - FORCE_INLINE float GetHeight() const - { - return _height; - } + float GetHeight() const; /// /// Sets the height of the capsule, measured in the object's local space. The capsule height will be scaled by the actor's world scale. @@ -104,10 +97,7 @@ public: /// Gets the slope limit (in degrees). Limits the collider to only climb slopes that are less steep (in degrees) than the indicated value. /// API_PROPERTY(Attributes="EditorOrder(210), DefaultValue(45.0f), Limit(0, 100), EditorDisplay(\"Character Controller\")") - FORCE_INLINE float GetSlopeLimit() const - { - return _slopeLimit; - } + float GetSlopeLimit() const; /// /// Sets the slope limit (in degrees). Limits the collider to only climb slopes that are less steep (in degrees) than the indicated value. @@ -151,10 +141,7 @@ public: /// Gets the minimum move distance of the character controller. The minimum traveled distance to consider. If traveled distance is smaller, the character doesn't move. This is used to stop the recursive motion algorithm when remaining distance to travel is small. /// API_PROPERTY(Attributes="EditorOrder(230), DefaultValue(0.0f), Limit(0, 1000), EditorDisplay(\"Character Controller\")") - FORCE_INLINE float GetMinMoveDistance() const - { - return _minMoveDistance; - } + float GetMinMoveDistance() const; /// /// Sets the minimum move distance of the character controller.The minimum traveled distance to consider. If traveled distance is smaller, the character doesn't move. This is used to stop the recursive motion algorithm when remaining distance to travel is small. @@ -171,18 +158,12 @@ public: /// /// Gets a value indicating whether this character was grounded during last move call grounded. /// - API_PROPERTY() FORCE_INLINE bool IsGrounded() const - { - return (static_cast(_lastFlags) & static_cast(CollisionFlags::Below)) != 0; - } + API_PROPERTY() bool IsGrounded() const; /// /// Gets the current collision flags. Tells which parts of the character capsule collided with the environment during the last move call. It can be used to trigger various character animations. /// - API_PROPERTY() FORCE_INLINE CollisionFlags GetFlags() const - { - return _lastFlags; - } + API_PROPERTY() CollisionFlags GetFlags() const; public: @@ -203,7 +184,6 @@ public: /// /// Gets the native PhysX rigid actor object. /// - /// The PhysX dynamic rigid actor. PxRigidDynamic* GetPhysXRigidActor() const; protected: @@ -211,7 +191,12 @@ protected: /// /// Creates the physics actor. /// - void CreateActor(); + void CreateController(); + + /// + /// Deletes the physics actor. + /// + void DeleteController(); /// /// Updates the character height and radius. @@ -248,6 +233,8 @@ protected: void DrawPhysicsDebug(RenderView& view) override; #endif void OnActiveInTreeChanged() override; + void OnEnable() override; + void OnDisable() override; void OnParentChanged() override; void OnTransformChanged() override; }; From 5e8da3ba5f7e019baab1e93c26d7f13bf17d7f5b Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Mon, 31 May 2021 16:46:10 +0300 Subject: [PATCH 068/198] Include toolchain preprocessor definitions in binding generation --- Source/Tools/Flax.Build/Build/Builder.cs | 5 ++- .../Build/NativeCpp/Builder.NativeCpp.cs | 32 ++++--------------- 2 files changed, 8 insertions(+), 29 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/Builder.cs b/Source/Tools/Flax.Build/Build/Builder.cs index 1bcfca18e..fd784fc3d 100644 --- a/Source/Tools/Flax.Build/Build/Builder.cs +++ b/Source/Tools/Flax.Build/Build/Builder.cs @@ -173,6 +173,7 @@ namespace Flax.Build //throw new Exception(string.Format("Platform {0} {1} is not supported.", targetPlatform, architecture)); var platform = Platform.GetPlatform(targetPlatform); + var toolchain = platform.GetToolchain(architecture); // Special case: building C# bindings only (eg. when building Linux game on Windows without C++ scripting or for C#-only projects) if (Configuration.BuildBindingsOnly || (project.IsCSharpOnlyProject && platform.HasModularBuildSupport)) @@ -186,7 +187,7 @@ namespace Flax.Build switch (target.Type) { case TargetType.NativeCpp: - BuildTargetNativeCppBindingsOnly(rules, graph, target, buildContext, platform, architecture, configuration); + BuildTargetNativeCppBindingsOnly(rules, graph, target, buildContext, toolchain, platform, architecture, configuration); break; case TargetType.DotNet: BuildTargetDotNet(rules, graph, target, platform, configuration); @@ -197,8 +198,6 @@ namespace Flax.Build continue; } - var toolchain = platform.GetToolchain(architecture); - using (new ProfileEventScope(target.Name)) { Log.Info(string.Format("Building target {0} in {1} for {2} {3}", target.Name, configuration, targetPlatform, architecture)); diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs index dc35c9fcc..1959cf3a9 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs @@ -553,7 +553,7 @@ namespace Flax.Build var graph = new TaskGraph(reference.Project.ProjectFolderPath); var skipBuild = target.IsPreBuilt || (Configuration.SkipTargets != null && Configuration.SkipTargets.Contains(target.Name)); target.PreBuild(); - referencedBuildData = BuildTargetNativeCppBindingsOnly(buildData.Rules, graph, target, buildContext, buildData.Platform, buildData.Architecture, buildData.Configuration); + referencedBuildData = BuildTargetNativeCppBindingsOnly(buildData.Rules, graph, target, buildContext, buildData.Toolchain, buildData.Platform, buildData.Architecture, buildData.Configuration, skipBuild); if (!skipBuild) { using (new ProfileEventScope("PrepareTasks")) @@ -935,7 +935,7 @@ namespace Flax.Build return buildData; } - private static BuildData BuildTargetNativeCppBindingsOnly(RulesAssembly rules, TaskGraph graph, Target target, Dictionary buildContext, Platform platform, TargetArchitecture architecture, TargetConfiguration configuration) + private static BuildData BuildTargetNativeCppBindingsOnly(RulesAssembly rules, TaskGraph graph, Target target, Dictionary buildContext, Toolchain toolchain, Platform platform, TargetArchitecture architecture, TargetConfiguration configuration, bool skipBuild = false) { if (buildContext.TryGetValue(target, out var buildData)) return buildData; @@ -954,24 +954,7 @@ namespace Flax.Build throw new Exception($"Cannot build target {target.Name}. The project file is missing (.flaxproj located in the folder above)."); // Setup build environment for the target - var platformName = platform.Target.ToString(); - var architectureName = architecture.ToString(); - var configurationName = configuration.ToString(); - var targetBuildOptions = new BuildOptions - { - Target = target, - Platform = platform, - Toolchain = null, - Architecture = architecture, - Configuration = configuration, - CompileEnv = new CompileEnvironment(), - LinkEnv = new LinkEnvironment(), - IntermediateFolder = Path.Combine(project.ProjectFolderPath, Configuration.IntermediateFolder, target.Name, platformName, architectureName, configurationName), - OutputFolder = Path.Combine(project.ProjectFolderPath, Configuration.BinariesFolder, target.Name, platformName, architectureName, configurationName), - WorkingDirectory = project.ProjectFolderPath, - HotReloadPostfix = Configuration.HotReloadPostfix, - }; - target.SetupTargetEnvironment(targetBuildOptions); + var targetBuildOptions = GetBuildOptions(target, toolchain.Platform, toolchain, toolchain.Architecture, configuration, project.ProjectFolderPath, skipBuild ? string.Empty : Configuration.HotReloadPostfix); using (new ProfileEventScope("PreBuild")) { @@ -1009,16 +992,13 @@ namespace Flax.Build { using (new ProfileEventScope(reference.Project.Name)) { + if (buildData.Toolchain == null) + buildData.Toolchain = platform.GetToolchain(architecture); + if (Configuration.BuildBindingsOnly || project.IsCSharpOnlyProject || reference.Project.IsCSharpOnlyProject || !platform.HasRequiredSDKsInstalled) - { BuildTargetReferenceNativeCppBindingsOnly(buildContext, buildData, reference); - } else - { - if (buildData.Toolchain == null) - buildData.Toolchain = platform.GetToolchain(architecture); BuildTargetReferenceNativeCpp(buildContext, buildData, reference); - } } } } From 2c2c7744eb163e02fb17c9548cb017ed4688058f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 31 May 2021 16:05:35 +0200 Subject: [PATCH 069/198] Add AuxTool to AOT pipeline in Game Cooker --- Source/Editor/Cooker/PlatformTools.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Editor/Cooker/PlatformTools.h b/Source/Editor/Cooker/PlatformTools.h index 20d578d2e..63b018343 100644 --- a/Source/Editor/Cooker/PlatformTools.h +++ b/Source/Editor/Cooker/PlatformTools.h @@ -136,6 +136,8 @@ public: String AssemblerArgs; String ArchiverPath; String ArchiverArgs; + String AuxToolPath; + String AuxToolArgs; String AotCachePath; Dictionary EnvVars; Array AssembliesSearchDirs; From 760b687b02d17a55814fc878806f1bb730fd8333 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 31 May 2021 19:32:35 +0200 Subject: [PATCH 070/198] Rename AnimationManager to Animations and expose to public --- .../{AnimationManager.cpp => Animations.cpp} | 18 +++++++++--------- .../{AnimationManager.h => Animations.h} | 2 +- Source/Engine/Level/Actors/AnimatedModel.cpp | 6 +++--- Source/Engine/Level/Actors/AnimatedModel.h | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) rename Source/Engine/Animations/{AnimationManager.cpp => Animations.cpp} (86%) rename Source/Engine/Animations/{AnimationManager.h => Animations.h} (94%) diff --git a/Source/Engine/Animations/AnimationManager.cpp b/Source/Engine/Animations/Animations.cpp similarity index 86% rename from Source/Engine/Animations/AnimationManager.cpp rename to Source/Engine/Animations/Animations.cpp index 6614c611a..9ef229d23 100644 --- a/Source/Engine/Animations/AnimationManager.cpp +++ b/Source/Engine/Animations/Animations.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. -#include "AnimationManager.h" +#include "Animations.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Level/Actors/AnimatedModel.h" #include "Engine/Engine/Time.h" @@ -9,12 +9,12 @@ Array UpdateList; Array UpdateBones; -class AnimationManagerService : public EngineService +class AnimationsService : public EngineService { public: - AnimationManagerService() - : EngineService(TEXT("Animation Manager"), -10) + AnimationsService() + : EngineService(TEXT("Animations"), -10) { } @@ -22,9 +22,9 @@ public: void Dispose() override; }; -AnimationManagerService AnimationManagerInstance; +AnimationsService AnimationManagerInstance; -void AnimationManagerService::Update() +void AnimationsService::Update() { PROFILE_CPU_NAMED("Animations"); @@ -78,18 +78,18 @@ void AnimationManagerService::Update() UpdateList.Clear(); } -void AnimationManagerService::Dispose() +void AnimationsService::Dispose() { UpdateList.Resize(0); UpdateBones.Resize(0); } -void AnimationManager::AddToUpdate(AnimatedModel* obj) +void Animations::AddToUpdate(AnimatedModel* obj) { UpdateList.Add(obj); } -void AnimationManager::RemoveFromUpdate(AnimatedModel* obj) +void Animations::RemoveFromUpdate(AnimatedModel* obj) { UpdateList.Remove(obj); } diff --git a/Source/Engine/Animations/AnimationManager.h b/Source/Engine/Animations/Animations.h similarity index 94% rename from Source/Engine/Animations/AnimationManager.h rename to Source/Engine/Animations/Animations.h index fc4e733ce..a1b90e753 100644 --- a/Source/Engine/Animations/AnimationManager.h +++ b/Source/Engine/Animations/Animations.h @@ -7,7 +7,7 @@ class AnimatedModel; /// /// The animations service. /// -class AnimationManager +class FLAXENGINE_API Animations { public: diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index a62aad592..d049f9731 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -2,7 +2,7 @@ #include "AnimatedModel.h" #include "BoneSocket.h" -#include "Engine/Animations/AnimationManager.h" +#include "Engine/Animations/Animations.h" #include "Engine/Engine/Engine.h" #if USE_EDITOR #include "Editor/Editor.h" @@ -52,7 +52,7 @@ void AnimatedModel::UpdateAnimation() if (AnimationGraph && AnimationGraph->IsLoaded() && AnimationGraph->Graph.IsReady()) { // Request an animation update - AnimationManager::AddToUpdate(this); + Animations::AddToUpdate(this); } else { @@ -389,7 +389,7 @@ void AnimatedModel::BeginPlay(SceneBeginData* data) void AnimatedModel::EndPlay() { - AnimationManager::RemoveFromUpdate(this); + Animations::RemoveFromUpdate(this); SetMasterPoseModel(nullptr); // Base diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index df218b897..8c9023d23 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -15,7 +15,7 @@ API_CLASS() class FLAXENGINE_API AnimatedModel : public ModelInstanceActor { DECLARE_SCENE_OBJECT(AnimatedModel); - friend class AnimationManagerService; + friend class AnimationsService; public: /// From 6877e14b049947562886b2e366d1145864c26bc1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 31 May 2021 22:40:41 +0200 Subject: [PATCH 071/198] Rename ParticleManager to Particles --- Source/Engine/Particles/ParticleEffect.cpp | 8 +++---- Source/Engine/Particles/ParticleEmitter.cpp | 4 ++-- .../{ParticleManager.cpp => Particles.cpp} | 24 +++++++++---------- .../{ParticleManager.h => Particles.h} | 2 +- Source/Engine/Particles/ParticlesData.h | 2 +- .../Engine/Particles/ParticlesSimulation.cpp | 8 +++---- 6 files changed, 24 insertions(+), 24 deletions(-) rename Source/Engine/Particles/{ParticleManager.cpp => Particles.cpp} (98%) rename Source/Engine/Particles/{ParticleManager.h => Particles.h} (98%) diff --git a/Source/Engine/Particles/ParticleEffect.cpp b/Source/Engine/Particles/ParticleEffect.cpp index f23aff28c..309b760fe 100644 --- a/Source/Engine/Particles/ParticleEffect.cpp +++ b/Source/Engine/Particles/ParticleEffect.cpp @@ -1,7 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "ParticleEffect.h" -#include "ParticleManager.h" +#include "Particles.h" #include "Engine/Serialization/JsonTools.h" #include "Engine/Serialization/Serialization.h" #include "Engine/Level/Scene/SceneRendering.h" @@ -270,7 +270,7 @@ void ParticleEffect::UpdateSimulation() // Request update _lastUpdateFrame = Engine::FrameCount; _lastMinDstSqr = MAX_float; - ParticleManager::UpdateEffect(this); + Particles::UpdateEffect(this); } void ParticleEffect::UpdateBounds() @@ -493,7 +493,7 @@ bool ParticleEffect::HasContentLoaded() const void ParticleEffect::Draw(RenderContext& renderContext) { _lastMinDstSqr = Math::Min(_lastMinDstSqr, Vector3::DistanceSquared(GetPosition(), renderContext.View.Position)); - ParticleManager::DrawParticles(renderContext, this); + Particles::DrawParticles(renderContext, this); } void ParticleEffect::DrawGeneric(RenderContext& renderContext) @@ -679,7 +679,7 @@ void ParticleEffect::Deserialize(DeserializeStream& stream, ISerializeModifier* void ParticleEffect::EndPlay() { CacheModifiedParameters(); - ParticleManager::OnEffectDestroy(this); + Particles::OnEffectDestroy(this); Instance.ClearState(); _parameters.Clear(); _parametersVersion = 0; diff --git a/Source/Engine/Particles/ParticleEmitter.cpp b/Source/Engine/Particles/ParticleEmitter.cpp index 0b1d3fbf4..b5c3ecdae 100644 --- a/Source/Engine/Particles/ParticleEmitter.cpp +++ b/Source/Engine/Particles/ParticleEmitter.cpp @@ -1,9 +1,9 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "ParticleEmitter.h" -#include "ParticleManager.h" #include "ParticleSystem.h" #include "ParticleEffect.h" +#include "Particles.h" #include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Content/Upgraders/ShaderAssetUpgrader.h" #include "Engine/Core/Log.h" @@ -287,7 +287,7 @@ void ParticleEmitter::unload(bool isReloading) UnregisterForShaderReloads(this); #endif - ParticleManager::OnEmitterUnload(this); + Particles::OnEmitterUnload(this); Graph.Clear(); #if COMPILE_WITH_GPU_PARTICLES diff --git a/Source/Engine/Particles/ParticleManager.cpp b/Source/Engine/Particles/Particles.cpp similarity index 98% rename from Source/Engine/Particles/ParticleManager.cpp rename to Source/Engine/Particles/Particles.cpp index 75d39cd13..468906a3b 100644 --- a/Source/Engine/Particles/ParticleManager.cpp +++ b/Source/Engine/Particles/Particles.cpp @@ -1,6 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. -#include "ParticleManager.h" +#include "Particles.h" #include "Engine/Content/Assets/Model.h" #include "Engine/Core/Collections/Sorting.h" #include "Engine/Core/Collections/HashSet.h" @@ -112,8 +112,8 @@ namespace ParticleManagerImpl using namespace ParticleManagerImpl; -bool ParticleManager::EnableParticleBufferPooling = true; -float ParticleManager::ParticleBufferRecycleTimeout = 10.0f; +bool Particles::EnableParticleBufferPooling = true; +float Particles::ParticleBufferRecycleTimeout = 10.0f; SpriteParticleRenderer SpriteRenderer; @@ -155,12 +155,12 @@ public: ParticleManagerService ParticleManagerServiceInstance; -void ParticleManager::UpdateEffect(ParticleEffect* effect) +void Particles::UpdateEffect(ParticleEffect* effect) { UpdateList.Add(effect); } -void ParticleManager::OnEffectDestroy(ParticleEffect* effect) +void Particles::OnEffectDestroy(ParticleEffect* effect) { UpdateList.Remove(effect); #if COMPILE_WITH_GPU_PARTICLES @@ -895,7 +895,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa #endif -void ParticleManager::DrawParticles(RenderContext& renderContext, ParticleEffect* effect) +void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effect) { // Setup auto& view = renderContext.View; @@ -1066,7 +1066,7 @@ void UpdateGPU(RenderTask* task, GPUContext* context) #endif -ParticleBuffer* ParticleManager::AcquireParticleBuffer(ParticleEmitter* emitter) +ParticleBuffer* Particles::AcquireParticleBuffer(ParticleEmitter* emitter) { ParticleBuffer* result = nullptr; ASSERT(emitter && emitter->IsLoaded()); @@ -1118,7 +1118,7 @@ ParticleBuffer* ParticleManager::AcquireParticleBuffer(ParticleEmitter* emitter) return result; } -void ParticleManager::RecycleParticleBuffer(ParticleBuffer* buffer) +void Particles::RecycleParticleBuffer(ParticleBuffer* buffer) { if (buffer->Emitter->EnablePooling && EnableParticleBufferPooling) { @@ -1138,7 +1138,7 @@ void ParticleManager::RecycleParticleBuffer(ParticleBuffer* buffer) } } -void ParticleManager::OnEmitterUnload(ParticleEmitter* emitter) +void Particles::OnEmitterUnload(ParticleEmitter* emitter) { PoolLocker.Lock(); const auto entries = Pool.TryGet(emitter); @@ -1262,7 +1262,7 @@ void ParticleManagerService::Update() { if (emitterInstance.Buffer) { - ParticleManager::RecycleParticleBuffer(emitterInstance.Buffer); + Particles::RecycleParticleBuffer(emitterInstance.Buffer); emitterInstance.Buffer = nullptr; } } @@ -1291,7 +1291,7 @@ void ParticleManagerService::Update() data.Sync(effect->Instance, particleSystem, track.AsEmitter.Index); if (!data.Buffer) { - data.Buffer = ParticleManager::AcquireParticleBuffer(emitter); + data.Buffer = Particles::AcquireParticleBuffer(emitter); } data.Time += dt; @@ -1355,7 +1355,7 @@ void ParticleManagerService::Update() for (int32 j = 0; j < entries.Count(); j++) { auto& e = entries[j]; - if (timeSeconds - e.LastTimeUsed >= ParticleManager::ParticleBufferRecycleTimeout) + if (timeSeconds - e.LastTimeUsed >= Particles::ParticleBufferRecycleTimeout) { Delete(e.Buffer); entries.RemoveAt(j--); diff --git a/Source/Engine/Particles/ParticleManager.h b/Source/Engine/Particles/Particles.h similarity index 98% rename from Source/Engine/Particles/ParticleManager.h rename to Source/Engine/Particles/Particles.h index 4308b2ab8..ed66d7bb4 100644 --- a/Source/Engine/Particles/ParticleManager.h +++ b/Source/Engine/Particles/Particles.h @@ -15,7 +15,7 @@ class Actor; /// /// The particles service used for simulation and emitters data pooling. /// -class FLAXENGINE_API ParticleManager +class FLAXENGINE_API Particles { public: diff --git a/Source/Engine/Particles/ParticlesData.h b/Source/Engine/Particles/ParticlesData.h index edda3c507..af1340ef8 100644 --- a/Source/Engine/Particles/ParticlesData.h +++ b/Source/Engine/Particles/ParticlesData.h @@ -14,7 +14,7 @@ #define PARTICLE_EMITTER_MAX_RIBBONS 4 class ParticleEmitter; -class ParticleManager; +class Particles; class GPUBuffer; class DynamicIndexBuffer; diff --git a/Source/Engine/Particles/ParticlesSimulation.cpp b/Source/Engine/Particles/ParticlesSimulation.cpp index fb0e9bf74..e85665afa 100644 --- a/Source/Engine/Particles/ParticlesSimulation.cpp +++ b/Source/Engine/Particles/ParticlesSimulation.cpp @@ -1,9 +1,9 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "ParticlesSimulation.h" -#include "ParticleManager.h" #include "ParticleSystem.h" #include "ParticleEmitter.h" +#include "Particles.h" #include "Engine/Graphics/GPUBuffer.h" #include "Engine/Graphics/GPUDevice.h" @@ -15,7 +15,7 @@ ParticleEmitterInstance::~ParticleEmitterInstance() { if (Buffer) { - ParticleManager::RecycleParticleBuffer(Buffer); + Particles::RecycleParticleBuffer(Buffer); } } @@ -30,7 +30,7 @@ void ParticleEmitterInstance::ClearState() #endif if (Buffer) { - ParticleManager::RecycleParticleBuffer(Buffer); + Particles::RecycleParticleBuffer(Buffer); Buffer = nullptr; } } @@ -72,7 +72,7 @@ void ParticleEmitterInstance::Sync(ParticleSystemInstance& systemInstance, Parti // Sync buffer version if (Buffer && Buffer->Version != Version) { - ParticleManager::RecycleParticleBuffer(Buffer); + Particles::RecycleParticleBuffer(Buffer); Buffer = nullptr; } } From 623299e59f2670d9d413c5f88ef6d04bde3c33b7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jun 2021 10:27:30 +0200 Subject: [PATCH 072/198] Add support for AssetPreview to use debug drawing if needed --- Source/Editor/Gizmo/EditorPrimitives.cs | 12 ++- .../Viewport/MainEditorGizmoViewport.cs | 10 +- .../Editor/Viewport/PrefabWindowViewport.cs | 64 ++++------- .../Editor/Viewport/Previews/AssetPreview.cs | 100 +++++++++++++++++- Source/Engine/Graphics/RenderTask.cpp | 2 +- 5 files changed, 135 insertions(+), 53 deletions(-) diff --git a/Source/Editor/Gizmo/EditorPrimitives.cs b/Source/Editor/Gizmo/EditorPrimitives.cs index cdb9f4747..3e37a2361 100644 --- a/Source/Editor/Gizmo/EditorPrimitives.cs +++ b/Source/Editor/Gizmo/EditorPrimitives.cs @@ -8,9 +8,8 @@ namespace FlaxEditor.Gizmo /// /// Interface for editor viewports that can contain and use . /// - /// [HideInEditor] - public interface IEditorPrimitivesOwner : IGizmoOwner + public interface IEditorPrimitivesOwner { /// /// Draws the custom editor primitives. @@ -68,11 +67,14 @@ namespace FlaxEditor.Gizmo var renderList = RenderList.GetFromPool(); var prevList = renderContext.List; renderContext.List = renderList; - for (int i = 0; i < Viewport.Gizmos.Count; i++) + try { - Viewport.Gizmos[i].Draw(ref renderContext); + Viewport.DrawEditorPrimitives(context, ref renderContext, target, targetDepth); + } + catch (Exception ex) + { + Editor.LogWarning(ex); } - Viewport.DrawEditorPrimitives(context, ref renderContext, target, targetDepth); // Sort draw calls renderList.SortDrawCalls(ref renderContext, false, DrawCallsListType.GBuffer); diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index 6202a8d6e..68667b16f 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -22,7 +22,7 @@ namespace FlaxEditor.Viewport /// Main editor gizmo viewport used by the . /// /// - public partial class MainEditorGizmoViewport : EditorGizmoViewport, IEditorPrimitivesOwner + public partial class MainEditorGizmoViewport : EditorGizmoViewport, IEditorPrimitivesOwner, IGizmoOwner { private readonly Editor _editor; @@ -463,6 +463,12 @@ namespace FlaxEditor.Viewport DebugDraw.Draw(ref renderContext, target.View(), targetDepth.View(), true); } + + // Draw gizmos + for (int i = 0; i < Gizmos.Count; i++) + { + Gizmos[i].Draw(ref renderContext); + } } private void OnPostRender(GPUContext context, RenderContext renderContext) @@ -660,7 +666,7 @@ namespace FlaxEditor.Viewport var orientation = ViewOrientation; FocusSelection(ref orientation); } - + /// /// Focuses the viewport on the current selection of the gizmo. /// diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index 946cf3620..df3057203 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -24,7 +24,7 @@ namespace FlaxEditor.Viewport /// /// /// - public class PrefabWindowViewport : PrefabPreview, IEditorPrimitivesOwner + public class PrefabWindowViewport : PrefabPreview, IGizmoOwner { [HideInEditor] private sealed class PrefabSpritesRenderer : MainEditorGizmoViewport.EditorSpritesRenderer @@ -51,7 +51,6 @@ namespace FlaxEditor.Viewport private ViewportWidgetButton _scaleSnapping; private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32); - private IntPtr _debugDrawContext; private PrefabSpritesRenderer _spritesRenderer; private readonly DragAssets _dragAssets; private readonly DragActorType _dragActorType = new DragActorType(ValidateDragActorType); @@ -67,16 +66,6 @@ namespace FlaxEditor.Viewport /// public SelectionOutline SelectionOutline; - /// - /// The editor primitives postFx. - /// - public EditorPrimitives EditorPrimitives; - - /// - /// Gets or sets a value indicating whether draw shapes. - /// - public bool DrawDebugDraw = true; - /// /// Initializes a new instance of the class. /// @@ -88,8 +77,9 @@ namespace FlaxEditor.Viewport _window.SelectionChanged += OnSelectionChanged; Undo = window.Undo; ViewportCamera = new FPSCamera(); - _debugDrawContext = DebugDraw.AllocateContext(); _dragAssets = new DragAssets(ValidateDragItem); + ShowDebugDraw = true; + ShowEditorPrimitives = true; // Prepare rendering task Task.ActorsSource = ActorsSources.CustomActors; @@ -102,9 +92,6 @@ namespace FlaxEditor.Viewport SelectionOutline = FlaxEngine.Object.New(); SelectionOutline.SelectionGetter = () => TransformGizmo.SelectedParents; Task.CustomPostFx.Add(SelectionOutline); - EditorPrimitives = FlaxEngine.Object.New(); - EditorPrimitives.Viewport = this; - Task.CustomPostFx.Add(EditorPrimitives); _spritesRenderer = FlaxEngine.Object.New(); _spritesRenderer.Task = Task; _spritesRenderer.Viewport = this; @@ -282,13 +269,6 @@ namespace FlaxEditor.Viewport { var task = renderContext.Task; - // Render editor primitives, gizmo and debug shapes in debug view modes - // Note: can use Output buffer as both input and output because EditorPrimitives is using a intermediate buffers - if (EditorPrimitives && EditorPrimitives.CanRender) - { - EditorPrimitives.Render(context, ref renderContext, task.Output, task.Output); - } - // Render editor sprites if (_spritesRenderer && _spritesRenderer.CanRender) { @@ -791,7 +771,7 @@ namespace FlaxEditor.Viewport var orientation = ViewOrientation; FocusSelection(ref orientation); } - + /// /// Focuses the viewport on the current selection of the gizmo. /// @@ -876,35 +856,35 @@ namespace FlaxEditor.Viewport /// public override void OnDestroy() { - if (_debugDrawContext != IntPtr.Zero) - { - DebugDraw.FreeContext(_debugDrawContext); - _debugDrawContext = IntPtr.Zero; - } FlaxEngine.Object.Destroy(ref SelectionOutline); - FlaxEngine.Object.Destroy(ref EditorPrimitives); FlaxEngine.Object.Destroy(ref _spritesRenderer); base.OnDestroy(); } /// - public void DrawEditorPrimitives(GPUContext context, ref RenderContext renderContext, GPUTexture target, GPUTexture targetDepth) + public override void DrawEditorPrimitives(GPUContext context, ref RenderContext renderContext, GPUTexture target, GPUTexture targetDepth) { - // Draw selected objects debug shapes and visuals - if (DrawDebugDraw && (renderContext.View.Flags & ViewFlags.DebugDraw) == ViewFlags.DebugDraw) + base.DrawEditorPrimitives(context, ref renderContext, target, targetDepth); + + // Draw gizmos + for (int i = 0; i < Gizmos.Count; i++) { - DebugDraw.SetContext(_debugDrawContext); - DebugDraw.UpdateContext(_debugDrawContext, 1.0f / Engine.FramesPerSecond); - unsafe + Gizmos[i].Draw(ref renderContext); + } + } + + /// + protected override void OnDebugDraw(GPUContext context, ref RenderContext renderContext) + { + base.OnDebugDraw(context, ref renderContext); + + unsafe + { + fixed (IntPtr* actors = _debugDrawData.ActorsPtrs) { - fixed (IntPtr* actors = _debugDrawData.ActorsPtrs) - { - DebugDraw.DrawActors(new IntPtr(actors), _debugDrawData.ActorsCount, false); - } + DebugDraw.DrawActors(new IntPtr(actors), _debugDrawData.ActorsCount, false); } - DebugDraw.Draw(ref renderContext, target.View(), targetDepth.View(), true); - DebugDraw.SetContext(IntPtr.Zero); } } } diff --git a/Source/Editor/Viewport/Previews/AssetPreview.cs b/Source/Editor/Viewport/Previews/AssetPreview.cs index 791e98143..f6126480c 100644 --- a/Source/Editor/Viewport/Previews/AssetPreview.cs +++ b/Source/Editor/Viewport/Previews/AssetPreview.cs @@ -1,5 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System; +using FlaxEditor.Gizmo; using FlaxEditor.GUI.ContextMenu; using FlaxEditor.Viewport.Cameras; using FlaxEngine; @@ -11,9 +13,13 @@ namespace FlaxEditor.Viewport.Previews /// Generic asset preview editor viewport base class. /// /// - public abstract class AssetPreview : EditorViewport + public abstract class AssetPreview : EditorViewport, IEditorPrimitivesOwner { private ContextMenuButton _showDefaultSceneButton; + private IntPtr _debugDrawContext; + private bool _debugDrawEnable; + private bool _editorPrimitivesEnable; + private EditorPrimitives _editorPrimitives; /// /// The preview light. Allows to modify rendering settings. @@ -61,6 +67,56 @@ namespace FlaxEditor.Viewport.Previews } } + /// + /// Gets or sets a value indicating whether draw shapes. + /// + public bool ShowDebugDraw + { + get => _debugDrawEnable; + set + { + if (_debugDrawEnable == value) + return; + _debugDrawEnable = value; + if (_debugDrawContext == IntPtr.Zero) + { + _debugDrawContext = DebugDraw.AllocateContext(); + var view = Task.View; + view.Flags |= ViewFlags.DebugDraw; + Task.View = view; + } + } + } + + /// + /// Gets or sets a value indicating whether draw shapes and other editor primitives such as gizmos. + /// + public bool ShowEditorPrimitives + { + get => _editorPrimitivesEnable; + set + { + if (_editorPrimitivesEnable == value) + return; + _editorPrimitivesEnable = value; + if (_editorPrimitives == null) + { + _editorPrimitives = Object.New(); + _editorPrimitives.Viewport = this; + Task.CustomPostFx.Add(_editorPrimitives); + Task.PostRender += OnPostRender; + var view = Task.View; + view.Flags |= ViewFlags.CustomPostProcess; + Task.View = view; + } + } + } + + /// + /// Gets the editor primitives renderer. Valid only if is true. + /// + public EditorPrimitives EditorPrimitives => _editorPrimitives; + /// /// Initializes a new instance of the class. /// @@ -114,7 +170,7 @@ namespace FlaxEditor.Viewport.Previews SunPower = 9.0f }; // - SkyLight = new SkyLight() + SkyLight = new SkyLight { Mode = SkyLight.Modes.CustomTexture, Brightness = 2.1f, @@ -135,20 +191,58 @@ namespace FlaxEditor.Viewport.Previews Task.AddCustomActor(PostFxVolume); } + private void OnPostRender(GPUContext context, RenderContext renderContext) + { + if (renderContext.View.Mode != ViewMode.Default && _editorPrimitives && _editorPrimitives.CanRender) + { + // Render editor primitives, gizmo and debug shapes in debug view modes + // Note: can use Output buffer as both input and output because EditorPrimitives is using a intermediate buffers + _editorPrimitives.Render(context, ref renderContext, renderContext.Task.Output, renderContext.Task.Output); + } + } + + /// + /// Called when drawing debug shapes with for this viewport. + /// + /// The GPU context. + /// The render context. + protected virtual void OnDebugDraw(GPUContext context, ref RenderContext renderContext) + { + } + /// public override bool HasLoadedAssets => base.HasLoadedAssets && Sky.HasContentLoaded && EnvProbe.Probe.IsLoaded && PostFxVolume.HasContentLoaded; /// public override void OnDestroy() { - // Ensure to cleanup created actor objects Object.Destroy(ref PreviewLight); Object.Destroy(ref EnvProbe); Object.Destroy(ref Sky); Object.Destroy(ref SkyLight); Object.Destroy(ref PostFxVolume); + Object.Destroy(ref _editorPrimitives); + if (_debugDrawContext != IntPtr.Zero) + { + DebugDraw.FreeContext(_debugDrawContext); + _debugDrawContext = IntPtr.Zero; + } base.OnDestroy(); } + + /// + public virtual void DrawEditorPrimitives(GPUContext context, ref RenderContext renderContext, GPUTexture target, GPUTexture targetDepth) + { + // Draw selected objects debug shapes and visuals + if (ShowDebugDraw && (renderContext.View.Flags & ViewFlags.DebugDraw) == ViewFlags.DebugDraw) + { + DebugDraw.SetContext(_debugDrawContext); + DebugDraw.UpdateContext(_debugDrawContext, 1.0f / Mathf.Max(Engine.FramesPerSecond, 1)); + OnDebugDraw(context, ref renderContext); + DebugDraw.Draw(ref renderContext, target.View(), targetDepth.View(), true); + DebugDraw.SetContext(IntPtr.Zero); + } + } } } diff --git a/Source/Engine/Graphics/RenderTask.cpp b/Source/Engine/Graphics/RenderTask.cpp index b4da776d7..f3e73c220 100644 --- a/Source/Engine/Graphics/RenderTask.cpp +++ b/Source/Engine/Graphics/RenderTask.cpp @@ -267,7 +267,7 @@ void SceneRenderTask::OnCollectDrawCalls(RenderContext& renderContext) { for (int32 i = 0; i < CustomActors.Count(); i++) { - if (CustomActors[i]->GetIsActive()) + if (CustomActors[i] && CustomActors[i]->GetIsActive()) CustomActors[i]->DrawHierarchy(renderContext); } } From a730c3b1af236d6e1aafa3d3059f29a2ae14c1c8 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jun 2021 10:27:57 +0200 Subject: [PATCH 073/198] Add using immediate DebugDraw for animated model skeleton drawing in Editor preview --- .../Viewport/Previews/AnimatedModelPreview.cs | 193 ++++++------------ 1 file changed, 57 insertions(+), 136 deletions(-) diff --git a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs index 697a7c1c1..86ee3dc17 100644 --- a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs +++ b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs @@ -1,6 +1,5 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. -using System.Collections.Generic; using FlaxEngine; using FlaxEditor.GUI.Input; using Object = FlaxEngine.Object; @@ -14,11 +13,7 @@ namespace FlaxEditor.Viewport.Previews public class AnimatedModelPreview : AssetPreview { private AnimatedModel _previewModel; - private StaticModel _previewNodesActor; - private Model _previewNodesModel; - private int _previewNodesCounter; - private List _previewNodesVB; - private List _previewNodesIB; + private bool _showNodes; /// /// Gets or sets the skinned model asset to preview. @@ -42,7 +37,19 @@ namespace FlaxEditor.Viewport.Previews /// /// Gets or sets a value indicating whether show animated model skeleton nodes debug view. /// - public bool ShowNodes { get; set; } = false; + public bool ShowNodes + { + get => _showNodes; + set + { + _showNodes = value; + if (value) + { + ShowDebugDraw = true; + ShowEditorPrimitives = true; + } + } + } /// /// Gets or sets a value indicating whether scale the model to the normalized bounds. @@ -72,17 +79,8 @@ namespace FlaxEditor.Viewport.Previews UpdateMode = AnimatedModel.AnimationUpdateMode.Manual }; - _previewNodesModel = FlaxEngine.Content.CreateVirtualAsset(); - _previewNodesModel.SetupLODs(new[] { 1 }); - _previewNodesActor = new StaticModel - { - Model = _previewNodesModel - }; - _previewNodesActor.SetMaterial(0, FlaxEngine.Content.LoadAsyncInternal(EditorAssets.WiresDebugMaterial)); - // Link actors for rendering Task.AddCustomActor(_previewModel); - Task.AddCustomActor(_previewNodesActor); if (useWidgets) { @@ -121,6 +119,49 @@ namespace FlaxEditor.Viewport.Previews } } + /// + protected override void OnDebugDraw(GPUContext context, ref RenderContext renderContext) + { + base.OnDebugDraw(context, ref renderContext); + + // Draw skeleton nodes + if (_showNodes) + { + _previewModel.GetCurrentPose(out var pose); + var nodes = _previewModel.SkinnedModel?.Nodes; + if (pose != null && pose.Length != 0 && nodes != null) + { + // Draw bounding box at the node locations + var nodesMask = NodesMask != null && NodesMask.Length == nodes.Length ? NodesMask : null; + var localBox = new OrientedBoundingBox(new Vector3(-1.0f), new Vector3(1.0f)); + for (int nodeIndex = 0; nodeIndex < pose.Length; nodeIndex++) + { + if (nodesMask != null && !nodesMask[nodeIndex]) + continue; + var transform = pose[nodeIndex]; + transform.Decompose(out var scale, out Matrix _, out _); + transform = Matrix.Invert(Matrix.Scaling(scale)) * transform; + var box = localBox * transform; + DebugDraw.DrawWireBox(box, Color.Green, 0, false); + } + + // Nodes connections + for (int nodeIndex = 0; nodeIndex < nodes.Length; nodeIndex++) + { + int parentIndex = nodes[nodeIndex].ParentIndex; + if (parentIndex != -1) + { + if (nodesMask != null && (!nodesMask[nodeIndex] || !nodesMask[parentIndex])) + continue; + var parentPos = pose[parentIndex].TranslationVector; + var bonePos = pose[nodeIndex].TranslationVector; + DebugDraw.DrawLine(parentPos, bonePos, Color.Green, 0, false); + } + } + } + } + } + /// public override void Update(float deltaTime) { @@ -131,122 +172,6 @@ namespace FlaxEditor.Viewport.Previews { _previewModel.UpdateAnimation(); } - - // Update the nodes debug (once every few frames) - _previewNodesActor.Transform = _previewModel.Transform; - var updateNodesCount = PlayAnimation || _previewNodesVB?.Count == 0 ? 1 : 10; - _previewNodesActor.IsActive = ShowNodes; - if (_previewNodesCounter++ % updateNodesCount == 0 && ShowNodes) - { - _previewModel.GetCurrentPose(out var pose); - var nodes = _previewModel.SkinnedModel?.Nodes; - if (pose == null || pose.Length == 0 || nodes == null) - { - _previewNodesActor.IsActive = false; - } - else - { - if (_previewNodesVB == null) - _previewNodesVB = new List(1024 * 2); - else - _previewNodesVB.Clear(); - if (_previewNodesIB == null) - _previewNodesIB = new List(1024 * 3); - else - _previewNodesIB.Clear(); - - // Draw bounding box at the node locations - var nodesMask = NodesMask != null && NodesMask.Length == nodes.Length ? NodesMask : null; - var localBox = new OrientedBoundingBox(new Vector3(-1.0f), new Vector3(1.0f)); - for (int nodeIndex = 0; nodeIndex < pose.Length; nodeIndex++) - { - if (nodesMask != null && !nodesMask[nodeIndex]) - continue; - - var transform = pose[nodeIndex]; - transform.Decompose(out var scale, out Matrix _, out _); - transform = Matrix.Invert(Matrix.Scaling(scale)) * transform; - - // Some inlined code to improve performance - var box = localBox * transform; - // - var iStart = _previewNodesVB.Count; - box.GetCorners(_previewNodesVB); - // - _previewNodesIB.Add(iStart + 0); - _previewNodesIB.Add(iStart + 1); - _previewNodesIB.Add(iStart + 0); - // - _previewNodesIB.Add(iStart + 0); - _previewNodesIB.Add(iStart + 4); - _previewNodesIB.Add(iStart + 0); - // - _previewNodesIB.Add(iStart + 1); - _previewNodesIB.Add(iStart + 2); - _previewNodesIB.Add(iStart + 1); - // - _previewNodesIB.Add(iStart + 1); - _previewNodesIB.Add(iStart + 5); - _previewNodesIB.Add(iStart + 1); - // - _previewNodesIB.Add(iStart + 2); - _previewNodesIB.Add(iStart + 3); - _previewNodesIB.Add(iStart + 2); - // - _previewNodesIB.Add(iStart + 2); - _previewNodesIB.Add(iStart + 6); - _previewNodesIB.Add(iStart + 2); - // - _previewNodesIB.Add(iStart + 3); - _previewNodesIB.Add(iStart + 7); - _previewNodesIB.Add(iStart + 3); - // - _previewNodesIB.Add(iStart + 4); - _previewNodesIB.Add(iStart + 5); - _previewNodesIB.Add(iStart + 4); - // - _previewNodesIB.Add(iStart + 4); - _previewNodesIB.Add(iStart + 7); - _previewNodesIB.Add(iStart + 4); - // - _previewNodesIB.Add(iStart + 5); - _previewNodesIB.Add(iStart + 6); - _previewNodesIB.Add(iStart + 5); - // - _previewNodesIB.Add(iStart + 6); - _previewNodesIB.Add(iStart + 7); - _previewNodesIB.Add(iStart + 6); - // - } - - // Nodes connections - for (int nodeIndex = 0; nodeIndex < nodes.Length; nodeIndex++) - { - int parentIndex = nodes[nodeIndex].ParentIndex; - - if (parentIndex != -1) - { - if (nodesMask != null && (!nodesMask[nodeIndex] || !nodesMask[parentIndex])) - continue; - - var parentPos = pose[parentIndex].TranslationVector; - var bonePos = pose[nodeIndex].TranslationVector; - - var iStart = _previewNodesVB.Count; - _previewNodesVB.Add(parentPos); - _previewNodesVB.Add(bonePos); - _previewNodesIB.Add(iStart + 0); - _previewNodesIB.Add(iStart + 1); - _previewNodesIB.Add(iStart + 0); - } - } - - if (_previewNodesIB.Count > 0) - _previewNodesModel.LODs[0].Meshes[0].UpdateMesh(_previewNodesVB, _previewNodesIB); - else - _previewNodesActor.IsActive = false; - } - } } /// @@ -265,11 +190,7 @@ namespace FlaxEditor.Viewport.Previews /// public override void OnDestroy() { - // Ensure to cleanup created actor objects - _previewNodesActor.Model = null; Object.Destroy(ref _previewModel); - Object.Destroy(ref _previewNodesActor); - Object.Destroy(ref _previewNodesModel); NodesMask = null; base.OnDestroy(); From 1789e879bf97d5ea26bdd9355c4147f4281de24a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jun 2021 10:36:13 +0200 Subject: [PATCH 074/198] Add option to show bounds of the animated model in Editor preview --- .../Viewport/Previews/AnimatedModelPreview.cs | 32 ++++++++++++++++--- .../Editor/Viewport/Previews/AssetPreview.cs | 5 +++ Source/Engine/Level/Actors/AnimatedModel.cpp | 2 +- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs index 86ee3dc17..158ccc370 100644 --- a/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs +++ b/Source/Editor/Viewport/Previews/AnimatedModelPreview.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using FlaxEditor.GUI.ContextMenu; using FlaxEngine; using FlaxEditor.GUI.Input; using Object = FlaxEngine.Object; @@ -12,8 +13,9 @@ namespace FlaxEditor.Viewport.Previews /// public class AnimatedModelPreview : AssetPreview { + private ContextMenuButton _showBoundsButton; private AnimatedModel _previewModel; - private bool _showNodes; + private bool _showNodes, _showBounds; /// /// Gets or sets the skinned model asset to preview. @@ -44,10 +46,23 @@ namespace FlaxEditor.Viewport.Previews { _showNodes = value; if (value) - { ShowDebugDraw = true; - ShowEditorPrimitives = true; - } + } + } + + /// + /// Gets or sets a value indicating whether show animated model bounding box debug view. + /// + public bool ShowBounds + { + get => _showBounds; + set + { + _showBounds = value; + if (value) + ShowDebugDraw = true; + if (_showBoundsButton != null) + _showBoundsButton.Checked = value; } } @@ -84,6 +99,9 @@ namespace FlaxEditor.Viewport.Previews if (useWidgets) { + // Show Bounds + _showBoundsButton = ViewWidgetShowMenu.AddButton("Bounds", () => ShowBounds = !ShowBounds); + // Preview LOD { var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD"); @@ -160,6 +178,12 @@ namespace FlaxEditor.Viewport.Previews } } } + + // Draw bounds + if (_showBounds) + { + DebugDraw.DrawWireBox(_previewModel.Box, Color.Violet.RGBMultiplied(0.8f), 0, false); + } } /// diff --git a/Source/Editor/Viewport/Previews/AssetPreview.cs b/Source/Editor/Viewport/Previews/AssetPreview.cs index f6126480c..150990447 100644 --- a/Source/Editor/Viewport/Previews/AssetPreview.cs +++ b/Source/Editor/Viewport/Previews/AssetPreview.cs @@ -85,6 +85,11 @@ namespace FlaxEditor.Viewport.Previews view.Flags |= ViewFlags.DebugDraw; Task.View = view; } + if (value) + { + // Need to show editor primitives to show debug shapes + ShowEditorPrimitives = true; + } } } diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index a62aad592..df1650eac 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -617,7 +617,7 @@ void AnimatedModel::DrawGeneric(RenderContext& renderContext) void AnimatedModel::OnDebugDrawSelected() { - DEBUG_DRAW_WIRE_BOX(_box, Color::Violet * 0.8f, 0, true); + DEBUG_DRAW_WIRE_BOX(_box, Color::Violet.RGBMultiplied(0.8f), 0, true); // Base ModelInstanceActor::OnDebugDrawSelected(); From f701e35c5018155e40c9987816aac634297fda58 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jun 2021 10:45:23 +0200 Subject: [PATCH 075/198] Add option to show bounds of the model in Editor preview --- .../Editor/Viewport/Previews/ModelPreview.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Source/Editor/Viewport/Previews/ModelPreview.cs b/Source/Editor/Viewport/Previews/ModelPreview.cs index 3f285ddcd..81c0c350f 100644 --- a/Source/Editor/Viewport/Previews/ModelPreview.cs +++ b/Source/Editor/Viewport/Previews/ModelPreview.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using FlaxEditor.GUI.ContextMenu; using FlaxEditor.GUI.Input; using FlaxEngine; using Object = FlaxEngine.Object; @@ -12,7 +13,9 @@ namespace FlaxEditor.Viewport.Previews /// public class ModelPreview : AssetPreview { + private ContextMenuButton _showBoundsButton; private StaticModel _previewModel; + private bool _showBounds; /// /// Gets or sets the model asset to preview. @@ -28,6 +31,22 @@ namespace FlaxEditor.Viewport.Previews /// public StaticModel PreviewActor => _previewModel; + /// + /// Gets or sets a value indicating whether show animated model bounding box debug view. + /// + public bool ShowBounds + { + get => _showBounds; + set + { + _showBounds = value; + if (value) + ShowDebugDraw = true; + if (_showBoundsButton != null) + _showBoundsButton.Checked = value; + } + } + /// /// Gets or sets a value indicating whether scale the model to the normalized bounds. /// @@ -50,6 +69,9 @@ namespace FlaxEditor.Viewport.Previews if (useWidgets) { + // Show Bounds + _showBoundsButton = ViewWidgetShowMenu.AddButton("Bounds", () => ShowBounds = !ShowBounds); + // Preview LOD { var previewLOD = ViewWidgetButtonMenu.AddButton("Preview LOD"); @@ -85,6 +107,18 @@ namespace FlaxEditor.Viewport.Previews } } + /// + protected override void OnDebugDraw(GPUContext context, ref RenderContext renderContext) + { + base.OnDebugDraw(context, ref renderContext); + + // Draw bounds + if (_showBounds) + { + DebugDraw.DrawWireBox(_previewModel.Box, Color.Violet.RGBMultiplied(0.8f), 0, false); + } + } + /// public override bool OnKeyDown(KeyboardKeys key) { From 9618a57432154e14d808d6d8f83b02250b085898 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jun 2021 11:54:41 +0200 Subject: [PATCH 076/198] Add `BoundingBox.MakeScaled` --- Source/Engine/Core/Math/BoundingBox.cs | 29 ++++++++++++++++++ Source/Engine/Core/Math/BoundingBox.h | 41 +++++++++----------------- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/Source/Engine/Core/Math/BoundingBox.cs b/Source/Engine/Core/Math/BoundingBox.cs index 784f39930..6538f7995 100644 --- a/Source/Engine/Core/Math/BoundingBox.cs +++ b/Source/Engine/Core/Math/BoundingBox.cs @@ -469,6 +469,35 @@ namespace FlaxEngine */ } + /// + /// Creates the bounding box that is offseted by the given vector. Adds the offset value to minimum and maximum points. + /// + /// The box. + /// The bounds offset. + /// The offsetted bounds. + public static BoundingBox MakeOffsetted(ref BoundingBox box, ref Vector3 offset) + { + BoundingBox result; + Vector3.Add(ref box.Minimum, ref offset, out result.Minimum); + Vector3.Add(ref box.Maximum, ref offset, out result.Maximum); + return result; + } + + /// + /// Creates the bounding box that is scaled by the given factor. Applies scale to the size of the bounds. + /// + /// The box. + /// The bounds scale. + /// The scaled bounds. + public static BoundingBox MakeScaled(ref BoundingBox box, float scale) + { + Vector3.Subtract(ref box.Maximum, ref box.Minimum, out var size); + Vector3 sizeHalf = size * 0.5f; + Vector3 center = box.Minimum + sizeHalf; + sizeHalf = sizeHalf * scale; + return new BoundingBox(center - sizeHalf, center + sizeHalf); + } + /// /// Transforms bounding box using the given transformation matrix. /// diff --git a/Source/Engine/Core/Math/BoundingBox.h b/Source/Engine/Core/Math/BoundingBox.h index 8a349e022..92dd3bcc3 100644 --- a/Source/Engine/Core/Math/BoundingBox.h +++ b/Source/Engine/Core/Math/BoundingBox.h @@ -254,12 +254,7 @@ public: /// The points that will be contained by the box. /// The amount of points to use. /// The constructed bounding box. - static BoundingBox FromPoints(const Vector3* points, int32 pointsCount) - { - BoundingBox result; - FromPoints(points, pointsCount, result); - return result; - } + static BoundingBox FromPoints(const Vector3* points, int32 pointsCount); /// /// Constructs a Bounding Box from a given sphere. @@ -273,12 +268,7 @@ public: /// /// The sphere that will designate the extents of the box. /// The constructed bounding box. - static BoundingBox FromSphere(const BoundingSphere& sphere) - { - BoundingBox result; - FromSphere(sphere, result); - return result; - } + static BoundingBox FromSphere(const BoundingSphere& sphere); /// /// Constructs a Bounding Box that is as large as the total combined area of the two specified boxes. @@ -298,26 +288,23 @@ public: /// The box. /// The matrix. /// The result transformed box. - static BoundingBox Transform(const BoundingBox& box, const Matrix& matrix) - { - BoundingBox result; - Transform(box, matrix, result); - return result; - } + static BoundingBox Transform(const BoundingBox& box, const Matrix& matrix); /// /// Creates the bounding box that is offseted by the given vector. Adds the offset value to minimum and maximum points. /// /// The box. - /// The offset. - /// The result. - static BoundingBox MakeOffsetted(const BoundingBox& box, const Vector3& offset) - { - BoundingBox result; - result.Minimum = box.Minimum + offset; - result.Maximum = box.Maximum + offset; - return result; - } + /// The bounds offset. + /// The offsetted bounds. + static BoundingBox MakeOffsetted(const BoundingBox& box, const Vector3& offset); + + /// + /// Creates the bounding box that is scaled by the given factor. Applies scale to the size of the bounds. + /// + /// The box. + /// The bounds scale. + /// The scaled bounds. + static BoundingBox MakeScaled(const BoundingBox& box, float scale); /// /// Transforms the bounding box using the specified matrix. From 9b0b11f08f30fb1fa18baa0199f145d4e0a7fd9f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jun 2021 11:54:56 +0200 Subject: [PATCH 077/198] Missing change 9618a57432154e14d808d6d8f83b02250b085898 --- Source/Engine/Core/Math/BoundingBox.cpp | 39 +++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Source/Engine/Core/Math/BoundingBox.cpp b/Source/Engine/Core/Math/BoundingBox.cpp index 439b4341f..81ef146e6 100644 --- a/Source/Engine/Core/Math/BoundingBox.cpp +++ b/Source/Engine/Core/Math/BoundingBox.cpp @@ -26,6 +26,13 @@ void BoundingBox::FromPoints(const Vector3* points, int32 pointsCount, BoundingB result = BoundingBox(min, max); } +BoundingBox BoundingBox::FromPoints(const Vector3* points, int32 pointsCount) +{ + BoundingBox result; + FromPoints(points, pointsCount, result); + return result; +} + void BoundingBox::FromSphere(const BoundingSphere& sphere, BoundingBox& result) { result = BoundingBox( @@ -34,6 +41,38 @@ void BoundingBox::FromSphere(const BoundingSphere& sphere, BoundingBox& result) ); } +BoundingBox BoundingBox::FromSphere(const BoundingSphere& sphere) +{ + BoundingBox result; + FromSphere(sphere, result); + return result; +} + +BoundingBox BoundingBox::Transform(const BoundingBox& box, const Matrix& matrix) +{ + BoundingBox result; + Transform(box, matrix, result); + return result; +} + +BoundingBox BoundingBox::MakeOffsetted(const BoundingBox& box, const Vector3& offset) +{ + BoundingBox result; + result.Minimum = box.Minimum + offset; + result.Maximum = box.Maximum + offset; + return result; +} + +BoundingBox BoundingBox::MakeScaled(const BoundingBox& box, float scale) +{ + Vector3 size; + Vector3::Subtract(box.Maximum, box.Minimum, size); + Vector3 sizeHalf = size * 0.5f; + const Vector3 center = box.Minimum + sizeHalf; + sizeHalf = sizeHalf * scale; + return BoundingBox(center - sizeHalf, center + sizeHalf); +} + void BoundingBox::Transform(const BoundingBox& box, const Matrix& matrix, BoundingBox& result) { // Reference: http://dev.theomader.com/transform-bounding-boxes/ From 772172ed6b2dad452c9ecd2065c2d2070d9dba94 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jun 2021 11:55:35 +0200 Subject: [PATCH 078/198] Fix bounding box calculations for Animated Models --- Source/Engine/Graphics/Models/SkinnedMeshDrawData.cpp | 4 ++++ Source/Engine/Graphics/Models/SkinnedMeshDrawData.h | 4 +--- Source/Engine/Level/Actors/AnimatedModel.cpp | 4 +--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Engine/Graphics/Models/SkinnedMeshDrawData.cpp b/Source/Engine/Graphics/Models/SkinnedMeshDrawData.cpp index 25c29b11a..a99984a0c 100644 --- a/Source/Engine/Graphics/Models/SkinnedMeshDrawData.cpp +++ b/Source/Engine/Graphics/Models/SkinnedMeshDrawData.cpp @@ -6,6 +6,10 @@ #include "Engine/Core/Math/Matrix.h" #include "Engine/Core/Math/Matrix3x4.h" +SkinnedMeshDrawData::SkinnedMeshDrawData() +{ +} + SkinnedMeshDrawData::~SkinnedMeshDrawData() { SAFE_DELETE_GPU_RESOURCE(BoneMatrices); diff --git a/Source/Engine/Graphics/Models/SkinnedMeshDrawData.h b/Source/Engine/Graphics/Models/SkinnedMeshDrawData.h index b5d86a34d..f9d434fee 100644 --- a/Source/Engine/Graphics/Models/SkinnedMeshDrawData.h +++ b/Source/Engine/Graphics/Models/SkinnedMeshDrawData.h @@ -42,9 +42,7 @@ public: /// /// Initializes a new instance of the class. /// - SkinnedMeshDrawData() - { - } + SkinnedMeshDrawData(); /// /// Finalizes an instance of the class. diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index df1650eac..7953067e3 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -439,8 +439,7 @@ void AnimatedModel::UpdateLocalBounds() } // Scale bounds - box.Minimum *= BoundsScale; - box.Maximum *= BoundsScale; + box = box.MakeScaled(box, BoundsScale); _boxLocal = box; } @@ -502,7 +501,6 @@ void AnimatedModel::OnSkinnedModelLoaded() { Entries.SetupIfInvalid(SkinnedModel); - UpdateBounds(); GraphInstance.Invalidate(); if (_blendShapes.Weights.HasItems()) _blendShapes.WeightsDirty = true; From c5405183c9aaa1d84a6b62733100058c578394f4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jun 2021 12:14:04 +0200 Subject: [PATCH 079/198] Fix tooltips to be constrained by monitor bounds instead of virtual desktop or parent window bounds #470 #540 --- Source/Engine/UI/GUI/Tooltip.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Source/Engine/UI/GUI/Tooltip.cs b/Source/Engine/UI/GUI/Tooltip.cs index 4541124e7..e6690c82d 100644 --- a/Source/Engine/UI/GUI/Tooltip.cs +++ b/Source/Engine/UI/GUI/Tooltip.cs @@ -72,19 +72,17 @@ namespace FlaxEngine.GUI Vector2 dpiSize = Size * dpiScale; Vector2 locationWS = target.PointToWindow(location); Vector2 locationSS = parentWin.PointToScreen(locationWS); - Vector2 screenSize = Platform.VirtualDesktopSize; - Vector2 parentWinLocationSS = parentWin.PointToScreen(Vector2.Zero); - float parentWinRightSS = parentWinLocationSS.Y + parentWin.Size.Y; - float parentWinBottomSS = parentWinLocationSS.X + parentWin.Size.X; + Rectangle monitorBounds = Platform.GetMonitorBounds(locationSS); + Vector2 rightBottomMonitorBounds = monitorBounds.BottomRight; Vector2 rightBottomLocationSS = locationSS + dpiSize; // Prioritize tooltip placement within parent window, fall back to virtual desktop - if (parentWinRightSS < rightBottomLocationSS.Y || screenSize.Y < rightBottomLocationSS.Y) + if (rightBottomMonitorBounds.Y < rightBottomLocationSS.Y) { // Direction: up locationSS.Y -= dpiSize.Y; } - if (parentWinBottomSS < rightBottomLocationSS.X || screenSize.X < rightBottomLocationSS.X) + if (rightBottomMonitorBounds.X < rightBottomLocationSS.X) { // Direction: left locationSS.X -= dpiSize.X; From accbef68412a5bc880b1553e215b2e4fe94c993d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jun 2021 13:29:42 +0200 Subject: [PATCH 080/198] Fix missing `mipIndex` when clearing lightmap textures with a buffer on baking start --- Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp index 305cc71b8..867c5b3f9 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp @@ -70,7 +70,7 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context) { uint32 rowPitch, slicePitch; texture->ComputePitch(mipIndex, rowPitch, slicePitch); - context->UpdateTexture(textures[textureIndex], 0, 0, cleaner, rowPitch, slicePitch); + context->UpdateTexture(textures[textureIndex], 0, mipIndex, cleaner, rowPitch, slicePitch); } } } From c30e338f0ab02dd562510f710dbf13ce9b38a256 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jun 2021 14:38:24 +0200 Subject: [PATCH 081/198] Fix gizmos debug draw shapes --- Source/Editor/Viewport/MainEditorGizmoViewport.cs | 12 ++++++------ Source/Editor/Viewport/PrefabWindowViewport.cs | 4 ++-- Source/Engine/Level/Actors/PointLight.cpp | 7 +++++-- Source/Engine/Level/Actors/SpotLight.cpp | 7 +++++-- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/Source/Editor/Viewport/MainEditorGizmoViewport.cs b/Source/Editor/Viewport/MainEditorGizmoViewport.cs index 68667b16f..0c759c950 100644 --- a/Source/Editor/Viewport/MainEditorGizmoViewport.cs +++ b/Source/Editor/Viewport/MainEditorGizmoViewport.cs @@ -450,6 +450,12 @@ namespace FlaxEditor.Viewport /// public void DrawEditorPrimitives(GPUContext context, ref RenderContext renderContext, GPUTexture target, GPUTexture targetDepth) { + // Draw gizmos + for (int i = 0; i < Gizmos.Count; i++) + { + Gizmos[i].Draw(ref renderContext); + } + // Draw selected objects debug shapes and visuals if (DrawDebugDraw && (renderContext.View.Flags & ViewFlags.DebugDraw) == ViewFlags.DebugDraw) { @@ -463,12 +469,6 @@ namespace FlaxEditor.Viewport DebugDraw.Draw(ref renderContext, target.View(), targetDepth.View(), true); } - - // Draw gizmos - for (int i = 0; i < Gizmos.Count; i++) - { - Gizmos[i].Draw(ref renderContext); - } } private void OnPostRender(GPUContext context, RenderContext renderContext) diff --git a/Source/Editor/Viewport/PrefabWindowViewport.cs b/Source/Editor/Viewport/PrefabWindowViewport.cs index df3057203..4e71677b2 100644 --- a/Source/Editor/Viewport/PrefabWindowViewport.cs +++ b/Source/Editor/Viewport/PrefabWindowViewport.cs @@ -865,13 +865,13 @@ namespace FlaxEditor.Viewport /// public override void DrawEditorPrimitives(GPUContext context, ref RenderContext renderContext, GPUTexture target, GPUTexture targetDepth) { - base.DrawEditorPrimitives(context, ref renderContext, target, targetDepth); - // Draw gizmos for (int i = 0; i < Gizmos.Count; i++) { Gizmos[i].Draw(ref renderContext); } + + base.DrawEditorPrimitives(context, ref renderContext, target, targetDepth); } /// diff --git a/Source/Engine/Level/Actors/PointLight.cpp b/Source/Engine/Level/Actors/PointLight.cpp index 5f9d4bdb3..9341002c1 100644 --- a/Source/Engine/Level/Actors/PointLight.cpp +++ b/Source/Engine/Level/Actors/PointLight.cpp @@ -136,8 +136,11 @@ void PointLight::Draw(RenderContext& renderContext) void PointLight::OnDebugDraw() { - // Draw source tube - DEBUG_DRAW_WIRE_TUBE(GetPosition(), GetOrientation(), SourceRadius, SourceLength, Color::Orange, 0, true); + if (SourceRadius > ZeroTolerance || SourceLength > ZeroTolerance) + { + // Draw source tube + DEBUG_DRAW_WIRE_TUBE(GetPosition(), GetOrientation(), SourceRadius, SourceLength, Color::Orange, 0, true); + } // Base LightWithShadow::OnDebugDraw(); diff --git a/Source/Engine/Level/Actors/SpotLight.cpp b/Source/Engine/Level/Actors/SpotLight.cpp index c601506c4..ec25ffb5b 100644 --- a/Source/Engine/Level/Actors/SpotLight.cpp +++ b/Source/Engine/Level/Actors/SpotLight.cpp @@ -184,8 +184,11 @@ void SpotLight::Draw(RenderContext& renderContext) void SpotLight::OnDebugDraw() { - // Draw source tube - DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(GetPosition(), SourceRadius), Color::Orange, 0, true); + if (SourceRadius > ZeroTolerance) + { + // Draw source tube + DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(GetPosition(), SourceRadius), Color::Orange, 0, true); + } // Base LightWithShadow::OnDebugDraw(); From bcca8c92919c37e484174bc8d923510feb474eb6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jun 2021 14:57:32 +0200 Subject: [PATCH 082/198] Add printing full object path for some warning logs --- Source/Engine/Audio/AudioSource.cpp | 4 ++-- Source/Engine/ShadowsOfMordor/Builder.Entries.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index 8969bc4d3..a6370cc8b 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -104,7 +104,7 @@ void AudioSource::Play() return; if (Clip == nullptr) { - LOG(Warning, "Cannot play audio source without a clip ({0})", GetName()); + LOG(Warning, "Cannot play audio source without a clip ({0})", GetNamePath()); return; } @@ -133,7 +133,7 @@ void AudioSource::Pause() { if (_state != States::Playing) { - LOG(Warning, "Cannot pause audio source that is not playing ({0})", GetName()); + LOG(Warning, "Cannot pause audio source that is not playing ({0})", GetNamePath()); return; } diff --git a/Source/Engine/ShadowsOfMordor/Builder.Entries.cpp b/Source/Engine/ShadowsOfMordor/Builder.Entries.cpp index aa66c955c..b14937338 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Entries.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Entries.cpp @@ -63,7 +63,7 @@ bool cacheStaticGeometryTree(Actor* actor, ShadowsOfMordor::Builder::SceneBuildC model->GetPath(), meshIndex, lodIndex, - staticModel->GetName()); + staticModel->GetNamePath()); } } } From 0f01683c06d3ee3d32863e9bd9c8ca1461212c95 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jun 2021 15:49:02 +0200 Subject: [PATCH 083/198] Fix build settings limits --- Source/Engine/Core/Config/BuildSettings.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Core/Config/BuildSettings.h b/Source/Engine/Core/Config/BuildSettings.h index 529171639..211bad866 100644 --- a/Source/Engine/Core/Config/BuildSettings.h +++ b/Source/Engine/Core/Config/BuildSettings.h @@ -18,19 +18,19 @@ public: /// /// The maximum amount of assets to include into a single assets package. Asset packages will split into several packages if need to. /// - API_FIELD(Attributes="EditorOrder(10), DefaultValue(4096), Limit(1, 32, ushort.MaxValue), EditorDisplay(\"General\", \"Max assets per package\")") + API_FIELD(Attributes="EditorOrder(10), DefaultValue(4096), Limit(1, ushort.MaxValue), EditorDisplay(\"General\", \"Max assets per package\")") int32 MaxAssetsPerPackage = 4096; /// /// The maximum size of the single assets package (in megabytes). Asset packages will split into several packages if need to. /// - API_FIELD(Attributes="EditorOrder(20), DefaultValue(1024), Limit(1, 16, ushort.MaxValue), EditorDisplay(\"General\", \"Max package size (in MB)\")") + API_FIELD(Attributes="EditorOrder(20), DefaultValue(1024), Limit(1, ushort.MaxValue), EditorDisplay(\"General\", \"Max package size (in MB)\")") int32 MaxPackageSizeMB = 1024; /// /// The game content cooking keycode. Use the same value for a game and DLC packages to support loading them by the build game. Use 0 to randomize it during building. /// - API_FIELD(Attributes="EditorOrder(30), DefaultValue(0), Limit(1, 16, ushort.MaxValue), EditorDisplay(\"General\")") + API_FIELD(Attributes="EditorOrder(30), DefaultValue(0), EditorDisplay(\"General\")") int32 ContentKey = 0; /// From 0aa9c865cac07e34642259add4c92086185cab19 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jun 2021 21:10:57 +0200 Subject: [PATCH 084/198] Fix renaming selected actor in scene tree after creation and use editor shortcut --- Source/Editor/SceneGraph/GUI/ActorTreeNode.cs | 15 --------------- .../Windows/Assets/PrefabWindow.Hierarchy.cs | 8 +++++++- Source/Editor/Windows/Assets/PrefabWindow.cs | 1 + Source/Editor/Windows/SceneTreeWindow.cs | 9 ++++++++- 4 files changed, 16 insertions(+), 17 deletions(-) diff --git a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs index 9dc0b1719..cebbfc544 100644 --- a/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs +++ b/Source/Editor/SceneGraph/GUI/ActorTreeNode.cs @@ -293,21 +293,6 @@ namespace FlaxEditor.SceneGraph.GUI } } - /// - public override bool OnKeyDown(KeyboardKeys key) - { - if (IsFocused) - { - if (key == KeyboardKeys.F2) - { - StartRenaming(); - return true; - } - } - - return base.OnKeyDown(key); - } - /// protected override DragDropEffect OnDragEnterHeader(DragData data) { diff --git a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs index afba74fbc..a37d88b61 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.Hierarchy.cs @@ -163,7 +163,13 @@ namespace FlaxEditor.Windows.Assets private void Rename() { - ((ActorNode)Selection[0]).TreeNode.StartRenaming(); + var selection = Selection; + if (selection.Count != 0 && selection[0] is ActorNode actor) + { + if (selection.Count != 0) + Select(actor); + actor.TreeNode.StartRenaming(); + } } /// diff --git a/Source/Editor/Windows/Assets/PrefabWindow.cs b/Source/Editor/Windows/Assets/PrefabWindow.cs index 85a9a2cb9..3820e762c 100644 --- a/Source/Editor/Windows/Assets/PrefabWindow.cs +++ b/Source/Editor/Windows/Assets/PrefabWindow.cs @@ -184,6 +184,7 @@ namespace FlaxEditor.Windows.Assets InputActions.Add(options => options.Paste, Paste); InputActions.Add(options => options.Duplicate, Duplicate); InputActions.Add(options => options.Delete, Delete); + InputActions.Add(options => options.Rename, Rename); InputActions.Add(options => options.FocusSelection, _viewport.FocusSelection); } diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index afe6d977e..e930ff053 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -69,6 +69,7 @@ namespace FlaxEditor.Windows InputActions.Add(options => options.RotateMode, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate); InputActions.Add(options => options.ScaleMode, () => Editor.MainTransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale); InputActions.Add(options => options.FocusSelection, () => Editor.Windows.EditWin.Viewport.FocusSelection()); + InputActions.Add(options => options.Rename, Rename); } private void OnSearchBoxTextChanged() @@ -91,7 +92,13 @@ namespace FlaxEditor.Windows private void Rename() { - (Editor.SceneEditing.Selection[0] as ActorNode).TreeNode.StartRenaming(); + var selection = Editor.SceneEditing.Selection; + if (selection.Count != 0 && selection[0] is ActorNode actor) + { + if (selection.Count != 0) + Editor.SceneEditing.Select(actor); + actor.TreeNode.StartRenaming(); + } } private void Spawn(Type type) From d111b2ba03f9184e09e6e6b4ae095c933f5216cb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 1 Jun 2021 21:20:00 +0200 Subject: [PATCH 085/198] Tweaks --- .../Editor/Windows/SceneTreeWindow.Actors.cs | 118 ------------------ Source/Editor/Windows/SceneTreeWindow.cs | 107 ++++++++++++++++ Source/Engine/Audio/Audio.cpp | 6 +- 3 files changed, 110 insertions(+), 121 deletions(-) delete mode 100644 Source/Editor/Windows/SceneTreeWindow.Actors.cs diff --git a/Source/Editor/Windows/SceneTreeWindow.Actors.cs b/Source/Editor/Windows/SceneTreeWindow.Actors.cs deleted file mode 100644 index 04d5a3ced..000000000 --- a/Source/Editor/Windows/SceneTreeWindow.Actors.cs +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -using System; -using System.Collections.Generic; -using FlaxEngine; - -namespace FlaxEditor.Windows -{ - public partial class SceneTreeWindow - { - /// - /// The spawnable actors group. - /// - public struct ActorsGroup - { - /// - /// The group name. - /// - public string Name; - - /// - /// The types to spawn (name and type). - /// - public KeyValuePair[] Types; - } - - /// - /// The Spawnable actors (groups with single entry are inlined without a child menu) - /// - public static readonly ActorsGroup[] SpawnActorsGroups = - { - new ActorsGroup - { - Types = new[] { new KeyValuePair("Actor", typeof(EmptyActor)) } - }, - new ActorsGroup - { - Types = new[] { new KeyValuePair("Model", typeof(StaticModel)) } - }, - new ActorsGroup - { - Types = new[] { new KeyValuePair("Camera", typeof(Camera)) } - }, - new ActorsGroup - { - Name = "Lights", - Types = new[] - { - new KeyValuePair("Directional Light", typeof(DirectionalLight)), - new KeyValuePair("Point Light", typeof(PointLight)), - new KeyValuePair("Spot Light", typeof(SpotLight)), - new KeyValuePair("Sky Light", typeof(SkyLight)), - } - }, - new ActorsGroup - { - Name = "Visuals", - Types = new[] - { - new KeyValuePair("Environment Probe", typeof(EnvironmentProbe)), - new KeyValuePair("Sky", typeof(Sky)), - new KeyValuePair("Skybox", typeof(Skybox)), - new KeyValuePair("Exponential Height Fog", typeof(ExponentialHeightFog)), - new KeyValuePair("PostFx Volume", typeof(PostFxVolume)), - new KeyValuePair("Decal", typeof(Decal)), - new KeyValuePair("Particle Effect", typeof(ParticleEffect)), - } - }, - new ActorsGroup - { - Name = "Physics", - Types = new[] - { - new KeyValuePair("Rigid Body", typeof(RigidBody)), - new KeyValuePair("Character Controller", typeof(CharacterController)), - new KeyValuePair("Box Collider", typeof(BoxCollider)), - new KeyValuePair("Sphere Collider", typeof(SphereCollider)), - new KeyValuePair("Capsule Collider", typeof(CapsuleCollider)), - new KeyValuePair("Mesh Collider", typeof(MeshCollider)), - new KeyValuePair("Fixed Joint", typeof(FixedJoint)), - new KeyValuePair("Distance Joint", typeof(DistanceJoint)), - new KeyValuePair("Slider Joint", typeof(SliderJoint)), - new KeyValuePair("Spherical Joint", typeof(SphericalJoint)), - new KeyValuePair("Hinge Joint", typeof(HingeJoint)), - new KeyValuePair("D6 Joint", typeof(D6Joint)), - } - }, - new ActorsGroup - { - Name = "Other", - Types = new[] - { - new KeyValuePair("Animated Model", typeof(AnimatedModel)), - new KeyValuePair("Bone Socket", typeof(BoneSocket)), - new KeyValuePair("CSG Box Brush", typeof(BoxBrush)), - new KeyValuePair("Audio Source", typeof(AudioSource)), - new KeyValuePair("Audio Listener", typeof(AudioListener)), - new KeyValuePair("Scene Animation", typeof(SceneAnimationPlayer)), - new KeyValuePair("Nav Mesh Bounds Volume", typeof(NavMeshBoundsVolume)), - new KeyValuePair("Nav Link", typeof(NavLink)), - new KeyValuePair("Nav Modifier Volume", typeof(NavModifierVolume)), - new KeyValuePair("Spline", typeof(Spline)), - } - }, - new ActorsGroup - { - Name = "GUI", - Types = new[] - { - new KeyValuePair("UI Control", typeof(UIControl)), - new KeyValuePair("UI Canvas", typeof(UICanvas)), - new KeyValuePair("Text Render", typeof(TextRender)), - new KeyValuePair("Sprite Render", typeof(SpriteRender)), - } - }, - }; - } -} diff --git a/Source/Editor/Windows/SceneTreeWindow.cs b/Source/Editor/Windows/SceneTreeWindow.cs index e930ff053..abe67190e 100644 --- a/Source/Editor/Windows/SceneTreeWindow.cs +++ b/Source/Editor/Windows/SceneTreeWindow.cs @@ -18,6 +18,113 @@ namespace FlaxEditor.Windows /// public partial class SceneTreeWindow : SceneEditorWindow { + /// + /// The spawnable actors group. + /// + public struct ActorsGroup + { + /// + /// The group name. + /// + public string Name; + + /// + /// The types to spawn (name and type). + /// + public KeyValuePair[] Types; + } + + /// + /// The Spawnable actors (groups with single entry are inlined without a child menu) + /// + public static readonly ActorsGroup[] SpawnActorsGroups = + { + new ActorsGroup + { + Types = new[] { new KeyValuePair("Actor", typeof(EmptyActor)) } + }, + new ActorsGroup + { + Types = new[] { new KeyValuePair("Model", typeof(StaticModel)) } + }, + new ActorsGroup + { + Types = new[] { new KeyValuePair("Camera", typeof(Camera)) } + }, + new ActorsGroup + { + Name = "Lights", + Types = new[] + { + new KeyValuePair("Directional Light", typeof(DirectionalLight)), + new KeyValuePair("Point Light", typeof(PointLight)), + new KeyValuePair("Spot Light", typeof(SpotLight)), + new KeyValuePair("Sky Light", typeof(SkyLight)), + } + }, + new ActorsGroup + { + Name = "Visuals", + Types = new[] + { + new KeyValuePair("Environment Probe", typeof(EnvironmentProbe)), + new KeyValuePair("Sky", typeof(Sky)), + new KeyValuePair("Skybox", typeof(Skybox)), + new KeyValuePair("Exponential Height Fog", typeof(ExponentialHeightFog)), + new KeyValuePair("PostFx Volume", typeof(PostFxVolume)), + new KeyValuePair("Decal", typeof(Decal)), + new KeyValuePair("Particle Effect", typeof(ParticleEffect)), + } + }, + new ActorsGroup + { + Name = "Physics", + Types = new[] + { + new KeyValuePair("Rigid Body", typeof(RigidBody)), + new KeyValuePair("Character Controller", typeof(CharacterController)), + new KeyValuePair("Box Collider", typeof(BoxCollider)), + new KeyValuePair("Sphere Collider", typeof(SphereCollider)), + new KeyValuePair("Capsule Collider", typeof(CapsuleCollider)), + new KeyValuePair("Mesh Collider", typeof(MeshCollider)), + new KeyValuePair("Fixed Joint", typeof(FixedJoint)), + new KeyValuePair("Distance Joint", typeof(DistanceJoint)), + new KeyValuePair("Slider Joint", typeof(SliderJoint)), + new KeyValuePair("Spherical Joint", typeof(SphericalJoint)), + new KeyValuePair("Hinge Joint", typeof(HingeJoint)), + new KeyValuePair("D6 Joint", typeof(D6Joint)), + } + }, + new ActorsGroup + { + Name = "Other", + Types = new[] + { + new KeyValuePair("Animated Model", typeof(AnimatedModel)), + new KeyValuePair("Bone Socket", typeof(BoneSocket)), + new KeyValuePair("CSG Box Brush", typeof(BoxBrush)), + new KeyValuePair("Audio Source", typeof(AudioSource)), + new KeyValuePair("Audio Listener", typeof(AudioListener)), + new KeyValuePair("Scene Animation", typeof(SceneAnimationPlayer)), + new KeyValuePair("Nav Mesh Bounds Volume", typeof(NavMeshBoundsVolume)), + new KeyValuePair("Nav Link", typeof(NavLink)), + new KeyValuePair("Nav Modifier Volume", typeof(NavModifierVolume)), + new KeyValuePair("Spline", typeof(Spline)), + } + }, + new ActorsGroup + { + Name = "GUI", + Types = new[] + { + new KeyValuePair("UI Control", typeof(UIControl)), + new KeyValuePair("UI Canvas", typeof(UICanvas)), + new KeyValuePair("Text Render", typeof(TextRender)), + new KeyValuePair("Sprite Render", typeof(SpriteRender)), + } + }, + }; + private TextBox _searchBox; private Tree _tree; private bool _isUpdatingSelection; diff --git a/Source/Engine/Audio/Audio.cpp b/Source/Engine/Audio/Audio.cpp index 51f674e73..913ffe87a 100644 --- a/Source/Engine/Audio/Audio.cpp +++ b/Source/Engine/Audio/Audio.cpp @@ -171,6 +171,7 @@ void Audio::OnRemoveSource(AudioSource* source) bool AudioService::Init() { + PROFILE_CPU_NAMED("Audio.Init"); const auto settings = AudioSettings::Get(); const bool mute = CommandLine::Options.Mute.IsTrue() || settings->DisableAudio; @@ -233,7 +234,7 @@ bool AudioService::Init() void AudioService::Update() { - PROFILE_CPU(); + PROFILE_CPU_NAMED("Audio.Update"); // Update the master volume float masterVolume = MasterVolume; @@ -242,10 +243,9 @@ void AudioService::Update() // Mute audio if app has no user focus masterVolume = 0.0f; } - if (Volume != masterVolume) + if (Math::NotNearEqual(Volume, masterVolume)) { Volume = masterVolume; - AudioBackend::SetVolume(masterVolume); } From 398fc4be37b3f5993571037ef86789f448546ab3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 2 Jun 2021 09:47:37 +0200 Subject: [PATCH 086/198] Add `Keyboard.IsAnyKeyDown` --- Source/Engine/Input/Input.cpp | 21 ++++++++++++++++++ Source/Engine/Input/Keyboard.h | 39 +++++++--------------------------- 2 files changed, 29 insertions(+), 31 deletions(-) diff --git a/Source/Engine/Input/Input.cpp b/Source/Engine/Input/Input.cpp index d7796691b..44131504d 100644 --- a/Source/Engine/Input/Input.cpp +++ b/Source/Engine/Input/Input.cpp @@ -223,6 +223,27 @@ bool Mouse::Update(EventQueue& queue) return false; } +void Keyboard::State::Clear() +{ + Platform::MemoryClear(this, sizeof(State)); +} + +Keyboard::Keyboard() + : InputDevice(SpawnParams(Guid::New(), TypeInitializer), TEXT("Keyboard")) +{ + _state.Clear(); + _prevState.Clear(); +} + +bool Keyboard::IsAnyKeyDown() const +{ + // TODO: optimize with SIMD + bool result = false; + for (auto e : _state.Keys) + result |= e; + return result; +} + void Keyboard::OnCharInput(Char c, Window* target) { // Skip control characters diff --git a/Source/Engine/Input/Keyboard.h b/Source/Engine/Input/Keyboard.h index f182f9731..7877b3f13 100644 --- a/Source/Engine/Input/Keyboard.h +++ b/Source/Engine/Input/Keyboard.h @@ -10,48 +10,20 @@ API_CLASS(NoSpawn) class FLAXENGINE_API Keyboard : public InputDevice { DECLARE_SCRIPTING_TYPE_NO_SPAWN(Keyboard); -public: +protected: - /// - /// The keyboard state. - /// struct State { - /// - /// The input text length (characters count). - /// uint16 InputTextLength; - - /// - /// The input text. - /// Char InputText[32]; - - /// - /// The keys. - /// bool Keys[(int32)KeyboardKeys::MAX]; - - /// - /// Clears the state. - /// - void Clear() - { - Platform::MemoryClear(this, sizeof(State)); - } + void Clear(); }; -protected: - State _state; State _prevState; - explicit Keyboard() - : InputDevice(SpawnParams(Guid::New(), TypeInitializer), TEXT("Keyboard")) - { - _state.Clear(); - _prevState.Clear(); - } + explicit Keyboard(); public: @@ -94,6 +66,11 @@ public: return !_state.Keys[static_cast(key)] && _prevState.Keys[static_cast(key)]; } + /// + /// Checks if any keyboard key is currently pressed. + /// + API_PROPERTY() bool IsAnyKeyDown() const; + public: /// From 33b31f91556ecd917d91b2d4c81b404139d145a4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 2 Jun 2021 09:48:02 +0200 Subject: [PATCH 087/198] Fix using Keyboard and Mouse in C# scripting --- Source/Engine/Platform/Android/AndroidPlatform.cpp | 8 ++++---- Source/Engine/Platform/Linux/LinuxPlatform.cpp | 8 ++++---- Source/Engine/Platform/UWP/UWPPlatform.cpp | 8 ++++---- Source/Engine/Platform/UWP/UWPWindow.cpp | 4 ++-- Source/Engine/Platform/Windows/WindowsInput.cpp | 12 ++++++------ 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Source/Engine/Platform/Android/AndroidPlatform.cpp b/Source/Engine/Platform/Android/AndroidPlatform.cpp index 786df6654..5b9d02424 100644 --- a/Source/Engine/Platform/Android/AndroidPlatform.cpp +++ b/Source/Engine/Platform/Android/AndroidPlatform.cpp @@ -260,7 +260,7 @@ namespace String AppPackageName, DeviceManufacturer, DeviceModel, DeviceBuildNumber; String SystemVersion, SystemLanguage, CacheDir, ExecutablePath; byte MacAddress[6]; - AndroidKeyboard KeyboardImpl; + AndroidKeyboard* KeyboardImpl; AndroidDeviceGamepad* GamepadImpl; AndroidTouchScreen* TouchScreenImpl; ScreenOrientationType Orientation; @@ -496,9 +496,9 @@ namespace if (eventType.KeyboardKey != KeyboardKeys::None) { if (isDown) - KeyboardImpl.OnKeyDown(eventType.KeyboardKey); + KeyboardImpl->OnKeyDown(eventType.KeyboardKey); else - KeyboardImpl.OnKeyUp(eventType.KeyboardKey); + KeyboardImpl->OnKeyUp(eventType.KeyboardKey); } // Gamepad @@ -850,7 +850,7 @@ bool AndroidPlatform::Init() } // Setup native platform input devices - Input::Keyboard = &KeyboardImpl; + Input::Keyboard = KeyboardImpl = New(); Input::Gamepads.Add(GamepadImpl = New()); Input::OnGamepadsChanged(); Input::CustomDevices.Add(TouchScreenImpl = New()); diff --git a/Source/Engine/Platform/Linux/LinuxPlatform.cpp b/Source/Engine/Platform/Linux/LinuxPlatform.cpp index 90bd1784f..74d39df7e 100644 --- a/Source/Engine/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Engine/Platform/Linux/LinuxPlatform.cpp @@ -1195,8 +1195,8 @@ struct Property namespace Impl { - LinuxKeyboard Keyboard; - LinuxMouse Mouse; + LinuxKeyboard* Keyboard; + LinuxMouse* Mouse; StringAnsi ClipboardText; void ClipboardGetText(String& result, X11::Atom source, X11::Atom atom, X11::Window window) @@ -2193,8 +2193,8 @@ bool LinuxPlatform::Init() } } - Input::Mouse = &Impl::Mouse; - Input::Keyboard = &Impl::Keyboard; + Input::Mouse = Impl::Mouse = New(); + Input::Keyboard = Impl::Keyboard = New(); return false; } diff --git a/Source/Engine/Platform/UWP/UWPPlatform.cpp b/Source/Engine/Platform/UWP/UWPPlatform.cpp index 522d5db15..b0fdf8f21 100644 --- a/Source/Engine/Platform/UWP/UWPPlatform.cpp +++ b/Source/Engine/Platform/UWP/UWPPlatform.cpp @@ -22,8 +22,8 @@ UWPPlatformImpl* CUWPPlatform = nullptr; namespace Impl { - extern UWPWindow::UWPKeyboard Keyboard; - extern UWPWindow::UWPMouse Mouse; + extern UWPWindow::UWPKeyboard* Keyboard; + extern UWPWindow::UWPMouse* Mouse; extern UWPWindow* Window; } @@ -62,8 +62,8 @@ bool UWPPlatform::Init() UserName = String::Empty; SystemDpi = CUWPPlatform->GetDpi(); - Input::Mouse = &Impl::Mouse; - Input::Keyboard = &Impl::Keyboard; + Input::Mouse = Impl::Mouse = New(); + Input::Keyboard = Impl::Keyboard = New(); return false; } diff --git a/Source/Engine/Platform/UWP/UWPWindow.cpp b/Source/Engine/Platform/UWP/UWPWindow.cpp index ba509b089..a55f3b5d2 100644 --- a/Source/Engine/Platform/UWP/UWPWindow.cpp +++ b/Source/Engine/Platform/UWP/UWPWindow.cpp @@ -11,8 +11,8 @@ namespace Impl { - UWPWindow::UWPKeyboard Keyboard; - UWPWindow::UWPMouse Mouse; + UWPWindow::UWPKeyboard* Keyboard = nullptr; + UWPWindow::UWPMouse* Mouse = nullptr; UWPWindow* Window = nullptr; } diff --git a/Source/Engine/Platform/Windows/WindowsInput.cpp b/Source/Engine/Platform/Windows/WindowsInput.cpp index 177eb067c..2838e2286 100644 --- a/Source/Engine/Platform/Windows/WindowsInput.cpp +++ b/Source/Engine/Platform/Windows/WindowsInput.cpp @@ -105,14 +105,14 @@ namespace WindowsInputImpl { float XInputLastUpdateTime = 0; bool XInputGamepads[XUSER_MAX_COUNT] = { false }; - WindowsMouse Mouse; - WindowsKeyboard Keyboard; + WindowsMouse* Mouse = nullptr; + WindowsKeyboard* Keyboard = nullptr; } void WindowsInput::Init() { - Input::Mouse = &WindowsInputImpl::Mouse; - Input::Keyboard = &WindowsInputImpl::Keyboard; + Input::Mouse = WindowsInputImpl::Mouse = New(); + Input::Keyboard = WindowsInputImpl::Keyboard = New(); } void WindowsInput::Update() @@ -142,9 +142,9 @@ void WindowsInput::Update() bool WindowsInput::WndProc(Window* window, Windows::UINT msg, Windows::WPARAM wParam, Windows::LPARAM lParam) { - if (WindowsInputImpl::Mouse.WndProc(window, msg, wParam, lParam)) + if (WindowsInputImpl::Mouse->WndProc(window, msg, wParam, lParam)) return true; - if (WindowsInputImpl::Keyboard.WndProc(window, msg, wParam, lParam)) + if (WindowsInputImpl::Keyboard->WndProc(window, msg, wParam, lParam)) return true; return false; } From 46e7e6173aaf3aa830f771d4620551695bb57c2a Mon Sep 17 00:00:00 2001 From: Crawcik Date: Wed, 2 Jun 2021 18:42:28 +0200 Subject: [PATCH 088/198] Rebuilding parrent collections layout --- .../CustomEditors/Editors/CollectionEditor.cs | 19 +++++++++++++ .../CustomEditors/Editors/DictionaryEditor.cs | 27 ++++++++++++++++--- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index 141330aea..f7c6c1e06 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -229,6 +229,24 @@ namespace FlaxEditor.CustomEditors.Editors } } + /// + /// Rebuilds the parent layout if its collection. + /// + public void RebuildParentCollection() + { + if (ParentEditor is DictionaryEditor dictionaryEditor) + { + dictionaryEditor.RebuildParentCollection(); + dictionaryEditor.RebuildLayout(); + return; + } + if (ParentEditor is CollectionEditor collectionEditor) + { + collectionEditor.RebuildParentCollection(); + collectionEditor.RebuildLayout(); + } + } + private void OnSizeChanged() { if (IsSetBlocked) @@ -312,6 +330,7 @@ namespace FlaxEditor.CustomEditors.Editors if (Count != _elementsCount) { RebuildLayout(); + RebuildParentCollection(); } } } diff --git a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs index 4a94136db..1a57fa59d 100644 --- a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs @@ -1,9 +1,5 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEditor.CustomEditors.GUI; using FlaxEditor.GUI; @@ -12,6 +8,10 @@ using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Json; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; namespace FlaxEditor.CustomEditors.Editors { @@ -279,6 +279,24 @@ namespace FlaxEditor.CustomEditors.Editors } } + /// + /// Rebuilds the parent layout if its collection. + /// + public void RebuildParentCollection() + { + if (ParentEditor is DictionaryEditor dictionaryEditor) + { + dictionaryEditor.RebuildParentCollection(); + dictionaryEditor.RebuildLayout(); + return; + } + if (ParentEditor is CollectionEditor collectionEditor) + { + collectionEditor.RebuildParentCollection(); + collectionEditor.RebuildLayout(); + } + } + private void OnSizeChanged() { if (IsSetBlocked) @@ -454,6 +472,7 @@ namespace FlaxEditor.CustomEditors.Editors if (Count != _elementsCount) { RebuildLayout(); + RebuildParentCollection(); } } } From 9629c48d9950ca72f5d67bdc95a04bbb308465d4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 3 Jun 2021 12:53:18 +0200 Subject: [PATCH 089/198] Revert CSharp only project build optimization to fix game cooking #539 #529 --- Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs index 1959cf3a9..8df614774 100644 --- a/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs +++ b/Source/Tools/Flax.Build/Build/NativeCpp/Builder.NativeCpp.cs @@ -995,7 +995,7 @@ namespace Flax.Build if (buildData.Toolchain == null) buildData.Toolchain = platform.GetToolchain(architecture); - if (Configuration.BuildBindingsOnly || project.IsCSharpOnlyProject || reference.Project.IsCSharpOnlyProject || !platform.HasRequiredSDKsInstalled) + if (Configuration.BuildBindingsOnly || reference.Project.IsCSharpOnlyProject || !platform.HasRequiredSDKsInstalled) BuildTargetReferenceNativeCppBindingsOnly(buildContext, buildData, reference); else BuildTargetReferenceNativeCpp(buildContext, buildData, reference); From a1f985bc7d1b496ae2a1e24723089d7179f2c107 Mon Sep 17 00:00:00 2001 From: Crawcik Date: Thu, 3 Jun 2021 13:44:21 +0200 Subject: [PATCH 090/198] Adding NotNullItem --- Source/Editor/Scripting/TypeUtils.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Source/Editor/Scripting/TypeUtils.cs b/Source/Editor/Scripting/TypeUtils.cs index 60f64ab8e..3d6362d5d 100644 --- a/Source/Editor/Scripting/TypeUtils.cs +++ b/Source/Editor/Scripting/TypeUtils.cs @@ -37,8 +37,9 @@ namespace FlaxEditor.Scripting /// Gets the default value for the given type (can be value type or reference type). /// /// The type. + /// Whether the value can be empty. If that's true, it can't. /// The created instance. - public static object GetDefaultValue(ScriptType type) + public static object GetDefaultValue(ScriptType type, bool notNull = false) { if (type.Type == typeof(string)) return string.Empty; @@ -64,7 +65,7 @@ namespace FlaxEditor.Scripting Utilities.Utils.InitDefaultValues(value); return value; } - if (new ScriptType(typeof(object)).IsAssignableFrom(type)) + if (!notNull && new ScriptType(typeof(object)).IsAssignableFrom(type)) return null; if (type.CanCreateInstance) { From e459707b0d658e6666634c2c186209ed02e8583d Mon Sep 17 00:00:00 2001 From: Crawcik Date: Thu, 3 Jun 2021 13:45:07 +0200 Subject: [PATCH 091/198] Custom background color for collections --- .../CustomEditors/Editors/ArrayEditor.cs | 4 +-- .../CustomEditors/Editors/CollectionEditor.cs | 34 +++++++++++++------ .../CustomEditors/Editors/DictionaryEditor.cs | 27 ++++++++++----- .../CustomEditors/Editors/ListEditor.cs | 8 ++--- .../Attributes/CollectionAttribute.cs | 5 +++ 5 files changed, 50 insertions(+), 28 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/ArrayEditor.cs b/Source/Editor/CustomEditors/Editors/ArrayEditor.cs index 41ca6ba86..af7bff167 100644 --- a/Source/Editor/CustomEditors/Editors/ArrayEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ArrayEditor.cs @@ -52,7 +52,7 @@ namespace FlaxEditor.CustomEditors.Editors else { // Initialize new entries with default values - var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType)); + var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType), NotNullItems); for (int i = oldSize; i < newSize; i++) newValues.SetValue(defaultValue, i); } @@ -60,7 +60,7 @@ namespace FlaxEditor.CustomEditors.Editors else if (newSize > 0) { // Initialize new entries with default values - var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType)); + var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType), NotNullItems); for (int i = 0; i < newSize; i++) newValues.SetValue(defaultValue, i); } diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index f7c6c1e06..2ae2e6ea5 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -77,11 +77,15 @@ namespace FlaxEditor.CustomEditors.Editors } } + /// + /// Determines if value of collection can be null. + /// + protected bool NotNullItems; private IntegerValueElement _size; + private Color _background; private int _elementsCount; private bool _readOnly; private bool _canReorderItems; - private bool _notNullItems; /// /// Gets the length of the collection. @@ -105,7 +109,7 @@ namespace FlaxEditor.CustomEditors.Editors { _readOnly = false; _canReorderItems = true; - _notNullItems = false; + NotNullItems = false; // No support for different collections for now if (HasDifferentValues || HasDifferentTypes) @@ -118,13 +122,19 @@ namespace FlaxEditor.CustomEditors.Editors Type overrideEditorType = null; float spacing = 10.0f; var collection = (CollectionAttribute)attributes?.FirstOrDefault(x => x is CollectionAttribute); - if (collection != null) + if (collection is null) + { + _readOnly = false; + NotNullItems = false; + _background = new Color(1f, 1f, 1f, 0.08f); + } + else { // TODO: handle NotNullItems by filtering child editors SetValue - _readOnly = collection.ReadOnly; _canReorderItems = collection.CanReorderItems; - _notNullItems = collection.NotNullItems; + NotNullItems = collection.NotNullItems; + _background = collection.BackgroundColor; overrideEditorType = TypeUtils.GetType(collection.OverrideEditorTypeName).Type; spacing = collection.Spacing; } @@ -146,6 +156,8 @@ namespace FlaxEditor.CustomEditors.Editors // Elements if (size > 0) { + var panel = layout.VerticalPanel(); + panel.Panel.BackgroundColor = _background; var elementType = ElementType; if (_canReorderItems) { @@ -153,7 +165,7 @@ namespace FlaxEditor.CustomEditors.Editors { if (i != 0 && spacing > 0f) { - if (layout.Children.Count > 0 && layout.Children[layout.Children.Count - 1] is PropertiesListElement propertiesListElement) + if (panel.Children.Count > 0 && panel.Children[panel.Children.Count - 1] is PropertiesListElement propertiesListElement) { if (propertiesListElement.Labels.Count > 0) { @@ -166,12 +178,12 @@ namespace FlaxEditor.CustomEditors.Editors } else { - layout.Space(spacing); + panel.Space(spacing); } } var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null; - layout.Object(new CollectionItemLabel(this, i), new ListValueContainer(elementType, i, Values), overrideEditor); + panel.Object(new CollectionItemLabel(this, i), new ListValueContainer(elementType, i, Values), overrideEditor); } } else @@ -180,14 +192,14 @@ namespace FlaxEditor.CustomEditors.Editors { if (i != 0 && spacing > 0f) { - if (layout.Children.Count > 0 && layout.Children[layout.Children.Count - 1] is PropertiesListElement propertiesListElement) + if (panel.Children.Count > 0 && panel.Children[panel.Children.Count - 1] is PropertiesListElement propertiesListElement) propertiesListElement.Space(spacing); else - layout.Space(spacing); + panel.Space(spacing); } var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null; - layout.Object("Element " + i, new ListValueContainer(elementType, i, Values), overrideEditor); + panel.Object("Element " + i, new ListValueContainer(elementType, i, Values), overrideEditor); } } } diff --git a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs index 1a57fa59d..62a8459f7 100644 --- a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs @@ -135,6 +135,7 @@ namespace FlaxEditor.CustomEditors.Editors } private IntegerValueElement _size; + private Color _background; private int _elementsCount; private bool _readOnly; private bool _notNullItems; @@ -164,8 +165,6 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - _readOnly = false; - _notNullItems = false; // No support for different collections for now if (HasDifferentValues || HasDifferentTypes) @@ -185,12 +184,20 @@ namespace FlaxEditor.CustomEditors.Editors if (attributes != null) { var collection = (CollectionAttribute)attributes.FirstOrDefault(x => x is CollectionAttribute); - if (collection != null) + if (collection is null) { // TODO: handle ReadOnly and NotNullItems by filtering child editors SetValue + + _readOnly = false; + _notNullItems = false; + _background = new Color(1f, 1f, 1f, 0.08f); + } + else + { _readOnly = collection.ReadOnly; _notNullItems = collection.NotNullItems; + _background = collection.BackgroundColor; overrideEditorType = TypeUtils.GetType(collection.OverrideEditorTypeName).Type; spacing = collection.Spacing; } @@ -213,13 +220,15 @@ namespace FlaxEditor.CustomEditors.Editors // Elements if (size > 0) { + var panel = layout.VerticalPanel(); + panel.Panel.BackgroundColor = _background; var keysEnumerable = ((IDictionary)Values[0]).Keys.OfType(); var keys = keysEnumerable as object[] ?? keysEnumerable.ToArray(); for (int i = 0; i < size; i++) { if (i != 0 && spacing > 0f) { - if (layout.Children.Count > 0 && layout.Children[layout.Children.Count - 1] is PropertiesListElement propertiesListElement) + if (panel.Children.Count > 0 && panel.Children[panel.Children.Count - 1] is PropertiesListElement propertiesListElement) { if (propertiesListElement.Labels.Count > 0) { @@ -232,13 +241,13 @@ namespace FlaxEditor.CustomEditors.Editors } else { - layout.Space(spacing); + panel.Space(spacing); } } var key = keys.ElementAt(i); var overrideEditor = overrideEditorType != null ? (CustomEditor)Activator.CreateInstance(overrideEditorType) : null; - layout.Object(new DictionaryItemLabel(this, key), new DictionaryValueContainer(new ScriptType(valueType), key, Values), overrideEditor); + panel.Object(new DictionaryItemLabel(this, key), new DictionaryValueContainer(new ScriptType(valueType), key, Values), overrideEditor); } } _elementsCount = size; @@ -407,7 +416,7 @@ namespace FlaxEditor.CustomEditors.Editors } } while (!isUnique); - newValues[Convert.ChangeType(uniqueKey, keyType)] = TypeUtils.GetDefaultValue(new ScriptType(valueType)); + newValues[Convert.ChangeType(uniqueKey, keyType)] = TypeUtils.GetDefaultValue(new ScriptType(valueType), _notNullItems); } else if (keyType.IsEnum) { @@ -428,7 +437,7 @@ namespace FlaxEditor.CustomEditors.Editors } } while (!isUnique && uniqueKeyIndex < enumValues.Length); - newValues[enumValues.GetValue(uniqueKeyIndex)] = TypeUtils.GetDefaultValue(new ScriptType(valueType)); + newValues[enumValues.GetValue(uniqueKeyIndex)] = TypeUtils.GetDefaultValue(new ScriptType(valueType), _notNullItems); } else if (keyType == typeof(string)) { @@ -448,7 +457,7 @@ namespace FlaxEditor.CustomEditors.Editors } } while (!isUnique); - newValues[uniqueKey] = TypeUtils.GetDefaultValue(new ScriptType(valueType)); + newValues[uniqueKey] = TypeUtils.GetDefaultValue(new ScriptType(valueType), _notNullItems); } else { diff --git a/Source/Editor/CustomEditors/Editors/ListEditor.cs b/Source/Editor/CustomEditors/Editors/ListEditor.cs index 0c6efaea4..a8871329a 100644 --- a/Source/Editor/CustomEditors/Editors/ListEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ListEditor.cs @@ -22,9 +22,7 @@ namespace FlaxEditor.CustomEditors.Editors var list = (IList)listType.CreateInstance(); var defaultValue = Scripting.TypeUtils.GetDefaultValue(ElementType); for (int i = 0; i < size; i++) - { list.Add(defaultValue); - } return list; } @@ -57,7 +55,7 @@ namespace FlaxEditor.CustomEditors.Editors else { // Initialize new entries with default values - var defaultValue = Scripting.TypeUtils.GetDefaultValue(elementType); + var defaultValue = Scripting.TypeUtils.GetDefaultValue(elementType, NotNullItems); for (int i = oldSize; i < newSize; i++) newValues.Add(defaultValue); } @@ -65,7 +63,7 @@ namespace FlaxEditor.CustomEditors.Editors else if (newSize > 0) { // Fill new entries with default value - var defaultValue = Scripting.TypeUtils.GetDefaultValue(elementType); + var defaultValue = Scripting.TypeUtils.GetDefaultValue(elementType, NotNullItems); for (int i = oldSize; i < newSize; i++) newValues.Add(defaultValue); } @@ -86,9 +84,7 @@ namespace FlaxEditor.CustomEditors.Editors var cloned = (IList)listType.CreateInstance(); for (int i = 0; i < size; i++) - { cloned.Add(list[i]); - } return cloned; } diff --git a/Source/Engine/Scripting/Attributes/CollectionAttribute.cs b/Source/Engine/Scripting/Attributes/CollectionAttribute.cs index 78781d3f9..61beac368 100644 --- a/Source/Engine/Scripting/Attributes/CollectionAttribute.cs +++ b/Source/Engine/Scripting/Attributes/CollectionAttribute.cs @@ -34,5 +34,10 @@ namespace FlaxEngine /// The spacing amount between collection items in the UI. /// public float Spacing; + + /// + /// Background color of the collection. + /// + public Color BackgroundColor = new Color(1f, 1f, 1f, 0.08f); } } From 9b55ffdf6c2289dfddc199a644b7234eb7c8b313 Mon Sep 17 00:00:00 2001 From: Crawcik Date: Thu, 3 Jun 2021 14:29:42 +0200 Subject: [PATCH 092/198] Adding collections background color in global settings --- .../CustomEditors/Editors/CollectionEditor.cs | 2 +- .../CustomEditors/Editors/DictionaryEditor.cs | 14 ++++++-------- Source/Editor/Options/OptionsModule.cs | 1 + .../Scripting/Attributes/CollectionAttribute.cs | 5 +++-- Source/Engine/Scripting/Scripting.cs | 1 + Source/Engine/UI/GUI/Style.cs | 5 +++++ 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index 2ae2e6ea5..424ff83ca 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -126,7 +126,7 @@ namespace FlaxEditor.CustomEditors.Editors { _readOnly = false; NotNullItems = false; - _background = new Color(1f, 1f, 1f, 0.08f); + _background = FlaxEngine.GUI.Style.Current.CollectionBackgroundColor; } else { diff --git a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs index 62a8459f7..99e84b225 100644 --- a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs @@ -1,5 +1,9 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEditor.CustomEditors.GUI; using FlaxEditor.GUI; @@ -8,10 +12,6 @@ using FlaxEditor.Scripting; using FlaxEngine; using FlaxEngine.GUI; using FlaxEngine.Json; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; namespace FlaxEditor.CustomEditors.Editors { @@ -186,15 +186,13 @@ namespace FlaxEditor.CustomEditors.Editors var collection = (CollectionAttribute)attributes.FirstOrDefault(x => x is CollectionAttribute); if (collection is null) { - // TODO: handle ReadOnly and NotNullItems by filtering child editors SetValue - - _readOnly = false; _notNullItems = false; - _background = new Color(1f, 1f, 1f, 0.08f); + _background = FlaxEngine.GUI.Style.Current.CollectionBackgroundColor; } else { + // TODO: handle ReadOnly and NotNullItems by filtering child editors SetValue _readOnly = collection.ReadOnly; _notNullItems = collection.NotNullItems; _background = collection.BackgroundColor; diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index ea42ee99b..a7f9a6dd0 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -232,6 +232,7 @@ namespace FlaxEditor.Options BorderNormal = Color.FromBgra(0xFF54545C), TextBoxBackground = Color.FromBgra(0xFF333337), TextBoxBackgroundSelected = Color.FromBgra(0xFF3F3F46), + CollectionBackgroundColor = Color.FromBgra(0x16FFFFFF), ProgressNormal = Color.FromBgra(0xFF0ad328), // Fonts diff --git a/Source/Engine/Scripting/Attributes/CollectionAttribute.cs b/Source/Engine/Scripting/Attributes/CollectionAttribute.cs index 61beac368..7d854d582 100644 --- a/Source/Engine/Scripting/Attributes/CollectionAttribute.cs +++ b/Source/Engine/Scripting/Attributes/CollectionAttribute.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; +using FlaxEngine.GUI; namespace FlaxEngine { @@ -36,8 +37,8 @@ namespace FlaxEngine public float Spacing; /// - /// Background color of the collection. + /// The collection background color. /// - public Color BackgroundColor = new Color(1f, 1f, 1f, 0.08f); + public Color BackgroundColor = Style.Current.CollectionBackgroundColor; } } diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index ae7821526..baafffd56 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -221,6 +221,7 @@ namespace FlaxEngine TextBoxBackground = Color.FromBgra(0xFF333337), ProgressNormal = Color.FromBgra(0xFF0ad328), TextBoxBackgroundSelected = Color.FromBgra(0xFF3F3F46), + CollectionBackgroundColor = Color.FromBgra(0x16FFFFFF), SharedTooltip = new Tooltip(), }; style.DragWindow = style.BackgroundSelected * 0.7f; diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index 9b4f0f64e..b6ca35f88 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -152,6 +152,11 @@ namespace FlaxEngine.GUI [EditorOrder(190)] public Color TextBoxBackgroundSelected; + /// + /// The collection background color. + /// + [EditorOrder(195)] + public Color CollectionBackgroundColor; /// /// The progress normal color. /// From 3a6b7ea80b5617062144021167dde07a3d18b3fb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 3 Jun 2021 15:19:00 +0200 Subject: [PATCH 093/198] Add support for `Function<>` as method parameter in scripting --- .../Bindings/BindingsGenerator.Api.cs | 8 +++--- .../Bindings/BindingsGenerator.CSharp.cs | 27 +++++++++++++++++++ .../Bindings/BindingsGenerator.Cpp.cs | 22 +++++++++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs index ded954e5a..42f785e16 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Api.cs @@ -210,17 +210,15 @@ namespace Flax.Build.Bindings if ((typeInfo.Type == "Array" || typeInfo.Type == "Span" || typeInfo.Type == "Dictionary" || typeInfo.Type == "HashSet") && typeInfo.GenericArgs != null) return false; - // Skip for BytesContainer + // Skip for special types if (typeInfo.Type == "BytesContainer" && typeInfo.GenericArgs == null) return false; - - // Skip for Variant if (typeInfo.Type == "Variant" && typeInfo.GenericArgs == null) return false; - - // Skip for VariantType if (typeInfo.Type == "VariantType" && typeInfo.GenericArgs == null) return false; + if (typeInfo.Type == "Function" && typeInfo.GenericArgs != null) + return false; // Find API type info var apiType = FindApiTypeInfo(buildData, typeInfo, caller); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 6d92c3969..41b4eb796 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -197,6 +197,26 @@ namespace Flax.Build.Bindings if (typeInfo.Type == "BytesContainer" && typeInfo.GenericArgs == null) return "byte[]"; + // Function + if (typeInfo.Type == "Function" && typeInfo.GenericArgs != null) + { + if (typeInfo.GenericArgs.Count == 0) + throw new Exception("Missing function return type."); + if (typeInfo.GenericArgs.Count > 1) + { + var args = string.Empty; + args += GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[1], caller); + for (int i = 2; i < typeInfo.GenericArgs.Count; i++) + args += ", " + GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[i], caller); + if (typeInfo.GenericArgs[0].Type == "void") + return string.Format("Action<{0}>", args); + return string.Format("Func<{0}, {1}>", args, GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller)); + } + if (typeInfo.GenericArgs[0].Type == "void") + return "Action"; + return string.Format("Func<{0}>", GenerateCSharpNativeToManaged(buildData, typeInfo.GenericArgs[0], caller)); + } + // Find API type info var apiType = FindApiTypeInfo(buildData, typeInfo, caller); if (apiType != null) @@ -235,6 +255,10 @@ namespace Flax.Build.Bindings if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null) return "IntPtr"; + // Function + if (typeInfo.Type == "Function" && typeInfo.GenericArgs != null) + return "IntPtr"; + return GenerateCSharpNativeToManaged(buildData, typeInfo, caller); } @@ -265,6 +289,9 @@ namespace Flax.Build.Bindings case "ManagedScriptingObject": // object return "FlaxEngine.Object.GetUnmanagedPtr"; + case "Function": + // delegate + return "Marshal.GetFunctionPointerForDelegate"; default: var apiType = FindApiTypeInfo(buildData, typeInfo, caller); if (apiType != null) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index a2fffddd4..70627d97e 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -420,6 +420,13 @@ namespace Flax.Build.Bindings return "ManagedBitArray::ToManaged({0})"; } + // Function + if (typeInfo.Type == "Function" && typeInfo.GenericArgs != null) + { + // TODO: automatic converting managed-native for Function + throw new NotImplementedException("TODO: converting native Function to managed"); + } + var apiType = FindApiTypeInfo(buildData, typeInfo, caller); if (apiType != null) { @@ -606,6 +613,21 @@ namespace Flax.Build.Bindings return "MUtils::LinkArray({0})"; } + // Function + if (typeInfo.Type == "Function" && typeInfo.GenericArgs != null) + { + var args = string.Empty; + if (typeInfo.GenericArgs.Count > 1) + { + args += typeInfo.GenericArgs[1].GetFullNameNative(buildData, caller); + for (int i = 2; i < typeInfo.GenericArgs.Count; i++) + args += ", " + typeInfo.GenericArgs[i].GetFullNameNative(buildData, caller); + } + var T = $"Function<{typeInfo.GenericArgs[0].GetFullNameNative(buildData, caller)}({args})>"; + type = T + "::Signature"; + return T + "({0})"; + } + if (apiType != null) { // Scripting Object (for non-pod types converting only, other API converts managed to unmanaged object in C# wrapper code) From dfee74ad1f146de4185d41110f0a6b850bacf3c7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 4 Jun 2021 12:38:44 +0200 Subject: [PATCH 094/198] Add missing name some for GPU resources --- Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs | 2 +- Source/Engine/Renderer/ProbesRenderer.cpp | 2 +- Source/Engine/ShadowsOfMordor/Builder.cpp | 2 +- Source/Engine/UI/GUI/RenderOutputControl.cs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs b/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs index fc9b17601..359fa0126 100644 --- a/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs @@ -471,7 +471,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks _atlases = new List(4); if (_output == null) { - _output = GPUDevice.Instance.CreateTexture(); + _output = GPUDevice.Instance.CreateTexture("CameraCutMedia.Output"); var desc = GPUTextureDescription.New2D(Width, Height, PixelFormat.R8G8B8A8_UNorm); _output.Init(ref desc); } diff --git a/Source/Engine/Renderer/ProbesRenderer.cpp b/Source/Engine/Renderer/ProbesRenderer.cpp index 0a078b4b7..504416685 100644 --- a/Source/Engine/Renderer/ProbesRenderer.cpp +++ b/Source/Engine/Renderer/ProbesRenderer.cpp @@ -276,7 +276,7 @@ bool ProbesRenderer::Init() } // Init rendering pipeline - _output = GPUTexture::New(); + _output = GPUDevice::Instance->CreateTexture(TEXT("Output")); if (_output->Init(GPUTextureDescription::New2D(ENV_PROBES_RESOLUTION, ENV_PROBES_RESOLUTION, ENV_PROBES_FORMAT))) return true; _task = New(); diff --git a/Source/Engine/ShadowsOfMordor/Builder.cpp b/Source/Engine/ShadowsOfMordor/Builder.cpp index 6225e36f6..5d90a4ec8 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.cpp @@ -432,7 +432,7 @@ bool ShadowsOfMordor::Builder::initResources() // TODO: remove this release and just create missing resources releaseResources(); - _output = GPUTexture::New(); + _output = GPUDevice::Instance->CreateTexture(TEXT("Output")); if (_output->Init(GPUTextureDescription::New2D(HEMISPHERES_RESOLUTION, HEMISPHERES_RESOLUTION, PixelFormat::R11G11B10_Float))) return true; _task = New(); diff --git a/Source/Engine/UI/GUI/RenderOutputControl.cs b/Source/Engine/UI/GUI/RenderOutputControl.cs index 9a7f6dfb1..bad38b3c3 100644 --- a/Source/Engine/UI/GUI/RenderOutputControl.cs +++ b/Source/Engine/UI/GUI/RenderOutputControl.cs @@ -96,7 +96,7 @@ namespace FlaxEngine.GUI { _task = task ?? throw new ArgumentNullException(); - _backBuffer = GPUDevice.Instance.CreateTexture(); + _backBuffer = GPUDevice.Instance.CreateTexture("RenderOutputControl.BackBuffer"); _resizeTime = ResizeCheckTime; _task.Output = _backBuffer; @@ -245,7 +245,7 @@ namespace FlaxEngine.GUI if (_backBufferOld == null && _backBuffer.IsAllocated) { _backBufferOld = _backBuffer; - _backBuffer = GPUDevice.Instance.CreateTexture(); + _backBuffer = GPUDevice.Instance.CreateTexture("RenderOutputControl.BackBuffer"); } // Set timeout to remove old buffer From f3652697aeade61e010f50a68c806eec82a3d9de Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Sun, 6 Jun 2021 17:16:19 +0300 Subject: [PATCH 095/198] Fix building UWP platform with Visual Studio 2019 --- .../Flax.Build/Platforms/Windows/WindowsToolchainBase.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs index b59bcae39..8a1b3b43f 100644 --- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs +++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs @@ -367,8 +367,11 @@ namespace Flax.Build.Platforms var vcToolChainDir = toolsets[Toolset]; switch (Toolset) { - case WindowsPlatformToolset.v141: return Path.Combine(vcToolChainDir, "lib", "x86", "store", "references"); - case WindowsPlatformToolset.v140: return Path.Combine(vcToolChainDir, "lib", "store", "references"); + case WindowsPlatformToolset.v142: + case WindowsPlatformToolset.v141: + return Path.Combine(vcToolChainDir, "lib", "x86", "store", "references"); + case WindowsPlatformToolset.v140: + return Path.Combine(vcToolChainDir, "lib", "store", "references"); default: return null; } } From 474add9d12d199097b00dbb12ee6dac5619d04e3 Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Sun, 6 Jun 2021 17:16:55 +0300 Subject: [PATCH 096/198] Fix input related compile errors in UWP platform --- Source/Engine/Platform/UWP/UWPWindow.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Source/Engine/Platform/UWP/UWPWindow.cpp b/Source/Engine/Platform/UWP/UWPWindow.cpp index a55f3b5d2..716d038de 100644 --- a/Source/Engine/Platform/UWP/UWPWindow.cpp +++ b/Source/Engine/Platform/UWP/UWPWindow.cpp @@ -325,7 +325,7 @@ UWPWindow::UWPWindow(const CreateWindowSettings& settings, UWPWindowImpl* impl) _dpiScale = _dpi / 96.0f; float x, y; _impl->GetBounds(&x, &y, &_logicalSize.X, &_logicalSize.Y); - _impl->GetMousePosition(&Impl::Mouse.MousePosition.X, &Impl::Mouse.MousePosition.Y); + _impl->GetMousePosition(&Impl::Mouse->MousePosition.X, &Impl::Mouse->MousePosition.Y); OnSizeChange(); } @@ -502,7 +502,7 @@ void UWPWindow::onKeyDown(int key) if (key < 7) return; - Impl::Keyboard.onKeyDown(key); + Impl::Keyboard->onKeyDown(key); } void UWPWindow::onKeyUp(int key) @@ -510,17 +510,17 @@ void UWPWindow::onKeyUp(int key) if (key < 7) return; - Impl::Keyboard.onKeyUp(key); + Impl::Keyboard->onKeyUp(key); } void UWPWindow::onCharacterReceived(int key) { - Impl::Keyboard.onCharacterReceived(key); + Impl::Keyboard->onCharacterReceived(key); } void UWPWindow::onMouseMoved(float x, float y) { - Impl::Mouse.onMouseMoved(x * _dpiScale, y * _dpiScale); + Impl::Mouse->onMouseMoved(x * _dpiScale, y * _dpiScale); } void UWPWindow::onPointerPressed(UWPWindowImpl::PointerData* pointer) @@ -529,7 +529,7 @@ void UWPWindow::onPointerPressed(UWPWindowImpl::PointerData* pointer) { pointer->PositionX *= _dpiScale; pointer->PositionY *= _dpiScale; - Impl::Mouse.onPointerPressed(pointer); + Impl::Mouse->onPointerPressed(pointer); } // TODO: impl mobile touch support @@ -541,7 +541,7 @@ void UWPWindow::onPointerMoved(UWPWindowImpl::PointerData* pointer) { pointer->PositionX *= _dpiScale; pointer->PositionY *= _dpiScale; - Impl::Mouse.onPointerMoved(pointer); + Impl::Mouse->onPointerMoved(pointer); } // TODO: impl mobile touch support @@ -553,7 +553,7 @@ void UWPWindow::onPointerWheelChanged(UWPWindowImpl::PointerData* pointer) { pointer->PositionX *= _dpiScale; pointer->PositionY *= _dpiScale; - Impl::Mouse.onPointerWheelChanged(pointer); + Impl::Mouse->onPointerWheelChanged(pointer); } } @@ -563,7 +563,7 @@ void UWPWindow::onPointerReleased(UWPWindowImpl::PointerData* pointer) { pointer->PositionX *= _dpiScale; pointer->PositionY *= _dpiScale; - Impl::Mouse.onPointerReleased(pointer); + Impl::Mouse->onPointerReleased(pointer); } // TODO: impl mobile touch support @@ -575,7 +575,7 @@ void UWPWindow::onPointerExited(UWPWindowImpl::PointerData* pointer) { pointer->PositionX *= _dpiScale; pointer->PositionY *= _dpiScale; - Impl::Mouse.onPointerExited(pointer); + Impl::Mouse->onPointerExited(pointer); } // TODO: impl mobile touch support From 3c48484870759476c56a48a77b2b7b16ccc8a281 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Sun, 6 Jun 2021 21:52:29 +0200 Subject: [PATCH 097/198] Use Stopwatch instead of DateTime.Now for increased accuracy DateTime.Now is decent, but not accurate enough for timings involving only a few milliseconds. --- Source/Tools/Flax.Build/Build/Profiling.cs | 7 +++++-- Source/Tools/Flax.Build/Program.cs | 8 ++++---- Source/Tools/Flax.Build/Utilities/Utilities.cs | 6 +++--- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Source/Tools/Flax.Build/Build/Profiling.cs b/Source/Tools/Flax.Build/Build/Profiling.cs index c5cc89bcb..785badad3 100644 --- a/Source/Tools/Flax.Build/Build/Profiling.cs +++ b/Source/Tools/Flax.Build/Build/Profiling.cs @@ -5,6 +5,7 @@ using System.IO; using System.Collections.Generic; using System.Text; using System.Threading; +using System.Diagnostics; namespace Flax.Build { @@ -71,6 +72,8 @@ namespace Flax.Build private static int _depth; private static readonly List _events = new List(1024); + private static readonly DateTime _startTime = DateTime.Now; + private static readonly Stopwatch _stopwatch = Stopwatch.StartNew(); // https://stackoverflow.com/questions/1416139/how-to-get-timestamp-of-tick-precision-in-net-c /// /// Begins the profiling event. @@ -81,7 +84,7 @@ namespace Flax.Build { Event e; e.Name = name; - e.StartTime = DateTime.Now; + e.StartTime = _startTime.AddTicks(_stopwatch.Elapsed.Ticks); e.Duration = TimeSpan.Zero; e.Depth = _depth++; e.ThreadId = Thread.CurrentThread.ManagedThreadId; @@ -95,7 +98,7 @@ namespace Flax.Build /// The event identifier returned by . public static void End(int id) { - var endTime = DateTime.Now; + var endTime = _startTime.AddTicks(_stopwatch.Elapsed.Ticks); var e = _events[id]; e.Duration = endTime - e.StartTime; _events[id] = e; diff --git a/Source/Tools/Flax.Build/Program.cs b/Source/Tools/Flax.Build/Program.cs index 89e71ace0..7411edb3b 100644 --- a/Source/Tools/Flax.Build/Program.cs +++ b/Source/Tools/Flax.Build/Program.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; +using System.Diagnostics; using System.IO; using System.Net; using System.Threading; @@ -25,7 +26,7 @@ namespace Flax.Build } Mutex singleInstanceMutex = null; - var startTime = DateTime.Now; + Stopwatch stopwatch = Stopwatch.StartNew(); bool failed = false; try @@ -166,9 +167,8 @@ namespace Flax.Build singleInstanceMutex.Dispose(); singleInstanceMutex = null; } - - var endTime = DateTime.Now; - Log.Info(string.Format("Total time: {0}", endTime - startTime)); + stopwatch.Stop(); + Log.Info(string.Format("Total time: {0}", stopwatch.Elapsed)); Log.Verbose("End."); Log.Dispose(); } diff --git a/Source/Tools/Flax.Build/Utilities/Utilities.cs b/Source/Tools/Flax.Build/Utilities/Utilities.cs index 8390377a8..e08755175 100644 --- a/Source/Tools/Flax.Build/Utilities/Utilities.cs +++ b/Source/Tools/Flax.Build/Utilities/Utilities.cs @@ -326,7 +326,7 @@ namespace Flax.Build } } - var startTime = DateTime.UtcNow; + Stopwatch stopwatch = Stopwatch.StartNew(); if (!options.HasFlag(RunOptions.NoLoggingOfRunCommand)) { Log.Verbose("Running: " + app + " " + (string.IsNullOrEmpty(commandLine) ? "" : commandLine)); @@ -397,11 +397,11 @@ namespace Flax.Build if (!options.HasFlag(RunOptions.NoWaitForExit)) { - var buildDuration = (DateTime.UtcNow - startTime).TotalMilliseconds; + stopwatch.Stop(); result = proc.ExitCode; if (!options.HasFlag(RunOptions.NoLoggingOfRunCommand) || options.HasFlag(RunOptions.NoLoggingOfRunDuration)) { - Log.Info(string.Format("Took {0}s to run {1}, ExitCode={2}", buildDuration / 1000, Path.GetFileName(app), result)); + Log.Info(string.Format("Took {0}s to run {1}, ExitCode={2}", stopwatch.Elapsed.TotalSeconds, Path.GetFileName(app), result)); } } From cc47cb85e47d7394aa8168f4d3b076651903794b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 7 Jun 2021 11:56:04 +0200 Subject: [PATCH 098/198] Cleanup renderer code --- Source/Engine/Graphics/DynamicBuffer.h | 1 - Source/Engine/Graphics/GPUDevice.cpp | 60 +++++++++++++++++++ Source/Engine/Graphics/GPUPipelineState.h | 40 +------------ Source/Engine/Graphics/GPUResource.h | 32 ++-------- Source/Engine/Graphics/RenderBuffers.cpp | 2 - .../DirectX/DX12/DescriptorHeapDX12.h | 17 ------ .../DirectX/DX12/GPUBufferDX12.cpp | 8 +-- .../DirectX/DX12/GPUBufferDX12.h | 6 -- .../DirectX/DX12/GPUDeviceDX12.h | 21 ++----- .../DirectX/DX12/GPUTextureDX12.h | 31 +--------- .../DirectX/DX12/IShaderResourceDX12.h | 4 -- .../DirectX/DX12/QueryHeapDX12.cpp | 17 +----- .../DirectX/DX12/ResourceOwnerDX12.h | 5 +- .../DirectX/DX12/UploadBufferDX12.cpp | 31 ++++------ .../DirectX/DX12/UploadBufferDX12.h | 1 - .../Vulkan/GPUContextVulkan.cpp | 3 - Source/Engine/Renderer/Utils/MultiScaler.cpp | 8 +-- 17 files changed, 92 insertions(+), 195 deletions(-) diff --git a/Source/Engine/Graphics/DynamicBuffer.h b/Source/Engine/Graphics/DynamicBuffer.h index 10afb52b8..f22143c1b 100644 --- a/Source/Engine/Graphics/DynamicBuffer.h +++ b/Source/Engine/Graphics/DynamicBuffer.h @@ -40,7 +40,6 @@ public: /// /// Gets buffer (may be null since it's using 'late init' feature) /// - /// Buffer FORCE_INLINE GPUBuffer* GetBuffer() const { return _buffer; diff --git a/Source/Engine/Graphics/GPUDevice.cpp b/Source/Engine/Graphics/GPUDevice.cpp index 8f30dc85a..f6be31923 100644 --- a/Source/Engine/Graphics/GPUDevice.cpp +++ b/Source/Engine/Graphics/GPUDevice.cpp @@ -31,6 +31,39 @@ GPUPipelineState* GPUPipelineState::New() return GPUDevice::Instance->CreatePipelineState(); } +bool GPUPipelineState::Init(const Description& desc) +{ + // Cache description in debug builds +#if BUILD_DEBUG + DebugDesc = desc; +#endif + + // Cache shader stages usage flags for pipeline state + _meta.InstructionsCount = 0; + _meta.UsedCBsMask = 0; + _meta.UsedSRsMask = 0; + _meta.UsedUAsMask = 0; +#define CHECK_STAGE(stage) \ + if (desc.stage) { \ + _meta.UsedCBsMask |= desc.stage->GetBindings().UsedCBsMask; \ + _meta.UsedSRsMask |= desc.stage->GetBindings().UsedSRsMask; \ + _meta.UsedUAsMask |= desc.stage->GetBindings().UsedUAsMask; \ + } + CHECK_STAGE(VS); + CHECK_STAGE(HS); + CHECK_STAGE(DS); + CHECK_STAGE(GS); + CHECK_STAGE(PS); +#undef CHECK_STAGE + + return false; +} + +GPUResource::ResourceType GPUPipelineState::GetResourceType() const +{ + return ResourceType::PipelineState; +} + GPUPipelineState::Description GPUPipelineState::Description::Default = { // Enable/disable depth write @@ -122,6 +155,33 @@ GPUPipelineState::Description GPUPipelineState::Description::DefaultFullscreenTr BlendingMode::Opaque, }; +void GPUResource::ReleaseGPU() +{ + if (_memoryUsage != 0) + { + Releasing(); + OnReleaseGPU(); + _memoryUsage = 0; + } +} + +void GPUResource::OnDeviceDispose() +{ + // By default we want to release resource data but keep it alive + ReleaseGPU(); +} + +void GPUResource::OnReleaseGPU() +{ +} + +void GPUResource::OnDeleteObject() +{ + ReleaseGPU(); + + PersistentScriptingObject::OnDeleteObject(); +} + struct GPUDevice::PrivateData { AssetReference QuadShader; diff --git a/Source/Engine/Graphics/GPUPipelineState.h b/Source/Engine/Graphics/GPUPipelineState.h index 5e16799c8..5e7609876 100644 --- a/Source/Engine/Graphics/GPUPipelineState.h +++ b/Source/Engine/Graphics/GPUPipelineState.h @@ -115,12 +115,10 @@ protected: public: #if BUILD_DEBUG - /// /// The description of the pipeline state cached on creation in debug builds. Can be used to help with rendering crashes or issues and validation. /// Description DebugDesc; - #endif public: @@ -128,7 +126,6 @@ public: /// /// Gets constant buffers usage mask (each set bit marks usage of the constant buffer at the bit index slot). Combined from all the used shader stages. /// - /// The constant buffers usage mask. FORCE_INLINE uint32 GetUsedCBsMask() const { return _meta.UsedCBsMask; @@ -137,7 +134,6 @@ public: /// /// Gets shader resources usage mask (each set bit marks usage of the shader resource slot at the bit index slot). Combined from all the used shader stages. /// - /// The shader resource usage mask. FORCE_INLINE uint32 GetUsedSRsMask() const { return _meta.UsedSRsMask; @@ -146,7 +142,6 @@ public: /// /// Gets unordered access usage mask (each set bit marks usage of the unordered access slot at the bit index slot). Combined from all the used shader stages. /// - /// The unordered access usage mask. FORCE_INLINE uint32 GetUsedUAsMask() const { return _meta.UsedUAsMask; @@ -157,7 +152,6 @@ public: /// /// Returns true if pipeline state is valid and ready to use /// - /// True if is valid API_PROPERTY() virtual bool IsValid() const = 0; /// @@ -165,39 +159,9 @@ public: /// /// Full pipeline state description /// True if cannot create state, otherwise false - API_FUNCTION() virtual bool Init(API_PARAM(Ref) const Description& desc) - { - // Cache description in debug builds -#if BUILD_DEBUG - DebugDesc = desc; -#endif - - // Cache shader stages usage flags for pipeline state - _meta.InstructionsCount = 0; - _meta.UsedCBsMask = 0; - _meta.UsedSRsMask = 0; - _meta.UsedUAsMask = 0; -#define CHECK_STAGE(stage) \ - if(desc.stage) { \ - _meta.UsedCBsMask |= desc.stage->GetBindings().UsedCBsMask; \ - _meta.UsedSRsMask |= desc.stage->GetBindings().UsedSRsMask; \ - _meta.UsedUAsMask |= desc.stage->GetBindings().UsedUAsMask; \ - } - CHECK_STAGE(VS); - CHECK_STAGE(HS); - CHECK_STAGE(DS); - CHECK_STAGE(GS); - CHECK_STAGE(PS); -#undef CHECK_STAGE - - return false; - } + API_FUNCTION() virtual bool Init(API_PARAM(Ref) const Description& desc); public: - // [GPUResource] - ResourceType GetResourceType() const final override - { - return ResourceType::PipelineState; - } + ResourceType GetResourceType() const final override; }; diff --git a/Source/Engine/Graphics/GPUResource.h b/Source/Engine/Graphics/GPUResource.h index 288da6579..8f014d318 100644 --- a/Source/Engine/Graphics/GPUResource.h +++ b/Source/Engine/Graphics/GPUResource.h @@ -75,13 +75,11 @@ public: /// /// Gets the resource type. /// - /// The type. virtual ResourceType GetResourceType() const = 0; /// /// Gets resource object type. /// - /// The object type. virtual ObjectType GetObjectType() const { return ObjectType::Other; @@ -91,7 +89,6 @@ public: /// Gets amount of GPU memory used by this resource (in bytes). /// It's a rough estimation. GPU memory may be fragmented, compressed or sub-allocated so the actual memory pressure from this resource may vary (also depends on the current graphics backend). /// - /// The current memory usage. API_PROPERTY() FORCE_INLINE uint64 GetMemoryUsage() const { return _memoryUsage; @@ -102,7 +99,6 @@ public: /// /// Gets the resource name. /// - /// The name of the resource. virtual String GetName() const { return String::Empty; @@ -113,33 +109,19 @@ public: /// /// Releases GPU resource data. /// - API_FUNCTION() void ReleaseGPU() - { - if (_memoryUsage != 0) - { - Releasing(); - OnReleaseGPU(); - _memoryUsage = 0; - } - } + API_FUNCTION() void ReleaseGPU(); /// /// Action called when GPU device is disposing. /// - virtual void OnDeviceDispose() - { - // By default we want to release resource data but keep it alive - ReleaseGPU(); - } + virtual void OnDeviceDispose(); protected: /// /// Releases GPU resource data (implementation). /// - virtual void OnReleaseGPU() - { - } + virtual void OnReleaseGPU(); public: @@ -152,13 +134,7 @@ public: return TEXT("GPU Resource"); #endif } - - void OnDeleteObject() override - { - ReleaseGPU(); - - PersistentScriptingObject::OnDeleteObject(); - } + void OnDeleteObject() override; }; /// diff --git a/Source/Engine/Graphics/RenderBuffers.cpp b/Source/Engine/Graphics/RenderBuffers.cpp index 31ce925ee..42ad32486 100644 --- a/Source/Engine/Graphics/RenderBuffers.cpp +++ b/Source/Engine/Graphics/RenderBuffers.cpp @@ -98,10 +98,8 @@ GPUTexture* RenderBuffers::RequestHalfResDepth(GPUContext* context) uint64 RenderBuffers::GetMemoryUsage() const { uint64 result = 0; - for (int32 i = 0; i < _resources.Count(); i++) result += _resources[i]->GetMemoryUsage(); - return result; } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h index 8123a1ead..f7de6a9d6 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h @@ -37,26 +37,9 @@ public: D3D12_CPU_DESCRIPTOR_HANDLE CPU() const; D3D12_GPU_DESCRIPTOR_HANDLE GPU() const; - // Creates shader resource view - // @param index Descriptor index in the heap - // @param resource Shader Resource to create view for it - // @param desc View description void CreateSRV(GPUDeviceDX12* device, ID3D12Resource* resource, D3D12_SHADER_RESOURCE_VIEW_DESC* desc = nullptr); - - // Creates render target view - // @param index Descriptor index in the heap - // @param resource Render Target to create view for it void CreateRTV(GPUDeviceDX12* device, ID3D12Resource* resource, D3D12_RENDER_TARGET_VIEW_DESC* desc = nullptr); - - // Creates depth stencil view - // @param index Descriptor index in the heap - // @param resource Render Target Depth to create view for it void CreateDSV(GPUDeviceDX12* device, ID3D12Resource* resource, D3D12_DEPTH_STENCIL_VIEW_DESC* desc = nullptr); - - // Creates unordered access view - // @param index Descriptor index in the heap - // @param resource Unordered Access to create view for it - // @param desc View description void CreateUAV(GPUDeviceDX12* device, ID3D12Resource* resource, D3D12_UNORDERED_ACCESS_VIEW_DESC* desc = nullptr, ID3D12Resource* counterResource = nullptr); void Release(); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp index df8ff9606..73d8111f0 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp @@ -125,13 +125,7 @@ bool GPUBufferDX12::OnInit() // Create resource ID3D12Resource* resource; D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COPY_DEST; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateCommittedResource( - &heapProperties, - D3D12_HEAP_FLAG_NONE, - &resourceDesc, - initialState, - nullptr, - IID_PPV_ARGS(&resource))); + VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, initialState, nullptr, IID_PPV_ARGS(&resource))); // Set state initResource(resource, initialState, 1); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.h index 7d607f960..7c45dfe5d 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.h @@ -107,17 +107,14 @@ public: { return false; } - D3D12_CPU_DESCRIPTOR_HANDLE SRV() const override { return _srv.CPU(); } - D3D12_CPU_DESCRIPTOR_HANDLE UAV() const override { return _uav.CPU(); } - ResourceOwnerDX12* GetResourceOwner() const override { return _owner; @@ -173,19 +170,16 @@ public: /// /// Gets buffer size in a GPU memory in bytes. /// - /// Size in bytes. uint64 GetSizeInBytes() const; /// /// Gets buffer location in a GPU memory. /// - /// GPU memory location. D3D12_GPU_VIRTUAL_ADDRESS GetLocation() const; /// /// Gets the counter resource. /// - /// The internal counter buffer. FORCE_INLINE GPUBufferDX12* GetCounter() const { return _counter; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h index ee6e69002..46dab6b57 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h @@ -100,18 +100,16 @@ public: public: /// - /// Gets DX12 device + /// Gets DX12 device. /// - /// DirectX 12 device FORCE_INLINE ID3D12Device* GetDevice() const { return _device; } /// - /// Gets DXGI factory + /// Gets DXGI factory. /// - /// DXGI factory object FORCE_INLINE IDXGIFactory4* GetDXGIFactory() const { return _factoryDXGI; @@ -120,28 +118,24 @@ public: /// /// Gets DirectX 12 command list object /// - /// Command list object (DirectX 12) ID3D12GraphicsCommandList* GetCommandList() const; /// - /// Gets command queue + /// Gets command queue. /// - /// Command queue FORCE_INLINE CommandQueueDX12* GetCommandQueue() const { return _commandQueue; } /// - /// Gets DirectX 12 command queue object + /// Gets DirectX 12 command queue object. /// - /// Command queue object (DirectX 12) ID3D12CommandQueue* GetCommandQueueDX12() const; /// - /// Gets root signature of the graphics pipeline + /// Gets root signature of the graphics pipeline. /// - /// Root signature FORCE_INLINE ID3D12RootSignature* GetRootSignature() const { return _rootSignature; @@ -150,7 +144,6 @@ public: /// /// Gets main commands context (for DirectX 12) /// - /// Main context FORCE_INLINE GPUContextDX12* GetMainContextDX12() const { return _mainContext; @@ -171,9 +164,7 @@ public: static FORCE_INLINE uint32 GetMaxMSAAQuality(uint32 sampleCount) { if (sampleCount <= 8) - { return 0; - } return 0xffffffff; } @@ -196,12 +187,10 @@ public: { return reinterpret_cast(_mainContext); } - void* GetNativePtr() const override { return _device; } - bool Init() override; void DrawBegin() override; void RenderEnd() override; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h index e38d5a712..01e23d08c 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h @@ -22,9 +22,6 @@ private: public: - /// - /// Initializes a new instance of the class. - /// GPUTextureViewDX12() { } @@ -45,9 +42,6 @@ public: return *this; } - /// - /// Finalizes an instance of the class. - /// ~GPUTextureViewDX12() { Release(); @@ -155,8 +149,7 @@ public: /// /// Gets the CPU handle to the render target view descriptor. /// - /// The CPU handle to the render target view descriptor. - D3D12_CPU_DESCRIPTOR_HANDLE RTV() const + FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE RTV() const { return _rtv.CPU(); } @@ -164,8 +157,7 @@ public: /// /// Gets the CPU handle to the depth stencil view descriptor. /// - /// The CPU handle to the depth stencil view descriptor. - D3D12_CPU_DESCRIPTOR_HANDLE DSV() const + FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE DSV() const { return _dsv.CPU(); } @@ -183,17 +175,14 @@ public: { return _dsv.IsValid(); } - D3D12_CPU_DESCRIPTOR_HANDLE SRV() const override { return _srv.CPU(); } - D3D12_CPU_DESCRIPTOR_HANDLE UAV() const override { return _uav.CPU(); } - ResourceOwnerDX12* GetResourceOwner() const override { return _owner; @@ -223,11 +212,6 @@ private: public: - /// - /// Initializes a new instance of the class. - /// - /// The device. - /// The name. GPUTextureDX12(GPUDeviceDX12* device, const StringView& name) : GPUResourceDX12(device, name) { @@ -244,35 +228,29 @@ public: { return (GPUTextureView*)&_handlesPerSlice[arrayOrDepthIndex]; } - GPUTextureView* View(int32 arrayOrDepthIndex, int32 mipMapIndex) const override { return (GPUTextureView*)&_handlesPerMip[arrayOrDepthIndex][mipMapIndex]; } - GPUTextureView* ViewArray() const override { ASSERT(ArraySize() > 1); return (GPUTextureView*)&_handleArray; } - GPUTextureView* ViewVolume() const override { ASSERT(IsVolume()); return (GPUTextureView*)&_handleVolume; } - GPUTextureView* ViewReadOnlyDepth() const override { ASSERT(_desc.Flags & GPUTextureFlags::ReadOnlyDepthView); return (GPUTextureView*)&_handleReadOnlyDepth; } - void* GetNativePtr() const override { - return (void*)nullptr; + return (void*)_resource; } - bool GetData(int32 arrayOrDepthSliceIndex, int32 mipMapIndex, TextureMipData& data, uint32 mipRowPitch) override; // [ResourceOwnerDX12] @@ -286,17 +264,14 @@ public: { return (_desc.Flags & GPUTextureFlags::DepthStencil) != 0; } - D3D12_CPU_DESCRIPTOR_HANDLE SRV() const override { return _srv.CPU(); } - D3D12_CPU_DESCRIPTOR_HANDLE UAV() const override { return _uav.CPU(); } - ResourceOwnerDX12* GetResourceOwner() const override { return (ResourceOwnerDX12*)this; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/IShaderResourceDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/IShaderResourceDX12.h index 155b018e3..725f1d584 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/IShaderResourceDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/IShaderResourceDX12.h @@ -36,25 +36,21 @@ public: /// /// Determines whether this resource is depth/stencil buffer. /// - /// True if this resource is depth/stencil buffer, otherwise false. virtual bool IsDepthStencilResource() const = 0; /// /// Gets CPU handle to the shader resource view descriptor. /// - /// SRV virtual D3D12_CPU_DESCRIPTOR_HANDLE SRV() const = 0; /// /// Gets CPU handle to the unordered access view descriptor. /// - /// UAV virtual D3D12_CPU_DESCRIPTOR_HANDLE UAV() const = 0; /// /// Gets the resource owner. /// - /// Owner object. virtual ResourceOwnerDX12* GetResourceOwner() const = 0; }; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp index a7bbb6f2c..b7916f9ca 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/QueryHeapDX12.cpp @@ -85,27 +85,15 @@ void QueryHeapDX12::Destroy() void QueryHeapDX12::EndQueryBatchAndResolveQueryData(GPUContextDX12* context) { ASSERT(_currentBatch.Open); - - // Skip empty batches if (_currentBatch.Count == 0) - { return; - } // Close the current batch _currentBatch.Open = false; // Resolve the batch const int32 offset = _currentBatch.Start * _resultSize; - const int32 size = _currentBatch.Count * _resultSize; - context->GetCommandList()->ResolveQueryData( - _queryHeap, - _queryType, - _currentBatch.Start, - _currentBatch.Count, - _resultBuffer, - offset - ); + context->GetCommandList()->ResolveQueryData(_queryHeap, _queryType, _currentBatch.Start, _currentBatch.Count, _resultBuffer, offset); _currentBatch.Sync = _device->GetCommandQueue()->GetSyncPoint(); // Begin a new query batch @@ -199,8 +187,7 @@ void* QueryHeapDX12::ResolveQuery(ElementHandle& handle) Platform::MemoryCopy(_resultData.Get() + range.Begin, (byte*)mapped + range.Begin, batch.Count * _resultSize); // Unmap with an empty range to indicate nothing was written by the CPU - range.Begin = range.End = 0; - _resultBuffer->Unmap(0, &range); + _resultBuffer->Unmap(0, nullptr); // All elements got its results so we can remove this batch _batches.RemoveAt(i); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/ResourceOwnerDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/ResourceOwnerDX12.h index c3e241f8d..8b140a1c7 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/ResourceOwnerDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/ResourceOwnerDX12.h @@ -35,7 +35,7 @@ public: /// The current resource state. /// the destination resource state. /// True if need to perform a transition, otherwise false. - static bool IsTransitionNeeded(D3D12_RESOURCE_STATES currentState, D3D12_RESOURCE_STATES targetState) + FORCE_INLINE static bool IsTransitionNeeded(D3D12_RESOURCE_STATES currentState, D3D12_RESOURCE_STATES targetState) { return currentState != targetState && ((currentState | targetState) != currentState || targetState == D3D12_RESOURCE_STATE_COMMON); } @@ -81,7 +81,6 @@ public: /// /// Gets the subresources count. /// - /// The subresources count. FORCE_INLINE uint32 GetSubresourcesCount() const { return _subresourcesCount; @@ -90,7 +89,6 @@ public: /// /// Gets DirectX 12 resource object handle /// - /// DirectX 12 resource object handle FORCE_INLINE ID3D12Resource* GetResource() const { return _resource; @@ -99,7 +97,6 @@ public: /// /// Gets resource owner object as a GPUResource type or returns null if cannot perform cast. /// - /// GPU Resource or null if cannot cast. virtual GPUResource* AsGPUResource() const = 0; protected: diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.cpp index c4680b57e..c00b5bf58 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.cpp @@ -60,6 +60,7 @@ DynamicAllocation UploadBufferDX12::Allocate(uint64 size, uint64 align) // Move in the page _currentOffset += size; + ASSERT(_currentPage->GetResource()); return result; } @@ -67,12 +68,8 @@ bool UploadBufferDX12::UploadBuffer(GPUContextDX12* context, ID3D12Resource* buf { // Allocate data const DynamicAllocation allocation = Allocate(size, 4); - - // Check if allocation is invalid if (allocation.IsInvalid()) - { return true; - } // Copy data Platform::MemoryCopy(allocation.CPUAddress, data, static_cast(size)); @@ -85,10 +82,8 @@ bool UploadBufferDX12::UploadBuffer(GPUContextDX12* context, ID3D12Resource* buf bool UploadBufferDX12::UploadTexture(GPUContextDX12* context, ID3D12Resource* texture, const void* srcData, uint32 srcRowPitch, uint32 srcSlicePitch, int32 mipIndex, int32 arrayIndex) { - // Cache resource info D3D12_RESOURCE_DESC resourceDesc = texture->GetDesc(); const UINT subresourceIndex = RenderToolsDX::CalcSubresourceIndex(mipIndex, arrayIndex, resourceDesc.MipLevels); - D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint; uint32 numRows; uint64 rowPitchAligned, mipSizeAligned; @@ -96,16 +91,8 @@ bool UploadBufferDX12::UploadTexture(GPUContextDX12* context, ID3D12Resource* te rowPitchAligned = footprint.Footprint.RowPitch; mipSizeAligned = rowPitchAligned * footprint.Footprint.Height; - // Destination texture copy location description - D3D12_TEXTURE_COPY_LOCATION dstLocation; - dstLocation.pResource = texture; - dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; - dstLocation.SubresourceIndex = subresourceIndex; - // Allocate data const DynamicAllocation allocation = Allocate(mipSizeAligned, D3D12_TEXTURE_DATA_PLACEMENT_ALIGNMENT); - - // Check if allocation is invalid if (allocation.Size != mipSizeAligned) return true; @@ -131,6 +118,12 @@ bool UploadBufferDX12::UploadTexture(GPUContextDX12* context, ID3D12Resource* te } } + // Destination texture copy location description + D3D12_TEXTURE_COPY_LOCATION dstLocation; + dstLocation.pResource = texture; + dstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + dstLocation.SubresourceIndex = subresourceIndex; + // Source buffer copy location description D3D12_TEXTURE_COPY_LOCATION srcLocation; srcLocation.pResource = allocation.Page->GetResource(); @@ -235,13 +228,7 @@ UploadBufferPageDX12::UploadBufferPageDX12(GPUDeviceDX12* device, uint64 size) resourceDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; resourceDesc.Flags = D3D12_RESOURCE_FLAG_NONE; ID3D12Resource* resource; - VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateCommittedResource( - &heapProperties, - D3D12_HEAP_FLAG_NONE, - &resourceDesc, - D3D12_RESOURCE_STATE_GENERIC_READ, - nullptr, - IID_PPV_ARGS(&resource))); + VALIDATE_DIRECTX_RESULT(_device->GetDevice()->CreateCommittedResource(&heapProperties, D3D12_HEAP_FLAG_NONE, &resourceDesc, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&resource))); // Set state initResource(resource, D3D12_RESOURCE_STATE_GENERIC_READ, 1); @@ -260,6 +247,8 @@ void UploadBufferPageDX12::OnReleaseGPU() { _resource->Unmap(0, nullptr); } + GPUAddress = 0; + CPUAddress = nullptr; // Release releaseResource(); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.h index 6995e5c65..605d3268c 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.h @@ -183,7 +183,6 @@ public: /// /// Gets the current generation number. /// - /// The current generation number. FORCE_INLINE uint64 GetCurrentGeneration() const { return _currentGeneration; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index 69dd0134d..3a3e41b27 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -160,7 +160,6 @@ void GPUContextVulkan::AddImageBarrier(GPUTextureViewVulkan* handle, VkImageLayo for (int32 i = 0; i < state.GetSubresourcesCount(); i++) { const VkImageLayout srcLayout = state.GetSubresourceState(i); - if (srcLayout != dstLayout) { VkImageSubresourceRange range; @@ -175,13 +174,11 @@ void GPUContextVulkan::AddImageBarrier(GPUTextureViewVulkan* handle, VkImageLayo } ASSERT(state.CheckResourceState(dstLayout)); } - state.SetResourceState(dstLayout); } else { const VkImageLayout srcLayout = state.GetSubresourceState(subresourceIndex); - if (srcLayout != dstLayout) { // Transition a single subresource diff --git a/Source/Engine/Renderer/Utils/MultiScaler.cpp b/Source/Engine/Renderer/Utils/MultiScaler.cpp index f38edab35..54995a9d4 100644 --- a/Source/Engine/Renderer/Utils/MultiScaler.cpp +++ b/Source/Engine/Renderer/Utils/MultiScaler.cpp @@ -182,8 +182,8 @@ void MultiScaler::Filter(const FilterMode mode, GPUContext* context, const int32 // Prepare Data data; - data.TexelSize.X = 1.0f / width; - data.TexelSize.Y = 1.0f / height; + data.TexelSize.X = 1.0f / (float)width; + data.TexelSize.Y = 1.0f / (float)height; auto cb = _shader->GetShader()->GetCB(0); context->UpdateCB(cb, &data); context->BindCB(0, cb); @@ -219,8 +219,8 @@ void MultiScaler::DownscaleDepth(GPUContext* context, int32 dstWidth, int32 dstH // Prepare Data data; - data.TexelSize.X = 2.0f / dstWidth; - data.TexelSize.Y = 2.0f / dstHeight; + data.TexelSize.X = 2.0f / (float)dstWidth; + data.TexelSize.Y = 2.0f / (float)dstHeight; auto cb = _shader->GetShader()->GetCB(0); context->UpdateCB(cb, &data); context->BindCB(0, cb); From fa1cd56a0dcea2fe9e195424fea018a5ef263c59 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 7 Jun 2021 11:56:41 +0200 Subject: [PATCH 099/198] Optimize Vulkan shader compiler memory allocation --- .../Engine/ShadersCompilation/ShaderCompiler.cpp | 15 +++++++++++++++ Source/Engine/ShadersCompilation/ShaderCompiler.h | 1 + .../Vulkan/ShaderCompilerVulkan.cpp | 6 +----- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Source/Engine/ShadersCompilation/ShaderCompiler.cpp b/Source/Engine/ShadersCompilation/ShaderCompiler.cpp index 32decb15f..5852a9b65 100644 --- a/Source/Engine/ShadersCompilation/ShaderCompiler.cpp +++ b/Source/Engine/ShadersCompilation/ShaderCompiler.cpp @@ -364,6 +364,21 @@ bool ShaderCompiler::WriteShaderFunctionBegin(ShaderCompilationContext* context, return false; } +bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache, int32 cacheSize) +{ + auto output = context->Output; + + // [Output] Write compiled shader cache + output->WriteUint32(cacheSize + headerSize); + output->WriteBytes(header, headerSize); + output->WriteBytes(cache, cacheSize); + + // [Output] Shader bindings meta + output->Write(&bindings); + + return false; +} + bool ShaderCompiler::WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* cache, int32 cacheSize) { auto output = context->Output; diff --git a/Source/Engine/ShadersCompilation/ShaderCompiler.h b/Source/Engine/ShadersCompilation/ShaderCompiler.h index 43947ab89..1e31ca21e 100644 --- a/Source/Engine/ShadersCompilation/ShaderCompiler.h +++ b/Source/Engine/ShadersCompilation/ShaderCompiler.h @@ -96,6 +96,7 @@ protected: virtual bool OnCompileEnd(); static bool WriteShaderFunctionBegin(ShaderCompilationContext* context, ShaderFunctionMeta& meta); + static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* header, int32 headerSize, const void* cache, int32 cacheSize); static bool WriteShaderFunctionPermutation(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const ShaderBindings& bindings, const void* cache, int32 cacheSize); static bool WriteShaderFunctionEnd(ShaderCompilationContext* context, ShaderFunctionMeta& meta); static bool WriteCustomDataVS(ShaderCompilationContext* context, ShaderFunctionMeta& meta, int32 permutationIndex, const Array& macros); diff --git a/Source/Engine/ShadersCompilation/Vulkan/ShaderCompilerVulkan.cpp b/Source/Engine/ShadersCompilation/Vulkan/ShaderCompilerVulkan.cpp index 998cc4291..597fd0b39 100644 --- a/Source/Engine/ShadersCompilation/Vulkan/ShaderCompilerVulkan.cpp +++ b/Source/Engine/ShadersCompilation/Vulkan/ShaderCompilerVulkan.cpp @@ -801,12 +801,8 @@ bool ShaderCompilerVulkan::CompileShader(ShaderFunctionMeta& meta, WritePermutat int32 spirvBytesCount = (int32)spirv.size() * sizeof(unsigned); header.Type = SpirvShaderHeader::Types::Raw; - Array data; - data.Resize(sizeof(header) + spirvBytesCount); - Platform::MemoryCopy(data.Get(), &header, sizeof(header)); - Platform::MemoryCopy(data.Get() + sizeof(header), &spirv[0], spirvBytesCount); - if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, data.Get(), data.Count())) + if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, &header, sizeof(header), &spirv[0], spirvBytesCount)) return true; if (customDataWrite && customDataWrite(_context, meta, permutationIndex, _macros)) From f4c8808d19b4343fcfafb2750dbb60a6d43ccba9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 7 Jun 2021 12:19:49 +0200 Subject: [PATCH 100/198] Add SRV slots isage info for D3D12 shaders cache --- Source/Engine/Graphics/Shaders/GPUShader.h | 2 +- .../DirectX/DX12/GPUPipelineStateDX12.cpp | 64 ++++++- .../DirectX/DX12/GPUPipelineStateDX12.h | 9 +- .../DirectX/DX12/GPUShaderDX12.cpp | 18 +- .../DirectX/DX12/GPUShaderProgramDX12.h | 87 +++------ .../GraphicsDevice/DirectX/DX12/Types.h | 19 ++ .../DirectX/ShaderCompilerDX.cpp | 178 ++++++++---------- .../ShadersCompilation/ShaderCompiler.cpp | 2 +- 8 files changed, 193 insertions(+), 186 deletions(-) create mode 100644 Source/Engine/GraphicsDevice/DirectX/DX12/Types.h diff --git a/Source/Engine/Graphics/Shaders/GPUShader.h b/Source/Engine/Graphics/Shaders/GPUShader.h index 76c8df546..66f57b207 100644 --- a/Source/Engine/Graphics/Shaders/GPUShader.h +++ b/Source/Engine/Graphics/Shaders/GPUShader.h @@ -12,7 +12,7 @@ class GPUShaderProgram; /// /// The runtime version of the shaders cache supported by the all graphics back-ends. The same for all the shader cache formats (easier to sync and validate). /// -#define GPU_SHADER_CACHE_VERSION 7 +#define GPU_SHADER_CACHE_VERSION 8 /// /// Represents collection of shader programs with permutations and custom names. diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.cpp index 5c18c2e2d..50940d46d 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.cpp @@ -3,6 +3,7 @@ #if GRAPHICS_API_DIRECTX12 #include "GPUPipelineStateDX12.h" +#include "GPUShaderProgramDX12.h" #include "GPUTextureDX12.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/GraphicsDevice/DirectX/RenderToolsDX.h" @@ -63,6 +64,44 @@ ID3D12PipelineState* GPUPipelineStateDX12::GetState(GPUTextureViewDX12* depth, i LOG_DIRECTX_RESULT(result); if (FAILED(result)) return nullptr; +#if GPU_ENABLE_RESOURCE_NAMING + char name[200]; + int32 nameLen = 0; + if (DebugDesc.VS) + { + Platform::MemoryCopy(name + nameLen, *DebugDesc.VS->GetName(), DebugDesc.VS->GetName().Length()); + nameLen += DebugDesc.VS->GetName().Length(); + name[nameLen++] = '+'; + } + if (DebugDesc.HS) + { + Platform::MemoryCopy(name + nameLen, *DebugDesc.HS->GetName(), DebugDesc.HS->GetName().Length()); + nameLen += DebugDesc.HS->GetName().Length(); + name[nameLen++] = '+'; + } + if (DebugDesc.DS) + { + Platform::MemoryCopy(name + nameLen, *DebugDesc.DS->GetName(), DebugDesc.DS->GetName().Length()); + nameLen += DebugDesc.DS->GetName().Length(); + name[nameLen++] = '+'; + } + if (DebugDesc.GS) + { + Platform::MemoryCopy(name + nameLen, *DebugDesc.GS->GetName(), DebugDesc.GS->GetName().Length()); + nameLen += DebugDesc.GS->GetName().Length(); + name[nameLen++] = '+'; + } + if (DebugDesc.PS) + { + Platform::MemoryCopy(name + nameLen, *DebugDesc.PS->GetName(), DebugDesc.PS->GetName().Length()); + nameLen += DebugDesc.PS->GetName().Length(); + name[nameLen++] = '+'; + } + if (nameLen && name[nameLen - 1] == '+') + nameLen--; + name[nameLen] = '\0'; + SetDebugObjectName(state, name); +#endif // Cache it _states.Add(key, state); @@ -89,16 +128,23 @@ bool GPUPipelineStateDX12::Init(const Description& desc) psDesc.pRootSignature = _device->GetRootSignature(); // Shaders + Platform::MemoryClear(&Header, sizeof(Header)); psDesc.InputLayout = { static_cast(desc.VS->GetInputLayout()), desc.VS->GetInputLayoutSize() }; - psDesc.VS = { desc.VS->GetBufferHandle(), desc.VS->GetBufferSize() }; - if (desc.HS) - psDesc.HS = { desc.HS->GetBufferHandle(), desc.HS->GetBufferSize() }; - if (desc.DS) - psDesc.DS = { desc.DS->GetBufferHandle(), desc.DS->GetBufferSize() }; - if (desc.GS) - psDesc.GS = { desc.GS->GetBufferHandle(), desc.GS->GetBufferSize() }; - if (desc.PS) - psDesc.PS = { desc.PS->GetBufferHandle(), desc.PS->GetBufferSize() }; +#define INIT_SHADER_STAGE(stage, type) \ + if (desc.stage) \ + { \ + psDesc.stage = { desc.stage->GetBufferHandle(), desc.stage->GetBufferSize() }; \ + auto shader = (type*)desc.stage; \ + auto srCount = Math::FloorLog2(shader->GetBindings().UsedSRsMask) + 1; \ + for (uint32 i = 0; i < srCount; i++) \ + if (shader->Header.SrDimensions[i]) \ + Header.SrDimensions[i] = shader->Header.SrDimensions[i]; \ + } + INIT_SHADER_STAGE(HS, GPUShaderProgramHSDX12); + INIT_SHADER_STAGE(DS, GPUShaderProgramDSDX12); + INIT_SHADER_STAGE(GS, GPUShaderProgramGSDX12); + INIT_SHADER_STAGE(VS, GPUShaderProgramVSDX12); + INIT_SHADER_STAGE(PS, GPUShaderProgramPSDX12); const static D3D12_PRIMITIVE_TOPOLOGY_TYPE primTypes1[] = { D3D12_PRIMITIVE_TOPOLOGY_TYPE_UNDEFINED, diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.h index 020d03d82..df6850d6e 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.h @@ -6,6 +6,7 @@ #include "Engine/Graphics/GPUPipelineState.h" #include "GPUDeviceDX12.h" +#include "Types.h" #include "../IncludeDirectXHeaders.h" class GPUTextureViewDX12; @@ -50,18 +51,12 @@ private: public: - /// - /// Init - /// - /// Graphics Device GPUPipelineStateDX12(GPUDeviceDX12* device); public: - /// - /// Direct3D primitive topology - /// D3D_PRIMITIVE_TOPOLOGY PrimitiveTopologyType = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; + DxShaderHeader Header; /// /// Gets DirectX 12 graphics pipeline state object for the given rendering state. Uses depth buffer and render targets formats and multi-sample levels to setup a proper PSO. Uses caching. diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderDX12.cpp index 61a52c0e7..cfcc43e8d 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderDX12.cpp @@ -5,10 +5,16 @@ #include "GPUShaderDX12.h" #include "Engine/Serialization/MemoryReadStream.h" #include "GPUShaderProgramDX12.h" +#include "Types.h" #include "../RenderToolsDX.h" GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize, MemoryReadStream& stream) { + // Extract the DX shader header from the cache + DxShaderHeader* header = (DxShaderHeader*)cacheBytes; + cacheBytes += sizeof(DxShaderHeader); + cacheSize -= sizeof(DxShaderHeader); + GPUShaderProgram* shader = nullptr; switch (type) { @@ -87,34 +93,34 @@ GPUShaderProgram* GPUShaderDX12::CreateGPUShaderProgram(ShaderStage type, const } // Create object - shader = New(initializer, cacheBytes, cacheSize, inputLayout, inputLayoutSize); + shader = New(initializer, header, cacheBytes, cacheSize, inputLayout, inputLayoutSize); break; } case ShaderStage::Hull: { int32 controlPointsCount; stream.ReadInt32(&controlPointsCount); - shader = New(initializer, cacheBytes, cacheSize, controlPointsCount); + shader = New(initializer, header, cacheBytes, cacheSize, controlPointsCount); break; } case ShaderStage::Domain: { - shader = New(initializer, cacheBytes, cacheSize); + shader = New(initializer, header, cacheBytes, cacheSize); break; } case ShaderStage::Geometry: { - shader = New(initializer, cacheBytes, cacheSize); + shader = New(initializer, header, cacheBytes, cacheSize); break; } case ShaderStage::Pixel: { - shader = New(initializer, cacheBytes, cacheSize); + shader = New(initializer, header, cacheBytes, cacheSize); break; } case ShaderStage::Compute: { - shader = New(_device, initializer, cacheBytes, cacheSize); + shader = New(_device, initializer, header, cacheBytes, cacheSize); break; } } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderProgramDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderProgramDX12.h index 11f16d5c2..1be80d842 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderProgramDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUShaderProgramDX12.h @@ -6,6 +6,7 @@ #include "GPUDeviceDX12.h" #include "Engine/Graphics/Shaders/GPUShaderProgram.h" +#include "Types.h" #include "../IncludeDirectXHeaders.h" /// @@ -20,18 +21,18 @@ protected: public: - /// - /// Initializes a new instance of the class. - /// - /// The program initialization data. - /// The shader data. - /// The shader data size. - GPUShaderProgramDX12(const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize) + + GPUShaderProgramDX12(const GPUShaderProgramInitializer& initializer, DxShaderHeader* header, byte* cacheBytes, uint32 cacheSize) + : Header(*header) { BaseType::Init(initializer); _data.Set(cacheBytes, cacheSize); } +public: + + DxShaderHeader Header; + public: // [BaseType] @@ -39,7 +40,6 @@ public: { return (void*)_data.Get(); } - uint32 GetBufferSize() const override { return _data.Count(); @@ -58,16 +58,8 @@ private: public: - /// - /// Initializes a new instance of the class. - /// - /// The program initialization data. - /// The shader data. - /// The shader data size. - /// The input layout description. - /// The input layout description size. - GPUShaderProgramVSDX12(const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize, D3D12_INPUT_ELEMENT_DESC* inputLayout, byte inputLayoutSize) - : GPUShaderProgramDX12(initializer, cacheBytes, cacheSize) + GPUShaderProgramVSDX12(const GPUShaderProgramInitializer& initializer, DxShaderHeader* header, byte* cacheBytes, uint32 cacheSize, D3D12_INPUT_ELEMENT_DESC* inputLayout, byte inputLayoutSize) + : GPUShaderProgramDX12(initializer, header, cacheBytes, cacheSize) , _inputLayoutSize(inputLayoutSize) { for (byte i = 0; i < inputLayoutSize; i++) @@ -81,7 +73,6 @@ public: { return (void*)_inputLayout; } - byte GetInputLayoutSize() const override { return _inputLayoutSize; @@ -95,15 +86,8 @@ class GPUShaderProgramHSDX12 : public GPUShaderProgramDX12 { public: - /// - /// Initializes a new instance of the class. - /// - /// The program initialization data. - /// The shader data. - /// The shader data size. - /// The control points used by the hull shader for processing. - GPUShaderProgramHSDX12(const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize, int32 controlPointsCount) - : GPUShaderProgramDX12(initializer, cacheBytes, cacheSize) + GPUShaderProgramHSDX12(const GPUShaderProgramInitializer& initializer, DxShaderHeader* header, byte* cacheBytes, uint32 cacheSize, int32 controlPointsCount) + : GPUShaderProgramDX12(initializer, header, cacheBytes, cacheSize) { _controlPointsCount = controlPointsCount; } @@ -116,13 +100,8 @@ class GPUShaderProgramDSDX12 : public GPUShaderProgramDX12 { public: - /// - /// Initializes a new instance of the class. - /// - /// The program initialization data. - /// The shader data size. - GPUShaderProgramDSDX12(const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize) - : GPUShaderProgramDX12(initializer, cacheBytes, cacheSize) + GPUShaderProgramDSDX12(const GPUShaderProgramInitializer& initializer, DxShaderHeader* header, byte* cacheBytes, uint32 cacheSize) + : GPUShaderProgramDX12(initializer, header, cacheBytes, cacheSize) { } }; @@ -134,14 +113,8 @@ class GPUShaderProgramGSDX12 : public GPUShaderProgramDX12 { public: - /// - /// Initializes a new instance of the class. - /// - /// The program initialization data. - /// The shader data. - /// The shader data size. - GPUShaderProgramGSDX12(const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize) - : GPUShaderProgramDX12(initializer, cacheBytes, cacheSize) + GPUShaderProgramGSDX12(const GPUShaderProgramInitializer& initializer, DxShaderHeader* header, byte* cacheBytes, uint32 cacheSize) + : GPUShaderProgramDX12(initializer, header, cacheBytes, cacheSize) { } }; @@ -153,14 +126,8 @@ class GPUShaderProgramPSDX12 : public GPUShaderProgramDX12 { public: - /// - /// Initializes a new instance of the class. - /// - /// The program initialization data. - /// The shader data. - /// The shader data size. - GPUShaderProgramPSDX12(const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize) - : GPUShaderProgramDX12(initializer, cacheBytes, cacheSize) + GPUShaderProgramPSDX12(const GPUShaderProgramInitializer& initializer, DxShaderHeader* header, byte* cacheBytes, uint32 cacheSize) + : GPUShaderProgramDX12(initializer, header, cacheBytes, cacheSize) { } }; @@ -178,23 +145,13 @@ private: public: - /// - /// Initializes a new instance of the class. - /// - /// The graphics device. - /// The program initialization data. - /// The shader data. - /// The shader data size. - GPUShaderProgramCSDX12(GPUDeviceDX12* device, const GPUShaderProgramInitializer& initializer, byte* cacheBytes, uint32 cacheSize) - : GPUShaderProgramDX12(initializer, cacheBytes, cacheSize) + GPUShaderProgramCSDX12(GPUDeviceDX12* device, const GPUShaderProgramInitializer& initializer, DxShaderHeader* header, byte* cacheBytes, uint32 cacheSize) + : GPUShaderProgramDX12(initializer, header, cacheBytes, cacheSize) , _device(device) , _state(nullptr) { } - - /// - /// Destructor - /// + ~GPUShaderProgramCSDX12() { _device->AddResourceToLateRelease(_state); @@ -205,7 +162,6 @@ public: /// /// Gets DirectX 12 compute pipeline state object /// - /// DirectX 12 compute pipeline state object FORCE_INLINE ID3D12PipelineState* GetState() const { return _state; @@ -214,7 +170,6 @@ public: /// /// Gets or creates compute pipeline state for that compute shader. /// - /// DirectX 12 compute pipeline state object ID3D12PipelineState* GetOrCreateState(); }; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/Types.h b/Source/Engine/GraphicsDevice/DirectX/DX12/Types.h new file mode 100644 index 000000000..a4f49d793 --- /dev/null +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/Types.h @@ -0,0 +1,19 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#if COMPILE_WITH_DX_SHADER_COMPILER || GRAPHICS_API_DIRECTX12 + +#include "../IncludeDirectXHeaders.h" + +struct DxShaderHeader +{ + /// + /// The SRV dimensions per-slot. + /// + byte SrDimensions[32]; + + // .. rest is just a actual data array +}; + +#endif diff --git a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp index 1f58dce93..e557a1b98 100644 --- a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp +++ b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp @@ -6,6 +6,7 @@ #include "Engine/Core/Log.h" #include "Engine/Threading/Threading.h" #include "Engine/Graphics/Config.h" +#include "Engine/GraphicsDevice/DirectX/DX12/Types.h" #include "Engine/Utilities/StringConverter.h" #include "Engine/Platform/Win32/IncludeWindowsHeaders.h" #include "Engine/Platform/Windows/ComPtr.h" @@ -112,99 +113,6 @@ ShaderCompilerDX::~ShaderCompilerDX() containerReflection->Release(); } -namespace -{ - bool ProcessShader(ShaderCompilationContext* context, Array& constantBuffers, ID3D12ShaderReflection* shaderReflection, D3D12_SHADER_DESC& desc, ShaderBindings& bindings) - { - // Extract constant buffers usage information - for (uint32 a = 0; a < desc.ConstantBuffers; a++) - { - // Get CB - auto cb = shaderReflection->GetConstantBufferByIndex(a); - - // Get CB description - D3D12_SHADER_BUFFER_DESC cbDesc; - cb->GetDesc(&cbDesc); - - // Check buffer type - if (cbDesc.Type == D3D_CT_CBUFFER) - { - // Find CB slot index - int32 slot = INVALID_INDEX; - for (uint32 b = 0; b < desc.BoundResources; b++) - { - D3D12_SHADER_INPUT_BIND_DESC bDesc; - shaderReflection->GetResourceBindingDesc(b, &bDesc); - if (StringUtils::Compare(bDesc.Name, cbDesc.Name) == 0) - { - slot = bDesc.BindPoint; - break; - } - } - if (slot == INVALID_INDEX) - { - context->OnError("Missing bound resource."); - return true; - } - - // Set flag - bindings.UsedCBsMask |= 1 << slot; - - // Try to add CB to the list - for (int32 b = 0; b < constantBuffers.Count(); b++) - { - auto& cc = constantBuffers[b]; - if (cc.Slot == slot) - { - cc.IsUsed = true; - cc.Size = cbDesc.Size; - break; - } - } - } - } - - // Extract resources usage - for (uint32 i = 0; i < desc.BoundResources; i++) - { - // Get resource description - D3D12_SHADER_INPUT_BIND_DESC resDesc; - shaderReflection->GetResourceBindingDesc(i, &resDesc); - - switch (resDesc.Type) - { - // Sampler - case D3D_SIT_SAMPLER: - break; - - // Constant Buffer - case D3D_SIT_CBUFFER: - case D3D_SIT_TBUFFER: - break; - - // Shader Resource - case D3D_SIT_TEXTURE: - case D3D_SIT_STRUCTURED: - case D3D_SIT_BYTEADDRESS: - bindings.UsedSRsMask |= 1 << resDesc.BindPoint; - break; - - // Unordered Access - case D3D_SIT_UAV_RWTYPED: - case D3D_SIT_UAV_RWSTRUCTURED: - case D3D_SIT_UAV_RWBYTEADDRESS: - case D3D_SIT_UAV_APPEND_STRUCTURED: - case D3D_SIT_UAV_CONSUME_STRUCTURED: - case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER: - bindings.UsedUAsMask |= 1 << resDesc.BindPoint; - break; - } - } - - return false; - } -} - bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationData customDataWrite) { if (WriteShaderFunctionBegin(_context, meta)) @@ -393,11 +301,89 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD shaderReflection->GetDesc(&desc); // Process shader reflection data + DxShaderHeader header; + Platform::MemoryClear(&header, sizeof(header)); ShaderBindings bindings = { desc.InstructionCount, 0, 0, 0 }; - if (ProcessShader(_context, _constantBuffers, shaderReflection.Get(), desc, bindings)) - return true; + for (uint32 a = 0; a < desc.ConstantBuffers; a++) + { + auto cb = shaderReflection->GetConstantBufferByIndex(a); + D3D12_SHADER_BUFFER_DESC cbDesc; + cb->GetDesc(&cbDesc); + if (cbDesc.Type == D3D_CT_CBUFFER) + { + // Find CB slot index + int32 slot = INVALID_INDEX; + for (uint32 b = 0; b < desc.BoundResources; b++) + { + D3D12_SHADER_INPUT_BIND_DESC bDesc; + shaderReflection->GetResourceBindingDesc(b, &bDesc); + if (StringUtils::Compare(bDesc.Name, cbDesc.Name) == 0) + { + slot = bDesc.BindPoint; + break; + } + } + if (slot == INVALID_INDEX) + { + _context->OnError("Missing bound resource."); + return true; + } - if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, shaderBuffer->GetBufferPointer(), (int32)shaderBuffer->GetBufferSize())) + // Set flag + bindings.UsedCBsMask |= 1 << slot; + + // Try to add CB to the list + for (int32 b = 0; b < _constantBuffers.Count(); b++) + { + auto& cc = _constantBuffers[b]; + if (cc.Slot == slot) + { + cc.IsUsed = true; + cc.Size = cbDesc.Size; + break; + } + } + } + } + for (uint32 i = 0; i < desc.BoundResources; i++) + { + D3D12_SHADER_INPUT_BIND_DESC resDesc; + shaderReflection->GetResourceBindingDesc(i, &resDesc); + switch (resDesc.Type) + { + // Sampler + case D3D_SIT_SAMPLER: + break; + + // Constant Buffer + case D3D_SIT_CBUFFER: + case D3D_SIT_TBUFFER: + break; + + // Shader Resource + case D3D_SIT_TEXTURE: + bindings.UsedSRsMask |= 1 << resDesc.BindPoint; + header.SrDimensions[resDesc.BindPoint] = resDesc.Dimension; + break; + case D3D_SIT_STRUCTURED: + case D3D_SIT_BYTEADDRESS: + bindings.UsedSRsMask |= 1 << resDesc.BindPoint; + header.SrDimensions[resDesc.BindPoint] = D3D_SRV_DIMENSION_BUFFER; + break; + + // Unordered Access + case D3D_SIT_UAV_RWTYPED: + case D3D_SIT_UAV_RWSTRUCTURED: + case D3D_SIT_UAV_RWBYTEADDRESS: + case D3D_SIT_UAV_APPEND_STRUCTURED: + case D3D_SIT_UAV_CONSUME_STRUCTURED: + case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER: + bindings.UsedUAsMask |= 1 << resDesc.BindPoint; + break; + } + } + + if (WriteShaderFunctionPermutation(_context, meta, permutationIndex, bindings, &header, sizeof(header), shaderBuffer->GetBufferPointer(), (int32)shaderBuffer->GetBufferSize())) return true; if (customDataWrite && customDataWrite(_context, meta, permutationIndex, _macros)) diff --git a/Source/Engine/ShadersCompilation/ShaderCompiler.cpp b/Source/Engine/ShadersCompilation/ShaderCompiler.cpp index 5852a9b65..b4db1a10b 100644 --- a/Source/Engine/ShadersCompilation/ShaderCompiler.cpp +++ b/Source/Engine/ShadersCompilation/ShaderCompiler.cpp @@ -80,7 +80,7 @@ bool ShaderCompiler::Compile(ShaderCompilationContext* context) _constantBuffers.Add({ meta->CB[i].Slot, false, 0 }); // [Output] Version number - output->WriteInt32(7); + output->WriteInt32(8); // [Output] Additional data start const int32 additionalDataStartPos = output->GetPosition(); From 39698c769476eee53e5362e0b26cf0d178548c1b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 7 Jun 2021 13:08:39 +0200 Subject: [PATCH 101/198] Fix descriptors bindings to shaders on D3D12 --- .../DirectX/DX12/DescriptorHeapDX12.cpp | 17 +-- .../DirectX/DX12/DescriptorHeapDX12.h | 7 +- .../DirectX/DX12/GPUBufferDX12.cpp | 14 +- .../DirectX/DX12/GPUBufferDX12.h | 25 +--- .../DirectX/DX12/GPUContextDX12.cpp | 134 +++++++++++++----- .../DirectX/DX12/GPUDeviceDX12.cpp | 98 +++++++++---- .../DirectX/DX12/GPUDeviceDX12.h | 5 +- .../DirectX/DX12/GPUSwapChainDX12.cpp | 4 +- .../DirectX/DX12/GPUTextureDX12.cpp | 133 ++++++++++++----- .../DirectX/DX12/GPUTextureDX12.h | 78 +--------- .../DirectX/DX12/IShaderResourceDX12.h | 7 +- 11 files changed, 300 insertions(+), 222 deletions(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.cpp index f07f66edc..dcfaff53a 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.cpp @@ -128,25 +128,14 @@ void DescriptorHeapWithSlotsDX12::ReleaseSlot(uint32 index) value &= ~mask; } -DescriptorHeapPoolDX12::DescriptorHeapPoolDX12(GPUDeviceDX12* device, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 descriptorsCount, bool shaderVisible) +DescriptorHeapPoolDX12::DescriptorHeapPoolDX12(GPUDeviceDX12* device, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 descriptorsCountPerHeap, bool shaderVisible) : _device(device) , _type(type) - , _descriptorsCount(descriptorsCount) + , _descriptorsCountPerHeap(descriptorsCountPerHeap) , _shaderVisible(shaderVisible) { } -void DescriptorHeapPoolDX12::Init() -{ - // Allocate first page - auto heap = New(_device); - if (heap->Create(_type, _descriptorsCount, _shaderVisible)) - { - Platform::Fatal(TEXT("Failed to allocate descriptor heap.")); - } - _heaps.Add(heap); -} - void DescriptorHeapPoolDX12::AllocateSlot(DescriptorHeapWithSlotsDX12*& heap, uint32& slot) { for (int32 i = 0; i < _heaps.Count(); i++) @@ -159,7 +148,7 @@ void DescriptorHeapPoolDX12::AllocateSlot(DescriptorHeapWithSlotsDX12*& heap, ui } heap = New(_device); - if (heap->Create(_type, _descriptorsCount, _shaderVisible)) + if (heap->Create(_type, _descriptorsCountPerHeap, _shaderVisible)) { Platform::Fatal(TEXT("Failed to allocate descriptor heap.")); } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h index f7de6a9d6..e77893b0b 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h @@ -133,17 +133,16 @@ private: GPUDeviceDX12* _device; D3D12_DESCRIPTOR_HEAP_TYPE _type; - uint32 _descriptorsCount; + uint32 _descriptorsCountPerHeap; bool _shaderVisible; - Array _heaps; + Array> _heaps; public: - DescriptorHeapPoolDX12(GPUDeviceDX12* device, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 descriptorsCount, bool shaderVisible); + DescriptorHeapPoolDX12(GPUDeviceDX12* device, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 descriptorsCountPerHeap, bool shaderVisible); public: - void Init(); void AllocateSlot(DescriptorHeapWithSlotsDX12*& heap, uint32& slot); void ReleaseGPU(); }; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp index 73d8111f0..9bcade16e 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.cpp @@ -9,6 +9,16 @@ #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/Async/Tasks/GPUUploadBufferTask.h" +void GPUBufferViewDX12::SetSRV(D3D12_SHADER_RESOURCE_VIEW_DESC& srvDesc) +{ + _srv.CreateSRV(_device, _owner->GetResource(), &srvDesc); +} + +void GPUBufferViewDX12::SetUAV(D3D12_UNORDERED_ACCESS_VIEW_DESC& uavDesc, ID3D12Resource* counterResource) +{ + _uav.CreateUAV(_device, _owner->GetResource(), &uavDesc, counterResource); +} + uint64 GPUBufferDX12::GetSizeInBytes() const { return _memoryUsage; @@ -201,7 +211,7 @@ bool GPUBufferDX12::OnInit() } if (_desc.Flags & GPUBufferFlags::RawBuffer) srvDesc.Buffer.Flags |= D3D12_BUFFER_SRV_FLAG_RAW; - _view.SetSRV(&srvDesc); + _view.SetSRV(srvDesc); } if (useUAV) { @@ -220,7 +230,7 @@ bool GPUBufferDX12::OnInit() uavDesc.Format = DXGI_FORMAT_UNKNOWN; else uavDesc.Format = RenderToolsDX::ToDxgiFormat(PixelFormatExtensions::FindUnorderedAccessFormat(_desc.Format)); - _view.SetUAV(&uavDesc, _counter ? _counter->GetResource() : nullptr); + _view.SetUAV(uavDesc, _counter ? _counter->GetResource() : nullptr); } return false; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.h index 7c45dfe5d..48811019a 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.h @@ -27,6 +27,7 @@ public: /// GPUBufferViewDX12() { + SrvDimension = D3D12_SRV_DIMENSION_BUFFER; } /// @@ -65,34 +66,14 @@ public: /// Sets the shader resource view. /// /// The SRV desc. - void SetSRV(D3D12_SHADER_RESOURCE_VIEW_DESC* srvDesc) - { - if (srvDesc) - { - _srv.CreateSRV(_device, _owner->GetResource(), srvDesc); - } - else - { - _srv.Release(); - } - } + void SetSRV(D3D12_SHADER_RESOURCE_VIEW_DESC& srvDesc); /// /// Sets the unordered access view. /// /// The UAV desc. /// The counter buffer resource. - void SetUAV(D3D12_UNORDERED_ACCESS_VIEW_DESC* uavDesc, ID3D12Resource* counterResource = nullptr) - { - if (uavDesc) - { - _uav.CreateUAV(_device, _owner->GetResource(), uavDesc, counterResource); - } - else - { - _uav.Release(); - } - } + void SetUAV(D3D12_UNORDERED_ACCESS_VIEW_DESC& uavDesc, ID3D12Resource* counterResource = nullptr); public: diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index 4d1bd3e17..ed45c8768 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -136,26 +136,36 @@ void GPUContextDX12::AddTransitionBarrier(ResourceOwnerDX12* resource, const D3D void GPUContextDX12::SetResourceState(ResourceOwnerDX12* resource, D3D12_RESOURCE_STATES after, int32 subresourceIndex) { - // Check if resource is missing auto nativeResource = resource->GetResource(); if (nativeResource == nullptr) return; - auto& state = resource->State; - if (subresourceIndex == D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES && !state.AreAllSubresourcesSame()) + if (subresourceIndex == -1) { - // Slow path because we have to transition the entire resource with multiple subresources that aren't in the same state - const uint32 subresourceCount = resource->GetSubresourcesCount(); - for (uint32 i = 0; i < subresourceCount; i++) + if (state.AreAllSubresourcesSame()) { - const D3D12_RESOURCE_STATES before = state.GetSubresourceState(i); - if (before != after) + // Transition entire resource at once + const D3D12_RESOURCE_STATES before = state.GetSubresourceState(subresourceIndex); + if (ResourceStateDX12::IsTransitionNeeded(before, after)) { - AddTransitionBarrier(resource, before, after, i); - state.SetSubresourceState(i, after); + AddTransitionBarrier(resource, before, after, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES); + state.SetSubresourceState(subresourceIndex, after); } } - ASSERT(state.CheckResourceState(after)); + else + { + // Slow path to transition each subresource + for (int32 i = 0; i < state.GetSubresourcesCount(); i++) + { + const D3D12_RESOURCE_STATES before = state.GetSubresourceState(i); + if (ResourceStateDX12::IsTransitionNeeded(before, after)) + { + AddTransitionBarrier(resource, before, after, i); + state.SetSubresourceState(i, after); + } + } + ASSERT(state.CheckResourceState(after)); + } state.SetResourceState(after); } else @@ -163,6 +173,7 @@ void GPUContextDX12::SetResourceState(ResourceOwnerDX12* resource, D3D12_RESOURC const D3D12_RESOURCE_STATES before = state.GetSubresourceState(subresourceIndex); if (ResourceStateDX12::IsTransitionNeeded(before, after)) { + // Transition a single subresource AddTransitionBarrier(resource, before, after, subresourceIndex); state.SetSubresourceState(subresourceIndex, after); } @@ -282,23 +293,25 @@ void GPUContextDX12::flushSRVs() ASSERT(srCount <= GPU_MAX_SR_BINDED); // Fill table with source descriptors + DxShaderHeader& header = _currentCompute ? ((GPUShaderProgramCSDX12*)_currentCompute)->Header : _currentState->Header; D3D12_CPU_DESCRIPTOR_HANDLE srcDescriptorRangeStarts[GPU_MAX_SR_BINDED]; for (uint32 i = 0; i < srCount; i++) { const auto handle = _srHandles[i]; - if (handle != nullptr) + const auto dimensions = (D3D12_SRV_DIMENSION)header.SrDimensions[i]; + if (handle != nullptr && dimensions) { + ASSERT(handle->SrvDimension == dimensions); srcDescriptorRangeStarts[i] = handle->SRV(); - - D3D12_RESOURCE_STATES state = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + // TODO: for setup states based on binding mode + D3D12_RESOURCE_STATES states = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; if (handle->IsDepthStencilResource()) - state |= D3D12_RESOURCE_STATE_DEPTH_READ; - - SetResourceState(handle->GetResourceOwner(), state, handle->SubresourceIndex); + states |= D3D12_RESOURCE_STATE_DEPTH_READ; + SetResourceState(handle->GetResourceOwner(), states, handle->SubresourceIndex); } else { - srcDescriptorRangeStarts[i] = _device->NullSRV(); + srcDescriptorRangeStarts[i] = _device->NullSRV(dimensions); } } @@ -306,10 +319,7 @@ void GPUContextDX12::flushSRVs() auto allocation = _device->RingHeap_CBV_SRV_UAV.AllocateTable(srCount); // Copy descriptors - _device->GetDevice()->CopyDescriptors( - 1, &allocation.CPU, &srCount, - srCount, srcDescriptorRangeStarts, nullptr, - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + _device->GetDevice()->CopyDescriptors(1, &allocation.CPU, &srCount, srCount, srcDescriptorRangeStarts, nullptr, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); // Flush SRV descriptors table if (_isCompute) @@ -400,10 +410,7 @@ void GPUContextDX12::flushUAVs() auto allocation = _device->RingHeap_CBV_SRV_UAV.AllocateTable(uaCount); // Copy descriptors - _device->GetDevice()->CopyDescriptors( - 1, &allocation.CPU, &uaCount, - uaCount, srcDescriptorRangeStarts, nullptr, - D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + _device->GetDevice()->CopyDescriptors(1, &allocation.CPU, &uaCount, uaCount, srcDescriptorRangeStarts, nullptr, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); // Flush UAV descriptors table if (_isCompute) @@ -444,8 +451,8 @@ void GPUContextDX12::flushRBs() if (_rbBufferSize > 0) { #if DX12_ENABLE_RESOURCE_BARRIERS_DEBUGGING - const auto info = String::Format(TEXT("[DX12 Resource Barrier]: Flush {0} barriers"), _rbBufferSize); - Log::Logger::Write(LogType::Info, info); + const auto info = String::Format(TEXT("[DX12 Resource Barrier]: Flush {0} barriers"), _rbBufferSize); + Log::Logger::Write(LogType::Info, info); #endif // Flush resource barriers @@ -490,6 +497,32 @@ void GPUContextDX12::onDrawCall() SetResourceState(_ibHandle, D3D12_RESOURCE_STATE_INDEX_BUFFER); } + // If SRV resource is not binded to RTV then transition it to the whole state (GPU-BASED VALIDATION complains about it) + for (uint32 i = 0; i < GPU_MAX_SR_BINDED; i++) + { + const auto handle = _srHandles[i]; + if (handle != nullptr && handle->GetResourceOwner()) + { + const auto resourceOwner = handle->GetResourceOwner(); + bool isRtv = false; + for (int32 j = 0; j < _rtCount; j++) + { + if (_rtHandles[j] && _rtHandles[j]->GetResourceOwner() == resourceOwner) + { + isRtv = true; + break; + } + } + if (!isRtv) + { + D3D12_RESOURCE_STATES states = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + if (handle->IsDepthStencilResource()) + states |= D3D12_RESOURCE_STATE_DEPTH_READ; + SetResourceState(handle->GetResourceOwner(), states); + } + } + } + // Flush flushSRVs(); flushRTVs(); @@ -497,6 +530,43 @@ void GPUContextDX12::onDrawCall() flushRBs(); flushPS(); flushCBs(); + +#if BUILD_DEBUG + // Additional verification of the state + for (int32 i = 0; i < _rtCount; i++) + { + const auto handle = _rtHandles[i]; + if (handle != nullptr && handle->GetResourceOwner()) + { + const auto& state = handle->GetResourceOwner()->State; + ASSERT((state.GetSubresourceState(handle->SubresourceIndex) & D3D12_RESOURCE_STATE_RENDER_TARGET) != 0); + } + } + const uint32 srMask = _currentState->GetUsedSRsMask(); + const uint32 srCount = Math::FloorLog2(srMask) + 1; + for (uint32 i = 0; i < srCount; i++) + { + const auto handle = _srHandles[i]; + if (handle != nullptr && handle->GetResourceOwner()) + { + const auto& state = handle->GetResourceOwner()->State; + bool isRtv = false; + for (int32 j = 0; j < _rtCount; j++) + { + if (_rtHandles[j] && _rtHandles[j]->GetResourceOwner() == handle->GetResourceOwner()) + { + isRtv = true; + break; + } + } + ASSERT((state.GetSubresourceState(handle->SubresourceIndex) & D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) != 0); + if (!isRtv) + { + ASSERT(state.AreAllSubresourcesSame()); + } + } + } +#endif } void GPUContextDX12::FrameBegin() @@ -722,10 +792,8 @@ void GPUContextDX12::BindCB(int32 slot, GPUConstantBuffer* cb) void GPUContextDX12::BindSR(int32 slot, GPUResourceView* view) { ASSERT(slot >= 0 && slot < GPU_MAX_SR_BINDED); - auto handle = view ? (IShaderResourceDX12*)view->GetNativePtr() : nullptr; - - if (_srHandles[slot] != handle) + if (_srHandles[slot] != handle || !handle) { _srMaskDirtyGraphics |= 1 << slot; _srMaskDirtyCompute |= 1 << slot; @@ -736,10 +804,8 @@ void GPUContextDX12::BindSR(int32 slot, GPUResourceView* view) void GPUContextDX12::BindUA(int32 slot, GPUResourceView* view) { ASSERT(slot >= 0 && slot < GPU_MAX_UA_BINDED); - auto handle = view ? (IShaderResourceDX12*)view->GetNativePtr() : nullptr; - - if (_uaHandles[slot] != handle) + if (_uaHandles[slot] != handle || !handle) { _uaMaskDirtyGraphics |= 1 << slot; _uaMaskDirtyCompute |= 1 << slot; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index 944833ca3..9563c58fc 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -350,32 +350,78 @@ bool GPUDeviceDX12::Init() _device->SetStablePowerState(TRUE); #endif - // Create commands queue + // Setup resources _commandQueue = New(this, D3D12_COMMAND_LIST_TYPE_DIRECT); if (_commandQueue->Init()) return true; - - // Create rendering main context _mainContext = New(this, D3D12_COMMAND_LIST_TYPE_DIRECT); - - // Create descriptors heaps - Heap_CBV_SRV_UAV.Init(); - Heap_RTV.Init(); - Heap_DSV.Init(); if (RingHeap_CBV_SRV_UAV.Init()) return true; // Create empty views + D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; + srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + for (int32 i = 0; i < ARRAY_COUNT(_nullSrv); i++) { - D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc; - srvDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; - srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; - srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; - srvDesc.Texture2D.MostDetailedMip = 0; - srvDesc.Texture2D.MipLevels = 1; - srvDesc.Texture2D.PlaneSlice = 0; - srvDesc.Texture2D.ResourceMinLODClamp = 0.0f; - _nullSrv.CreateSRV(this, nullptr, &srvDesc); + srvDesc.ViewDimension = (D3D12_SRV_DIMENSION)i; + switch (srvDesc.ViewDimension) + { + case D3D12_SRV_DIMENSION_BUFFER: + srvDesc.Buffer.FirstElement = 0; + srvDesc.Buffer.NumElements = 0; + srvDesc.Buffer.StructureByteStride = 0; + srvDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; + break; + case D3D12_SRV_DIMENSION_TEXTURE1D: + srvDesc.Texture1D.MostDetailedMip = 0; + srvDesc.Texture1D.MipLevels = 1; + srvDesc.Texture1D.ResourceMinLODClamp = 0.0f; + break; + case D3D12_SRV_DIMENSION_TEXTURE1DARRAY: + srvDesc.Texture1DArray.MostDetailedMip = 0; + srvDesc.Texture1DArray.MipLevels = 1; + srvDesc.Texture1DArray.FirstArraySlice = 0; + srvDesc.Texture1DArray.ArraySize = 1; + srvDesc.Texture1DArray.ResourceMinLODClamp = 0.0f; + break; + case D3D12_SRV_DIMENSION_UNKNOWN: // Map Unknown into Texture2D + case D3D12_SRV_DIMENSION_TEXTURE2D: + srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srvDesc.Texture2D.MostDetailedMip = 0; + srvDesc.Texture2D.MipLevels = 1; + srvDesc.Texture2D.PlaneSlice = 0; + srvDesc.Texture2D.ResourceMinLODClamp = 0.0f; + break; + case D3D12_SRV_DIMENSION_TEXTURE2DARRAY: + srvDesc.Texture2DArray.MostDetailedMip = 0; + srvDesc.Texture2DArray.MipLevels = 1; + srvDesc.Texture2DArray.FirstArraySlice = 0; + srvDesc.Texture2DArray.ArraySize = 0; + srvDesc.Texture2DArray.PlaneSlice = 0; + srvDesc.Texture2DArray.ResourceMinLODClamp = 0.0f; + break; + case D3D12_SRV_DIMENSION_TEXTURE3D: + srvDesc.Texture3D.MostDetailedMip = 0; + srvDesc.Texture3D.MipLevels = 1; + srvDesc.Texture3D.ResourceMinLODClamp = 0.0f; + break; + case D3D12_SRV_DIMENSION_TEXTURECUBE: + srvDesc.TextureCube.MostDetailedMip = 0; + srvDesc.TextureCube.MipLevels = 1; + srvDesc.TextureCube.ResourceMinLODClamp = 0.0f; + break; + case D3D12_SRV_DIMENSION_TEXTURECUBEARRAY: + srvDesc.TextureCubeArray.MostDetailedMip = 0; + srvDesc.TextureCubeArray.MipLevels = 1; + srvDesc.TextureCubeArray.First2DArrayFace = 0; + srvDesc.TextureCubeArray.NumCubes = 0; + srvDesc.TextureCubeArray.ResourceMinLODClamp = 0.0f; + break; + default: + continue; + } + _nullSrv[i].CreateSRV(this, nullptr, &srvDesc); } { D3D12_UNORDERED_ACCESS_VIEW_DESC uavDesc; @@ -574,14 +620,9 @@ bool GPUDeviceDX12::Init() void GPUDeviceDX12::DrawBegin() { - // Wait for the GPU to have at least one backbuffer to render to /*{ - PROFILE_CPU_NAMED("Wait For Fence"); - const uint64 nextFenceValue = _commandQueue->GetNextFenceValue(); - if (nextFenceValue >= DX12_BACK_BUFFER_COUNT) - { - _commandQueue->WaitForFence(nextFenceValue - DX12_BACK_BUFFER_COUNT); - } + PROFILE_CPU_NAMED("Wait For GPU"); + _commandQueue->WaitForGPU(); }*/ // Base @@ -606,9 +647,9 @@ GPUDeviceDX12::~GPUDeviceDX12() Dispose(); } -D3D12_CPU_DESCRIPTOR_HANDLE GPUDeviceDX12::NullSRV() const +D3D12_CPU_DESCRIPTOR_HANDLE GPUDeviceDX12::NullSRV(D3D12_SRV_DIMENSION dimension) const { - return _nullSrv.CPU(); + return _nullSrv[dimension].CPU(); } D3D12_CPU_DESCRIPTOR_HANDLE GPUDeviceDX12::NullUAV() const @@ -647,7 +688,8 @@ void GPUDeviceDX12::Dispose() updateRes2Dispose(); // Clear pipeline objects - _nullSrv.Release(); + for (auto& srv : _nullSrv) + srv.Release(); _nullUav.Release(); TimestampQueryHeap.Destroy(); DX_SAFE_RELEASE_CHECK(_rootSignature, 0); @@ -709,7 +751,6 @@ GPUSwapChain* GPUDeviceDX12::CreateSwapChain(Window* window) void GPUDeviceDX12::AddResourceToLateRelease(IGraphicsUnknown* resource, uint32 safeFrameCount) { - ASSERT(safeFrameCount < 32); if (resource == nullptr) return; @@ -735,7 +776,6 @@ void GPUDeviceDX12::updateRes2Dispose() for (int32 i = _res2Dispose.Count() - 1; i >= 0 && i < _res2Dispose.Count(); i--) { const DisposeResourceEntry& entry = _res2Dispose[i]; - if (entry.TargetFrame <= currentFrame) { auto refs = entry.Resource->Release(); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h index 46dab6b57..940be58c3 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.h @@ -55,7 +55,7 @@ private: GPUContextDX12* _mainContext; // Heaps - DescriptorHeapWithSlotsDX12::Slot _nullSrv; + DescriptorHeapWithSlotsDX12::Slot _nullSrv[D3D12_SRV_DIMENSION_TEXTURECUBEARRAY + 1]; DescriptorHeapWithSlotsDX12::Slot _nullUav; public: @@ -93,8 +93,7 @@ public: CommandSignatureDX12* DrawIndexedIndirectCommandSignature = nullptr; CommandSignatureDX12* DrawIndirectCommandSignature = nullptr; - D3D12_CPU_DESCRIPTOR_HANDLE NullSRV() const; - + D3D12_CPU_DESCRIPTOR_HANDLE NullSRV(D3D12_SRV_DIMENSION dimension) const; D3D12_CPU_DESCRIPTOR_HANDLE NullUAV() const; public: diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.cpp index b8ac22bba..7f7119038 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUSwapChainDX12.cpp @@ -21,7 +21,7 @@ void BackBufferDX12::Setup(GPUSwapChainDX12* window, ID3D12Resource* backbuffer) rtDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; rtDesc.Texture2D.MipSlice = 0; rtDesc.Texture2D.PlaneSlice = 0; - Handle.SetRTV(&rtDesc); + Handle.SetRTV(rtDesc); } #if GPU_USE_WINDOW_SRV @@ -35,7 +35,7 @@ void BackBufferDX12::Setup(GPUSwapChainDX12* window, ID3D12Resource* backbuffer) srDesc.Texture2D.MipLevels = 1; srDesc.Texture2D.ResourceMinLODClamp = 0; srDesc.Texture2D.PlaneSlice = 0; - Handle.SetSRV(&srDesc); + Handle.SetSRV(srDesc); } #endif } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp index 03ba98eaf..265023fa8 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp @@ -237,7 +237,7 @@ void GPUTextureDX12::onResidentMipsChanged() // Change view if (_handlesPerSlice[0].GetParent() == nullptr) _handlesPerSlice[0].Init(this, _device, this, Format(), MultiSampleLevel()); - _handlesPerSlice[0].SetSRV(&srDesc); + _handlesPerSlice[0].SetSRV(srDesc); } void GPUTextureDX12::OnReleaseGPU() @@ -254,6 +254,35 @@ void GPUTextureDX12::OnReleaseGPU() GPUTexture::OnReleaseGPU(); } +void GPUTextureViewDX12::Release() +{ + _rtv.Release(); + _srv.Release(); + _dsv.Release(); + _uav.Release(); +} + +void GPUTextureViewDX12::SetRTV(D3D12_RENDER_TARGET_VIEW_DESC& rtvDesc) +{ + _rtv.CreateRTV(_device, _owner->GetResource(), &rtvDesc); +} + +void GPUTextureViewDX12::SetSRV(D3D12_SHADER_RESOURCE_VIEW_DESC& srvDesc) +{ + SrvDimension = srvDesc.ViewDimension; + _srv.CreateSRV(_device, _owner->GetResource(), &srvDesc); +} + +void GPUTextureViewDX12::SetDSV(D3D12_DEPTH_STENCIL_VIEW_DESC& dsvDesc) +{ + _dsv.CreateDSV(_device, _owner->GetResource(), &dsvDesc); +} + +void GPUTextureViewDX12::SetUAV(D3D12_UNORDERED_ACCESS_VIEW_DESC& uavDesc, ID3D12Resource* counterResource) +{ + _uav.CreateUAV(_device, _owner->GetResource(), &uavDesc, counterResource); +} + void GPUTextureDX12::initHandles() { D3D12_RENDER_TARGET_VIEW_DESC rtDesc; @@ -318,7 +347,7 @@ void GPUTextureDX12::initHandles() srDesc.Texture3D.MostDetailedMip = 0; srDesc.Texture3D.MipLevels = MipLevels(); srDesc.Texture3D.ResourceMinLODClamp = 0; - _handleVolume.SetSRV(&srDesc); + _handleVolume.SetSRV(srDesc); } if (useRTV) { @@ -326,14 +355,14 @@ void GPUTextureDX12::initHandles() rtDesc.Texture3D.MipSlice = 0; rtDesc.Texture3D.FirstWSlice = 0; rtDesc.Texture3D.WSize = Depth(); - _handleVolume.SetRTV(&rtDesc); + _handleVolume.SetRTV(rtDesc); } if (useUAV) { uavDesc.ViewDimension = D3D12_UAV_DIMENSION_TEXTURE3D; uavDesc.Texture2D.MipSlice = 0; uavDesc.Texture2D.PlaneSlice = 0; - _handleVolume.SetUAV(&uavDesc); + _handleVolume.SetUAV(uavDesc); } // Init per slice views @@ -348,7 +377,7 @@ void GPUTextureDX12::initHandles() { rtDesc.Texture3D.FirstWSlice = sliceIndex; _handlesPerSlice[sliceIndex].Init(this, _device, this, format, msaa); - _handlesPerSlice[sliceIndex].SetRTV(&rtDesc); + _handlesPerSlice[sliceIndex].SetRTV(rtDesc); } } } @@ -378,7 +407,7 @@ void GPUTextureDX12::initHandles() dsDesc.Texture2DArray.FirstArraySlice = arrayIndex; dsDesc.Texture2DArray.MipSlice = 0; } - _handlesPerSlice[arrayIndex].SetDSV(&dsDesc); + _handlesPerSlice[arrayIndex].SetDSV(dsDesc); } if (useRTV) { @@ -398,7 +427,7 @@ void GPUTextureDX12::initHandles() rtDesc.Texture2DArray.MipSlice = 0; rtDesc.Texture2DArray.PlaneSlice = 0; } - _handlesPerSlice[arrayIndex].SetRTV(&rtDesc); + _handlesPerSlice[arrayIndex].SetRTV(rtDesc); } if (useSRV) { @@ -420,7 +449,7 @@ void GPUTextureDX12::initHandles() srDesc.Texture2DArray.PlaneSlice = 0; srDesc.Texture2DArray.ResourceMinLODClamp = 0; } - _handlesPerSlice[arrayIndex].SetSRV(&srDesc); + _handlesPerSlice[arrayIndex].SetSRV(srDesc); } if (useUAV) { @@ -429,7 +458,7 @@ void GPUTextureDX12::initHandles() uavDesc.Texture2DArray.FirstArraySlice = arrayIndex; uavDesc.Texture2DArray.MipSlice = 0; uavDesc.Texture2DArray.PlaneSlice = 0; - _handlesPerSlice[arrayIndex].SetSRV(&srDesc); + _handlesPerSlice[arrayIndex].SetSRV(srDesc); } } @@ -442,7 +471,7 @@ void GPUTextureDX12::initHandles() dsDesc.Texture2DArray.ArraySize = arraySize; dsDesc.Texture2DArray.FirstArraySlice = 0; dsDesc.Texture2DArray.MipSlice = 0; - _handleArray.SetDSV(&dsDesc); + _handleArray.SetDSV(dsDesc); } if (useRTV) { @@ -451,7 +480,7 @@ void GPUTextureDX12::initHandles() rtDesc.Texture2DArray.FirstArraySlice = 0; rtDesc.Texture2DArray.MipSlice = 0; rtDesc.Texture2DArray.PlaneSlice = 0; - _handleArray.SetRTV(&rtDesc); + _handleArray.SetRTV(rtDesc); } if (useSRV) { @@ -472,7 +501,7 @@ void GPUTextureDX12::initHandles() srDesc.Texture2DArray.ResourceMinLODClamp = 0; srDesc.Texture2DArray.PlaneSlice = 0; } - _handleArray.SetSRV(&srDesc); + _handleArray.SetSRV(srDesc); } if (useUAV) { @@ -481,7 +510,7 @@ void GPUTextureDX12::initHandles() uavDesc.Texture2DArray.FirstArraySlice = 0; uavDesc.Texture2DArray.MipSlice = 0; uavDesc.Texture2DArray.PlaneSlice = 0; - _handleArray.SetUAV(&uavDesc); + _handleArray.SetUAV(uavDesc); } } } @@ -508,7 +537,7 @@ void GPUTextureDX12::initHandles() dsDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; dsDesc.Texture2D.MipSlice = 0; } - _handlesPerSlice[0].SetDSV(&dsDesc); + _handlesPerSlice[0].SetDSV(dsDesc); } if (useRTV) { @@ -530,7 +559,7 @@ void GPUTextureDX12::initHandles() rtDesc.Texture2D.MipSlice = 0; rtDesc.Texture2D.PlaneSlice = 0; } - _handlesPerSlice[0].SetRTV(&rtDesc); + _handlesPerSlice[0].SetRTV(rtDesc); } if (useSRV) { @@ -553,7 +582,7 @@ void GPUTextureDX12::initHandles() srDesc.Texture2D.ResourceMinLODClamp = 0; srDesc.Texture2D.PlaneSlice = 0; } - _handlesPerSlice[0].SetSRV(&srDesc); + _handlesPerSlice[0].SetSRV(srDesc); } if (useUAV) { @@ -571,7 +600,7 @@ void GPUTextureDX12::initHandles() uavDesc.Texture2D.MipSlice = 0; uavDesc.Texture2D.PlaneSlice = 0; } - _handlesPerSlice[0].SetUAV(&uavDesc); + _handlesPerSlice[0].SetUAV(uavDesc); } } @@ -593,35 +622,63 @@ void GPUTextureDX12::initHandles() // DSV if (useDSV) { - dsDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY; - dsDesc.Texture2DArray.ArraySize = 1; - dsDesc.Texture2DArray.FirstArraySlice = arrayIndex; - dsDesc.Texture2DArray.MipSlice = mipIndex; - slice[mipIndex].SetDSV(&dsDesc); + if (isArray) + { + dsDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DARRAY; + dsDesc.Texture2DArray.ArraySize = 1; + dsDesc.Texture2DArray.FirstArraySlice = arrayIndex; + dsDesc.Texture2DArray.MipSlice = mipIndex; + } + else + { + dsDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D; + dsDesc.Texture2D.MipSlice = mipIndex; + } + slice[mipIndex].SetDSV(dsDesc); } // RTV if (useRTV) { - rtDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; - rtDesc.Texture2DArray.ArraySize = 1; - rtDesc.Texture2DArray.FirstArraySlice = arrayIndex; - rtDesc.Texture2DArray.MipSlice = mipIndex; - rtDesc.Texture2DArray.PlaneSlice = 0; - slice[mipIndex].SetRTV(&rtDesc); + if (isArray) + { + rtDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY; + rtDesc.Texture2DArray.ArraySize = 1; + rtDesc.Texture2DArray.FirstArraySlice = arrayIndex; + rtDesc.Texture2DArray.MipSlice = mipIndex; + rtDesc.Texture2DArray.PlaneSlice = 0; + } + else + { + rtDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D; + rtDesc.Texture2D.MipSlice = mipIndex; + rtDesc.Texture2D.PlaneSlice = 0; + } + slice[mipIndex].SetRTV(rtDesc); } // SRV if (useSRV) { - srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; - srDesc.Texture2DArray.ArraySize = 1; - srDesc.Texture2DArray.FirstArraySlice = arrayIndex; - srDesc.Texture2DArray.MipLevels = 1; - srDesc.Texture2DArray.MostDetailedMip = mipIndex; - srDesc.Texture2DArray.ResourceMinLODClamp = 0; - srDesc.Texture2DArray.PlaneSlice = 0; - slice[mipIndex].SetSRV(&srDesc); + if (isArray) + { + srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DARRAY; + srDesc.Texture2DArray.ArraySize = 1; + srDesc.Texture2DArray.FirstArraySlice = arrayIndex; + srDesc.Texture2DArray.MipLevels = 1; + srDesc.Texture2DArray.MostDetailedMip = mipIndex; + srDesc.Texture2DArray.ResourceMinLODClamp = 0; + srDesc.Texture2DArray.PlaneSlice = 0; + } + else + { + srDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + srDesc.Texture2D.MipLevels = 1; + srDesc.Texture2D.MostDetailedMip = mipIndex; + srDesc.Texture2D.ResourceMinLODClamp = 0; + srDesc.Texture2D.PlaneSlice = 0; + } + slice[mipIndex].SetSRV(srDesc); } } } @@ -652,7 +709,7 @@ void GPUTextureDX12::initHandles() dsDesc.Flags = D3D12_DSV_FLAG_READ_ONLY_DEPTH; if (PixelFormatExtensions::HasStencil(format)) dsDesc.Flags |= D3D12_DSV_FLAG_READ_ONLY_STENCIL; - _handleReadOnlyDepth.SetDSV(&dsDesc); + _handleReadOnlyDepth.SetDSV(dsDesc); } ASSERT(!useRTV); if (useSRV) @@ -676,7 +733,7 @@ void GPUTextureDX12::initHandles() srDesc.Texture2D.ResourceMinLODClamp = 0; srDesc.Texture2D.PlaneSlice = 0; } - _handleReadOnlyDepth.SetSRV(&srDesc); + _handleReadOnlyDepth.SetSRV(srDesc); } } } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h index 01e23d08c..c13df1756 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h @@ -58,7 +58,7 @@ public: /// Parent texture format /// Parent texture multi-sample level /// Used subresource index or -1 to cover whole resource. - void Init(GPUResource* parent, GPUDeviceDX12* device, ResourceOwnerDX12* owner, PixelFormat format, MSAALevel msaa, int32 subresourceIndex = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES) + void Init(GPUResource* parent, GPUDeviceDX12* device, ResourceOwnerDX12* owner, PixelFormat format, MSAALevel msaa, int32 subresourceIndex = -1) { GPUTextureView::Init(parent, format, msaa); SubresourceIndex = subresourceIndex; @@ -69,80 +69,14 @@ public: /// /// Releases the view. /// - void Release() - { - _rtv.Release(); - _srv.Release(); - _dsv.Release(); - _uav.Release(); - } + void Release(); public: - /// - /// Sets the render target view. - /// - /// The RTV desc. - void SetRTV(D3D12_RENDER_TARGET_VIEW_DESC* rtvDesc) - { - if (rtvDesc) - { - _rtv.CreateRTV(_device, _owner->GetResource(), rtvDesc); - } - else - { - _rtv.Release(); - } - } - - /// - /// Sets the shader resource view. - /// - /// The SRV desc. - void SetSRV(D3D12_SHADER_RESOURCE_VIEW_DESC* srvDesc) - { - if (srvDesc) - { - _srv.CreateSRV(_device, _owner->GetResource(), srvDesc); - } - else - { - _srv.Release(); - } - } - - /// - /// Sets the depth stencil view. - /// - /// The DSV desc. - void SetDSV(D3D12_DEPTH_STENCIL_VIEW_DESC* dsvDesc) - { - if (dsvDesc) - { - _dsv.CreateDSV(_device, _owner->GetResource(), dsvDesc); - } - else - { - _dsv.Release(); - } - } - - /// - /// Sets the unordered access view. - /// - /// The UAV desc. - /// The counter buffer resource. - void SetUAV(D3D12_UNORDERED_ACCESS_VIEW_DESC* uavDesc, ID3D12Resource* counterResource = nullptr) - { - if (uavDesc) - { - _uav.CreateUAV(_device, _owner->GetResource(), uavDesc, counterResource); - } - else - { - _uav.Release(); - } - } + void SetRTV(D3D12_RENDER_TARGET_VIEW_DESC& rtvDesc); + void SetSRV(D3D12_SHADER_RESOURCE_VIEW_DESC& srvDesc); + void SetDSV(D3D12_DEPTH_STENCIL_VIEW_DESC& dsvDesc); + void SetUAV(D3D12_UNORDERED_ACCESS_VIEW_DESC& uavDesc, ID3D12Resource* counterResource = nullptr); public: diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/IShaderResourceDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/IShaderResourceDX12.h index 725f1d584..445d83f2a 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/IShaderResourceDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/IShaderResourceDX12.h @@ -15,7 +15,7 @@ class IShaderResourceDX12 public: IShaderResourceDX12() - : SubresourceIndex(D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES) + : SubresourceIndex(-1) { } @@ -28,8 +28,11 @@ public: /// /// Affected subresource index or -1 if use whole resource. + /// This solves only resource states tracking per single subresource, not subresources range, if need to here should be range of subresources (for texture arrays, volume textures and cubemaps). /// - int32 SubresourceIndex; // Note: this solves only resource states tracking per single subresource, not subresources range, if need to here should be range of subresources (for texture arrays, volume textures and cubemaps) + int32 SubresourceIndex; + + D3D12_SRV_DIMENSION SrvDimension = D3D12_SRV_DIMENSION_UNKNOWN; public: From 3872d74bd2e1d4566332662ca9871e4d964ca662 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 7 Jun 2021 13:09:24 +0200 Subject: [PATCH 102/198] Fix missing UAV barriers on D3D12 --- .../DirectX/DX12/GPUContextDX12.cpp | 44 ++++++++++++------- .../DirectX/DX12/GPUContextDX12.h | 5 +++ 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index ed45c8768..0e40f89f6 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -93,7 +93,6 @@ GPUContextDX12::~GPUContextDX12() void GPUContextDX12::AddTransitionBarrier(ResourceOwnerDX12* resource, const D3D12_RESOURCE_STATES before, const D3D12_RESOURCE_STATES after, const int32 subresourceIndex) { - // Check if need to flush barriers if (_rbBufferSize == DX12_RB_BUFFER_SIZE) flushRBs(); @@ -108,20 +107,6 @@ void GPUContextDX12::AddTransitionBarrier(ResourceOwnerDX12* resource, const D3D Log::Logger::Write(LogType::Info, info); #endif -#if DX12_ENABLE_RESOURCE_BARRIERS_BATCHING - - // Enqueue barrier - D3D12_RESOURCE_BARRIER& barrier = _rbBuffer[_rbBufferSize]; - barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; - barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; - barrier.Transition.pResource = resource->GetResource(); - barrier.Transition.StateBefore = before; - barrier.Transition.StateAfter = after; - barrier.Transition.Subresource = subresourceIndex; - _rbBufferSize++; - -#else - D3D12_RESOURCE_BARRIER barrier; barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; @@ -129,8 +114,31 @@ void GPUContextDX12::AddTransitionBarrier(ResourceOwnerDX12* resource, const D3D barrier.Transition.StateBefore = before; barrier.Transition.StateAfter = after; barrier.Transition.Subresource = subresourceIndex; +#if DX12_ENABLE_RESOURCE_BARRIERS_BATCHING + _rbBuffer[_rbBufferSize++] = barrier; +#else _commandList->ResourceBarrier(1, &barrier); +#endif +} +void GPUContextDX12::AddUAVBarrier() +{ + if (_rbBufferSize == DX12_RB_BUFFER_SIZE) + flushRBs(); + +#if DX12_ENABLE_RESOURCE_BARRIERS_DEBUGGING + const auto info = String::Format(TEXT("[DX12 Resource Barrier]: UAV")); + Log::Logger::Write(LogType::Info, info); +#endif + + D3D12_RESOURCE_BARRIER barrier; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_UAV; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.UAV.pResource = nullptr; +#if DX12_ENABLE_RESOURCE_BARRIERS_BATCHING + _rbBuffer[_rbBufferSize++] = barrier; +#else + _commandList->ResourceBarrier(1, &barrier); #endif } @@ -916,6 +924,9 @@ void GPUContextDX12::Dispatch(GPUShaderProgramCS* shader, uint32 threadGroupCoun // Restore previous state on next draw call _psDirtyFlag = true; + + // Insert UAV barrier to ensure proper memory access for multiple sequential dispatches + AddUAVBarrier(); } void GPUContextDX12::DispatchIndirect(GPUShaderProgramCS* shader, GPUBuffer* bufferForArgs, uint32 offsetForArgs) @@ -946,6 +957,9 @@ void GPUContextDX12::DispatchIndirect(GPUShaderProgramCS* shader, GPUBuffer* buf // Restore previous state on next draw call _psDirtyFlag = true; + + // Insert UAV barrier to ensure proper memory access for multiple sequential dispatches + AddUAVBarrier(); } void GPUContextDX12::ResolveMultisample(GPUTexture* sourceMultisampleTexture, GPUTexture* destTexture, int32 sourceSubResource, int32 destSubResource, PixelFormat format) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h index 6fb670e7b..30e64ec3d 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h @@ -102,6 +102,11 @@ public: /// The index of the subresource. void AddTransitionBarrier(ResourceOwnerDX12* resource, const D3D12_RESOURCE_STATES before, const D3D12_RESOURCE_STATES after, const int32 subresourceIndex); + /// + /// Adds the UAV barrier. Supports batching barriers. + /// + void AddUAVBarrier(); + /// /// Set DirectX 12 resource state using resource barrier /// From 36c4c3c86af652e657df3d635253ea733fe57c13 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 7 Jun 2021 13:09:52 +0200 Subject: [PATCH 103/198] Fix lightmaps baking on D3D12 --- Source/Engine/ShadowsOfMordor/Builder.Debug.cpp | 4 +++- Source/Engine/ShadowsOfMordor/Builder.DoWork.cpp | 2 +- Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp | 7 ++++++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Source/Engine/ShadowsOfMordor/Builder.Debug.cpp b/Source/Engine/ShadowsOfMordor/Builder.Debug.cpp index 0e1236854..5be0072b4 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Debug.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Debug.cpp @@ -9,6 +9,8 @@ #if DEBUG_EXPORT_LIGHTMAPS_PREVIEW || DEBUG_EXPORT_CACHE_PREVIEW || DEBUG_EXPORT_HEMISPHERES_PREVIEW +#include "Engine/Engine/Globals.h" + String GetDebugDataPath() { auto result = Globals::ProjectCacheFolder / TEXT("ShadowsOfMordor_Debug"); @@ -165,7 +167,7 @@ void ShadowsOfMordor::Builder::downloadDebugHemisphereAtlases(SceneBuildCache* s GPUTexture* atlas = DebugExportHemispheresAtlases[atlasIndex]; TextureData textureData; - if (atlas->DownloadData(&textureData)) + if (atlas->DownloadData(textureData)) { LOG(Error, "Cannot download hemispheres atlas data."); continue; diff --git a/Source/Engine/ShadowsOfMordor/Builder.DoWork.cpp b/Source/Engine/ShadowsOfMordor/Builder.DoWork.cpp index fe388c039..b144a0d5a 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.DoWork.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.DoWork.cpp @@ -135,7 +135,7 @@ bool ShadowsOfMordor::Builder::doWorkInner(DateTime buildStart) } } reportProgress(BuildProgressStep::RenderHemispheres, 1.0f); - return true; + return false; } #endif diff --git a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp index 867c5b3f9..c214c7e4b 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Jobs.cpp @@ -17,6 +17,7 @@ #include "Engine/Terrain/TerrainPatch.h" #include "Engine/Terrain/TerrainManager.h" #include "Engine/Foliage/Foliage.h" +#include "Engine/Graphics/GPUDevice.h" #include "Engine/Profiler/Profiler.h" namespace ShadowsOfMordor @@ -37,6 +38,8 @@ namespace ShadowsOfMordor void ShadowsOfMordor::Builder::onJobRender(GPUContext* context) { + if (_workerActiveSceneIndex < 0 || _workerActiveSceneIndex >= _scenes.Count()) + return; auto scene = _scenes[_workerActiveSceneIndex]; int32 atlasSize = (int32)scene->GetSettings().AtlasSize; @@ -465,7 +468,9 @@ void ShadowsOfMordor::Builder::onJobRender(GPUContext* context) Swap(scene->TempLightmapData, lightmapEntry.LightmapData); // Keep blurring the empty lightmap texels (from background) - const int32 blurPasses = 24; + int32 blurPasses = 24; + if (context->GetDevice()->GetRendererType() == RendererType::DirectX12) + blurPasses = 0; // TODO: fix CS_Dilate passes on D3D12 (probably UAV synchronization issue) for (int32 blurPassIndex = 0; blurPassIndex < blurPasses; blurPassIndex++) { context->UnBindSR(0); From 4b42e1737221b59488da5b1b20b8b14380c769ff Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 7 Jun 2021 13:10:09 +0200 Subject: [PATCH 104/198] Increase D3D12 resources safe-frames timeout --- Source/Engine/GraphicsDevice/DirectX/DX12/ResourceOwnerDX12.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/ResourceOwnerDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/ResourceOwnerDX12.h index 8b140a1c7..acca85e3d 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/ResourceOwnerDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/ResourceOwnerDX12.h @@ -15,7 +15,7 @@ class GPUAsyncContextDX12; /// /// Default amount of frames to wait until resource delete. /// -#define DX12_RESOURCE_DELETE_SAFE_FRAMES_COUNT 10 +#define DX12_RESOURCE_DELETE_SAFE_FRAMES_COUNT 100 /// /// Custom resource state used to indicate invalid state (useful for debugging resource tracking issues). From f7957be3e70e8274bd5ab480b65e68cf8a79c7ec Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 7 Jun 2021 14:53:53 +0200 Subject: [PATCH 105/198] Fix GPU synchronization on D3D12 --- .../DirectX/DX12/CommandQueueDX12.h | 34 ----------- .../DirectX/DX12/DescriptorHeapDX12.cpp | 10 ++-- .../DirectX/DX12/DescriptorHeapDX12.h | 56 +++---------------- .../DirectX/DX12/GPUContextDX12.cpp | 7 ++- .../DirectX/DX12/GPUContextDX12.h | 15 +---- .../DirectX/DX12/GPUDeviceDX12.cpp | 9 +-- .../DirectX/DX12/GPUPipelineStateDX12.cpp | 2 +- .../DirectX/DX12/UploadBufferDX12.h | 2 +- 8 files changed, 27 insertions(+), 108 deletions(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.h index e92a3b819..882277125 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/CommandQueueDX12.h @@ -3,7 +3,6 @@ #pragma once #include "Engine/Core/Types/BaseTypes.h" -#include "Engine/Platform/Platform.h" #include "Engine/Platform/CriticalSection.h" #include "CommandAllocatorPoolDX12.h" #include "../IncludeDirectXHeaders.h" @@ -35,28 +34,16 @@ public: public: - /// - /// Gets the current fence value. - /// - /// The current fence value. FORCE_INLINE uint64 GetCurrentValue() const { return _currentValue; } - /// - /// Gets the last signaled fence value. - /// - /// The last signaled fence value. FORCE_INLINE uint64 GetLastSignaledValue() const { return _lastSignaledValue; } - /// - /// Gets the last completed fence value. - /// - /// The last completed fence value. FORCE_INLINE uint64 GetLastCompletedValue() const { return _lastCompletedValue; @@ -146,42 +133,21 @@ private: public: - /// - /// Init - /// - /// Graphics Device handle - /// Command queue type CommandQueueDX12(GPUDeviceDX12* device, D3D12_COMMAND_LIST_TYPE type); - - /// - /// Destructor - /// ~CommandQueueDX12(); public: - /// - /// Checks if command queue is ready for work - /// - /// True if is ready for work FORCE_INLINE bool IsReady() const { return _commandQueue != nullptr; } - /// - /// Gets DirectX 12 command queue object - /// - /// DirectX 12 command queue FORCE_INLINE ID3D12CommandQueue* GetCommandQueue() const { return _commandQueue; } - /// - /// Gets the command lists allocator pool. - /// - /// The allocator. FORCE_INLINE CommandAllocatorPoolDX12& GetAllocatorPool() { return _allocatorPool; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.cpp index dcfaff53a..bc1e0d922 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.cpp @@ -128,6 +128,11 @@ void DescriptorHeapWithSlotsDX12::ReleaseSlot(uint32 index) value &= ~mask; } +GPUResource::ResourceType DescriptorHeapWithSlotsDX12::GetResourceType() const +{ + return ResourceType::Descriptor; +} + DescriptorHeapPoolDX12::DescriptorHeapPoolDX12(GPUDeviceDX12* device, D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 descriptorsCountPerHeap, bool shaderVisible) : _device(device) , _type(type) @@ -184,14 +189,12 @@ DescriptorHeapRingBufferDX12::DescriptorHeapRingBufferDX12(GPUDeviceDX12* device bool DescriptorHeapRingBufferDX12::Init() { - // Create description + // Create heap D3D12_DESCRIPTOR_HEAP_DESC desc; desc.Type = _type; desc.NumDescriptors = _descriptorsCount; desc.Flags = _shaderVisible ? D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE : D3D12_DESCRIPTOR_HEAP_FLAG_NONE; desc.NodeMask = 0; - - // Create heap const HRESULT result = _device->GetDevice()->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&_heap)); LOG_DIRECTX_RESULT_WITH_RETURN(result); @@ -203,7 +206,6 @@ bool DescriptorHeapRingBufferDX12::Init() else _beginGPU.ptr = 0; _incrementSize = _device->GetDevice()->GetDescriptorHandleIncrementSize(desc.Type); - _memoryUsage = 1; return false; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h index e77893b0b..7b9bf20a2 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h @@ -63,36 +63,6 @@ public: public: - // Get heap - FORCE_INLINE operator ID3D12DescriptorHeap*() const - { - return _heap; - } - -public: - - // Create heap data - // @param type Heap data type - // @param descriptorsCount Amount of descriptors to use - // @param shaderVisible True if allow shaders to access heap data - bool Create(D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 descriptorsCount, bool shaderVisible = false); - -public: - - // Tries to find free descriptor slot - // @param index Result index to use - // @returns True if can assign descriptor to the heap - bool TryToGetUnusedSlot(uint32& index); - - // Release descriptor slot - // @param index Descriptor index in the heap - void ReleaseSlot(uint32 index); - -public: - - // Get handle to the CPU view at given index - // @param index Descriptor index - // @returns CPU address FORCE_INLINE D3D12_CPU_DESCRIPTOR_HANDLE CPU(uint32 index) { D3D12_CPU_DESCRIPTOR_HANDLE handle; @@ -100,9 +70,6 @@ public: return handle; } - // Get handle to the GPU view at given index - // @param index Descriptor index - // @returns GPU address FORCE_INLINE D3D12_GPU_DESCRIPTOR_HANDLE GPU(uint32 index) { D3D12_GPU_DESCRIPTOR_HANDLE handle; @@ -110,13 +77,16 @@ public: return handle; } +public: + + bool Create(D3D12_DESCRIPTOR_HEAP_TYPE type, uint32 descriptorsCount, bool shaderVisible = false); + bool TryToGetUnusedSlot(uint32& index); + void ReleaseSlot(uint32 index); + public: // [GPUResourceDX12] - ResourceType GetResourceType() const final override - { - return ResourceType::Descriptor; - } + ResourceType GetResourceType() const final override; protected: @@ -188,24 +158,12 @@ public: public: - /// - /// Gets DirectX 12 heap object - /// - /// Heap object FORCE_INLINE ID3D12DescriptorHeap* GetHeap() const { return _heap; } -public: - - // Setup heap - // @returns True if cannot setup heap, otherwise false bool Init(); - - // Allocate memory for descriptors table - // @param numDesc Amount of descriptors in table - // @returns Allocated data (GPU param is valid only for shader visible heaps) Allocation AllocateTable(uint32 numDesc); public: diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index 0e40f89f6..c99340496 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -79,6 +79,8 @@ GPUContextDX12::GPUContextDX12(GPUDeviceDX12* device, D3D12_COMMAND_LIST_TYPE ty , _rtDepth(nullptr) , _ibHandle(nullptr) { + FrameFenceValues[0] = 0; + FrameFenceValues[1] = 0; _currentAllocator = _device->GetCommandQueue()->RequestAllocator(); VALIDATE_DIRECTX_RESULT(device->GetDevice()->CreateCommandList(0, type, _currentAllocator, nullptr, IID_PPV_ARGS(&_commandList))); #if GPU_ENABLE_RESOURCE_NAMING @@ -592,7 +594,8 @@ void GPUContextDX12::FrameEnd() GPUContext::FrameEnd(); // Execute command (but don't wait for them) - Execute(false); + FrameFenceValues[1] = FrameFenceValues[0]; + FrameFenceValues[0] = Execute(false); } #if GPU_ALLOW_PROFILE_EVENTS @@ -1080,7 +1083,7 @@ void GPUContextDX12::Flush() return; // Flush GPU commands - Execute(); + Execute(true); Reset(); } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h index 30e64ec3d..7e9994add 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h @@ -68,29 +68,18 @@ private: public: - /// - /// Init - /// - /// Graphics device - /// Context type GPUContextDX12(GPUDeviceDX12* device, D3D12_COMMAND_LIST_TYPE type); - - /// - /// Destructor - /// ~GPUContextDX12(); public: - /// - /// Gets command list - /// - /// Command list to use FORCE_INLINE ID3D12GraphicsCommandList* GetCommandList() const { return _commandList; } + uint64 FrameFenceValues[2]; + public: /// diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index 9563c58fc..6b6cf5d49 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -169,7 +169,7 @@ GPUDeviceDX12::GPUDeviceDX12(IDXGIFactory4* dxgiFactory, GPUAdapterDX* adapter) , Heap_CBV_SRV_UAV(this, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 4 * 1024, false) , Heap_RTV(this, D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1 * 1024, false) , Heap_DSV(this, D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 64, false) - , RingHeap_CBV_SRV_UAV(this, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 64 * 1024, true) + , RingHeap_CBV_SRV_UAV(this, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 512 * 1024, true) { } @@ -620,10 +620,11 @@ bool GPUDeviceDX12::Init() void GPUDeviceDX12::DrawBegin() { - /*{ + { PROFILE_CPU_NAMED("Wait For GPU"); - _commandQueue->WaitForGPU(); - }*/ + //_commandQueue->WaitForGPU(); + _commandQueue->WaitForFence(_mainContext->FrameFenceValues[1]); + } // Base GPUDeviceDX::DrawBegin(); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.cpp index 50940d46d..08928e94b 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.cpp @@ -64,7 +64,7 @@ ID3D12PipelineState* GPUPipelineStateDX12::GetState(GPUTextureViewDX12* depth, i LOG_DIRECTX_RESULT(result); if (FAILED(result)) return nullptr; -#if GPU_ENABLE_RESOURCE_NAMING +#if GPU_ENABLE_RESOURCE_NAMING && BUILD_DEBUG char name[200]; int32 nameLen = 0; if (DebugDesc.VS) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.h index 605d3268c..7f1723cb5 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/UploadBufferDX12.h @@ -13,7 +13,7 @@ #define DX12_UPLOAD_PAGE_GEN_TIMEOUT DX12_BACK_BUFFER_COUNT // Upload buffer pages that are not used for a few frames are disposed -#define DX12_UPLOAD_PAGE_NOT_USED_FRAME_TIMEOUT 8 +#define DX12_UPLOAD_PAGE_NOT_USED_FRAME_TIMEOUT 60 class GPUTextureDX12; From ab55c145f104a722ae520532290c5ad2594a841f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 7 Jun 2021 14:55:08 +0200 Subject: [PATCH 106/198] Add DRED and optional GPU-Based Validation for D3D12 --- .../DirectX/DX12/GPUDeviceDX12.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index 6b6cf5d49..edaceba26 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -63,6 +63,24 @@ GPUDevice* GPUDeviceDX12::Create() debugLayer->EnableDebugLayer(); LOG(Info, "DirectX debugging layer enabled"); } +#if 0 + ComPtr debugLayer1; + D3D12GetDebugInterface(IID_PPV_ARGS(&debugLayer1)); + if (debugLayer1) + { + // GPU-based validation and synchronized validation for debugging only + debugLayer1->SetEnableGPUBasedValidation(true); + debugLayer1->SetEnableSynchronizedCommandQueueValidation(true); + } +#endif + ComPtr dredSettings; + VALIDATE_DIRECTX_RESULT(D3D12GetDebugInterface(IID_PPV_ARGS(&dredSettings))); + if (dredSettings) + { + // Turn on AutoBreadcrumbs and Page Fault reporting + dredSettings->SetAutoBreadcrumbsEnablement(D3D12_DRED_ENABLEMENT_FORCED_ON); + dredSettings->SetPageFaultEnablement(D3D12_DRED_ENABLEMENT_FORCED_ON); + } #endif // Create DXGI factory (CreateDXGIFactory2 is supported on Windows 8.1 or newer) From 9d9ecb3ba8773b4e5176d40ebe61571ffe0ccad6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 8 Jun 2021 14:12:31 +0200 Subject: [PATCH 107/198] Fix D3D12 resource state transitions barriers --- .../DirectX/DX12/GPUContextDX12.cpp | 113 ++++++++++-------- .../DirectX/DX12/GPUTextureDX12.cpp | 3 + .../DirectX/DX12/GPUTextureDX12.h | 1 + .../DirectX/DX12/ResourceOwnerDX12.h | 17 ++- 4 files changed, 78 insertions(+), 56 deletions(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index c99340496..3fd85fad8 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -159,7 +159,7 @@ void GPUContextDX12::SetResourceState(ResourceOwnerDX12* resource, D3D12_RESOURC if (ResourceStateDX12::IsTransitionNeeded(before, after)) { AddTransitionBarrier(resource, before, after, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES); - state.SetSubresourceState(subresourceIndex, after); + state.SetResourceState(after); } } else @@ -175,8 +175,8 @@ void GPUContextDX12::SetResourceState(ResourceOwnerDX12* resource, D3D12_RESOURC } } ASSERT(state.CheckResourceState(after)); + state.SetResourceState(after); } - state.SetResourceState(after); } else { @@ -286,7 +286,7 @@ void GPUContextDX12::flushSRVs() return; // Bind all required slots and mark them as not dirty - _srMaskDirtyCompute &= ~srMask; + //_srMaskDirtyCompute &= ~srMask; // TODO: this causes visual artifacts sometimes, maybe use binary SR-dirty flag for all slots? } else { @@ -295,7 +295,7 @@ void GPUContextDX12::flushSRVs() return; // Bind all required slots and mark them as not dirty - _srMaskDirtyGraphics &= ~srMask; + //_srMaskDirtyGraphics &= ~srMask; // TODO: this causes visual artifacts sometimes, maybe use binary SR-dirty flag for all slots? } // Count SRVs required to be bind to the pipeline (the index of the most significant bit that's set) @@ -309,7 +309,7 @@ void GPUContextDX12::flushSRVs() { const auto handle = _srHandles[i]; const auto dimensions = (D3D12_SRV_DIMENSION)header.SrDimensions[i]; - if (handle != nullptr && dimensions) + if (srMask & (1 << i) && handle != nullptr && dimensions) { ASSERT(handle->SrvDimension == dimensions); srcDescriptorRangeStarts[i] = handle->SRV(); @@ -360,7 +360,8 @@ void GPUContextDX12::flushRTVs() if (_rtDepth) { depthBuffer = _rtDepth->DSV(); - SetResourceState(_rtDepth->GetResourceOwner(), D3D12_RESOURCE_STATE_DEPTH_WRITE, _rtDepth->SubresourceIndex); + auto states = _rtDepth->ReadOnlyDepthView ? D3D12_RESOURCE_STATE_DEPTH_READ : D3D12_RESOURCE_STATE_DEPTH_WRITE; + SetResourceState(_rtDepth->GetResourceOwner(), states, _rtDepth->SubresourceIndex); } else { @@ -507,28 +508,33 @@ void GPUContextDX12::onDrawCall() SetResourceState(_ibHandle, D3D12_RESOURCE_STATE_INDEX_BUFFER); } - // If SRV resource is not binded to RTV then transition it to the whole state (GPU-BASED VALIDATION complains about it) - for (uint32 i = 0; i < GPU_MAX_SR_BINDED; i++) + if (_currentState) { - const auto handle = _srHandles[i]; - if (handle != nullptr && handle->GetResourceOwner()) + // If SRV resource is not binded to RTV then transition it to the whole state (GPU-BASED VALIDATION complains about it) + const uint32 srMask = _currentState->GetUsedSRsMask(); + const uint32 srCount = Math::FloorLog2(srMask) + 1; + for (uint32 i = 0; i < srCount; i++) { - const auto resourceOwner = handle->GetResourceOwner(); - bool isRtv = false; - for (int32 j = 0; j < _rtCount; j++) + const auto handle = _srHandles[i]; + if (srMask & (1 << i) && handle != nullptr && handle->GetResourceOwner()) { - if (_rtHandles[j] && _rtHandles[j]->GetResourceOwner() == resourceOwner) + const auto resourceOwner = handle->GetResourceOwner(); + bool isRtv = false; + for (int32 j = 0; j < _rtCount; j++) { - isRtv = true; - break; + if (_rtHandles[j] && _rtHandles[j]->GetResourceOwner() == resourceOwner) + { + isRtv = true; + break; + } + } + if (!isRtv) + { + D3D12_RESOURCE_STATES states = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + if (handle->IsDepthStencilResource()) + states |= D3D12_RESOURCE_STATE_DEPTH_READ; + SetResourceState(handle->GetResourceOwner(), states); } - } - if (!isRtv) - { - D3D12_RESOURCE_STATES states = D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; - if (handle->IsDepthStencilResource()) - states |= D3D12_RESOURCE_STATE_DEPTH_READ; - SetResourceState(handle->GetResourceOwner(), states); } } } @@ -543,36 +549,39 @@ void GPUContextDX12::onDrawCall() #if BUILD_DEBUG // Additional verification of the state - for (int32 i = 0; i < _rtCount; i++) + if (_currentState) { - const auto handle = _rtHandles[i]; - if (handle != nullptr && handle->GetResourceOwner()) + for (int32 i = 0; i < _rtCount; i++) { - const auto& state = handle->GetResourceOwner()->State; - ASSERT((state.GetSubresourceState(handle->SubresourceIndex) & D3D12_RESOURCE_STATE_RENDER_TARGET) != 0); - } - } - const uint32 srMask = _currentState->GetUsedSRsMask(); - const uint32 srCount = Math::FloorLog2(srMask) + 1; - for (uint32 i = 0; i < srCount; i++) - { - const auto handle = _srHandles[i]; - if (handle != nullptr && handle->GetResourceOwner()) - { - const auto& state = handle->GetResourceOwner()->State; - bool isRtv = false; - for (int32 j = 0; j < _rtCount; j++) + const auto handle = _rtHandles[i]; + if (handle != nullptr && handle->GetResourceOwner()) { - if (_rtHandles[j] && _rtHandles[j]->GetResourceOwner() == handle->GetResourceOwner()) - { - isRtv = true; - break; - } + const auto& state = handle->GetResourceOwner()->State; + ASSERT((state.GetSubresourceState(handle->SubresourceIndex) & D3D12_RESOURCE_STATE_RENDER_TARGET) != 0); } - ASSERT((state.GetSubresourceState(handle->SubresourceIndex) & D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) != 0); - if (!isRtv) + } + const uint32 srMask = _currentState->GetUsedSRsMask(); + const uint32 srCount = Math::FloorLog2(srMask) + 1; + for (uint32 i = 0; i < srCount; i++) + { + const auto handle = _srHandles[i]; + if (srMask & (1 << i) && handle != nullptr && handle->GetResourceOwner()) { - ASSERT(state.AreAllSubresourcesSame()); + const auto& state = handle->GetResourceOwner()->State; + bool isRtv = false; + for (int32 j = 0; j < _rtCount; j++) + { + if (_rtHandles[j] && _rtHandles[j]->GetResourceOwner() == handle->GetResourceOwner()) + { + isRtv = true; + break; + } + } + ASSERT((state.GetSubresourceState(handle->SubresourceIndex) & D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) != 0); + if (!isRtv) + { + ASSERT(state.AreAllSubresourcesSame()); + } } } } @@ -1069,10 +1078,10 @@ void GPUContextDX12::ClearState() void GPUContextDX12::FlushState() { // Flush - flushCBs(); - flushSRVs(); - flushRTVs(); - flushUAVs(); + //flushCBs(); + //flushSRVs(); + //flushRTVs(); + //flushUAVs(); flushRBs(); //flushPS(); } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp index 265023fa8..37058884a 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp @@ -450,6 +450,8 @@ void GPUTextureDX12::initHandles() srDesc.Texture2DArray.ResourceMinLODClamp = 0; } _handlesPerSlice[arrayIndex].SetSRV(srDesc); + if (isCubeMap) + _handlesPerSlice[arrayIndex].SrvDimension = D3D12_SRV_DIMENSION_TEXTURE2D; // Hack xD (to reproduce the problem comment this line and use Spot Light with a shadow) } if (useUAV) { @@ -688,6 +690,7 @@ void GPUTextureDX12::initHandles() if (_desc.Flags & GPUTextureFlags::ReadOnlyDepthView) { _handleReadOnlyDepth.Init(this, _device, this, format, msaa); + _handleReadOnlyDepth.ReadOnlyDepthView = true; if (useDSV) { if (isCubeMap) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h index c13df1756..b93828ed5 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.h @@ -73,6 +73,7 @@ public: public: + bool ReadOnlyDepthView = false; void SetRTV(D3D12_RENDER_TARGET_VIEW_DESC& rtvDesc); void SetSRV(D3D12_SHADER_RESOURCE_VIEW_DESC& srvDesc); void SetDSV(D3D12_DEPTH_STENCIL_VIEW_DESC& dsvDesc); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/ResourceOwnerDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/ResourceOwnerDX12.h index acca85e3d..f797c84c0 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/ResourceOwnerDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/ResourceOwnerDX12.h @@ -32,12 +32,21 @@ public: /// /// Returns true if resource state transition is needed in order to use resource in given state. /// - /// The current resource state. - /// the destination resource state. + /// The current resource state. + /// the destination resource state. /// True if need to perform a transition, otherwise false. - FORCE_INLINE static bool IsTransitionNeeded(D3D12_RESOURCE_STATES currentState, D3D12_RESOURCE_STATES targetState) + FORCE_INLINE static bool IsTransitionNeeded(D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES& after) { - return currentState != targetState && ((currentState | targetState) != currentState || targetState == D3D12_RESOURCE_STATE_COMMON); + if (before == D3D12_RESOURCE_STATE_DEPTH_WRITE && after == D3D12_RESOURCE_STATE_DEPTH_READ) + return false; + if (after == D3D12_RESOURCE_STATE_COMMON) + return before != D3D12_RESOURCE_STATE_COMMON; + if (after == D3D12_RESOURCE_STATE_DEPTH_READ) + return ~(before & (D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)) == 0; + const D3D12_RESOURCE_STATES combined = before | after; + if ((combined & (D3D12_RESOURCE_STATE_GENERIC_READ | D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT)) == combined) + after = combined; + return before != after; } }; From cfac2db5d2c490d28688b43f49fae77b4ef5495d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 8 Jun 2021 14:12:56 +0200 Subject: [PATCH 108/198] Add depth test for point/spot lights rendering to optimize far lights rendering --- Source/Engine/Renderer/LightPass.cpp | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Renderer/LightPass.cpp b/Source/Engine/Renderer/LightPass.cpp index 64219ef9c..648f934d3 100644 --- a/Source/Engine/Renderer/LightPass.cpp +++ b/Source/Engine/Renderer/LightPass.cpp @@ -100,26 +100,28 @@ bool LightPass::setupResources() psDesc = GPUPipelineState::Description::DefaultNoDepth; psDesc.BlendMode = BlendingMode::Add; psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RGB; - psDesc.CullMode = CullMode::Normal; psDesc.VS = shader->GetVS("VS_Model"); - if (_psLightPointNormal.Create(psDesc, shader, "PS_Point")) - return true; psDesc.CullMode = CullMode::Inverted; if (_psLightPointInverted.Create(psDesc, shader, "PS_Point")) return true; + psDesc.CullMode = CullMode::Normal; + psDesc.DepthTestEnable = true; + if (_psLightPointNormal.Create(psDesc, shader, "PS_Point")) + return true; } if (!_psLightSpotNormal.IsValid() || !_psLightSpotInverted.IsValid()) { psDesc = GPUPipelineState::Description::DefaultNoDepth; psDesc.BlendMode = BlendingMode::Add; psDesc.BlendMode.RenderTargetWriteMask = BlendingMode::ColorWrite::RGB; - psDesc.CullMode = CullMode::Normal; psDesc.VS = shader->GetVS("VS_Model"); - if (_psLightSpotNormal.Create(psDesc, shader, "PS_Spot")) - return true; psDesc.CullMode = CullMode::Inverted; if (_psLightSpotInverted.Create(psDesc, shader, "PS_Spot")) return true; + psDesc.CullMode = CullMode::Normal; + psDesc.DepthTestEnable = true; + if (_psLightSpotNormal.Create(psDesc, shader, "PS_Spot")) + return true; } if (!_psLightSkyNormal->IsValid() || !_psLightSkyInverted->IsValid()) { @@ -203,7 +205,9 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB PerFrame perFrame; // Bind output - context->SetRenderTarget(lightBuffer); + GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer; + GPUTextureView* depthBufferHandle = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : nullptr; + context->SetRenderTarget(depthBufferHandle, lightBuffer); // Set per frame data GBufferPass::SetInputs(renderContext.View, perFrame.GBuffer); @@ -221,7 +225,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB context->BindSR(0, renderContext.Buffers->GBuffer0); context->BindSR(1, renderContext.Buffers->GBuffer1); context->BindSR(2, renderContext.Buffers->GBuffer2); - context->BindSR(3, renderContext.Buffers->DepthBuffer); + context->BindSR(3, depthBuffer); context->BindSR(4, renderContext.Buffers->GBuffer3); // Check if debug lights @@ -270,7 +274,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB ShadowsPass::Instance()->RenderShadow(renderContext, light, shadowMaskView); // Bind output - context->SetRenderTarget(lightBuffer); + context->SetRenderTarget(depthBufferHandle, lightBuffer); // Set shadow mask context->BindSR(5, shadowMaskView); @@ -325,7 +329,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB ShadowsPass::Instance()->RenderShadow(renderContext, light, shadowMaskView); // Bind output - context->SetRenderTarget(lightBuffer); + context->SetRenderTarget(depthBufferHandle, lightBuffer); // Set shadow mask context->BindSR(5, shadowMaskView); @@ -366,7 +370,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB ShadowsPass::Instance()->RenderShadow(renderContext, light, lightIndex, shadowMaskView); // Bind output - context->SetRenderTarget(lightBuffer); + context->SetRenderTarget(depthBufferHandle, lightBuffer); // Set shadow mask context->BindSR(5, shadowMaskView); From e0b55583999b6348e2c79506f185b6bf9b437ac1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 8 Jun 2021 14:13:26 +0200 Subject: [PATCH 109/198] Fix spline model visibility after enable if was disabled on start --- Source/Engine/Level/Actors/SplineModel.cpp | 8 ++++++++ Source/Engine/Level/Actors/SplineModel.h | 1 + 2 files changed, 9 insertions(+) diff --git a/Source/Engine/Level/Actors/SplineModel.cpp b/Source/Engine/Level/Actors/SplineModel.cpp index 3e874f07f..3c43114f0 100644 --- a/Source/Engine/Level/Actors/SplineModel.cpp +++ b/Source/Engine/Level/Actors/SplineModel.cpp @@ -480,3 +480,11 @@ void SplineModel::OnTransformChanged() OnSplineUpdated(); } + +void SplineModel::OnActiveInTreeChanged() +{ + // Base + ModelInstanceActor::OnActiveInTreeChanged(); + + OnSplineUpdated(); +} diff --git a/Source/Engine/Level/Actors/SplineModel.h b/Source/Engine/Level/Actors/SplineModel.h index 6a0393cdc..5c08ea17f 100644 --- a/Source/Engine/Level/Actors/SplineModel.h +++ b/Source/Engine/Level/Actors/SplineModel.h @@ -125,4 +125,5 @@ protected: // [ModelInstanceActor] void OnTransformChanged() override; + void OnActiveInTreeChanged() override; }; From b30079300908a698e66e2446dd0aaa3f6c988268 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 8 Jun 2021 15:18:04 +0200 Subject: [PATCH 110/198] Add UAV slots dimensions to cache for D3D12 validation --- .../DirectX/DX12/GPUBufferDX12.h | 1 + .../DirectX/DX12/GPUContextDX12.cpp | 46 +++++-------------- .../DirectX/DX12/GPUContextDX12.h | 5 +- .../DirectX/DX12/GPUDeviceDX12.cpp | 3 +- .../DirectX/DX12/GPUPipelineStateDX12.cpp | 4 ++ .../DirectX/DX12/GPUTextureDX12.cpp | 1 + .../DirectX/DX12/IShaderResourceDX12.h | 1 + .../GraphicsDevice/DirectX/DX12/Types.h | 5 ++ .../DirectX/ShaderCompilerDX.cpp | 24 ++++++++++ 9 files changed, 50 insertions(+), 40 deletions(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.h index 48811019a..4d9c367f8 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUBufferDX12.h @@ -28,6 +28,7 @@ public: GPUBufferViewDX12() { SrvDimension = D3D12_SRV_DIMENSION_BUFFER; + UavDimension = D3D12_UAV_DIMENSION_BUFFER; } /// diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index 3fd85fad8..8b085d374 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -70,8 +70,6 @@ GPUContextDX12::GPUContextDX12(GPUDeviceDX12* device, D3D12_COMMAND_LIST_TYPE ty , _rbBufferSize(0) , _srMaskDirtyGraphics(0) , _srMaskDirtyCompute(0) - , _uaMaskDirtyGraphics(0) - , _uaMaskDirtyCompute(0) , _isCompute(0) , _rtDirtyFlag(0) , _psDirtyFlag(0) @@ -208,8 +206,6 @@ void GPUContextDX12::Reset() _rtDepth = nullptr; _srMaskDirtyGraphics = 0; _srMaskDirtyCompute = 0; - _uaMaskDirtyGraphics = 0; - _uaMaskDirtyCompute = 0; _psDirtyFlag = false; _isCompute = false; _currentCompute = nullptr; @@ -309,7 +305,7 @@ void GPUContextDX12::flushSRVs() { const auto handle = _srHandles[i]; const auto dimensions = (D3D12_SRV_DIMENSION)header.SrDimensions[i]; - if (srMask & (1 << i) && handle != nullptr && dimensions) + if (srMask & (1 << i) && handle != nullptr) { ASSERT(handle->SrvDimension == dimensions); srcDescriptorRangeStarts[i] = handle->SRV(); @@ -379,35 +375,30 @@ void GPUContextDX12::flushUAVs() if (_isCompute) { // Skip if no compute shader binded or it doesn't use shader resources - if (_uaMaskDirtyCompute == 0 || _currentCompute == nullptr || (uaMask = _currentCompute->GetBindings().UsedUAsMask) == 0) + if ((uaMask = _currentCompute->GetBindings().UsedUAsMask) == 0) return; - - // Bind all dirty slots and all used slots - uaMask |= _uaMaskDirtyCompute; - _uaMaskDirtyCompute = 0; } else { // Skip if no state binded or it doesn't use shader resources - if (_uaMaskDirtyGraphics == 0 || _currentState == nullptr || (uaMask = _currentState->GetUsedUAsMask()) == 0) + if (_currentState == nullptr || (uaMask = _currentState->GetUsedUAsMask()) == 0) return; - - // Bind all dirty slots and all used slots - uaMask |= _uaMaskDirtyGraphics; - _uaMaskDirtyGraphics = 0; } // Count UAVs required to be bind to the pipeline (the index of the most significant bit that's set) const uint32 uaCount = Math::FloorLog2(uaMask) + 1; - ASSERT(uaCount <= GPU_MAX_UA_BINDED); + ASSERT(uaCount <= GPU_MAX_UA_BINDED + 1); // Fill table with source descriptors - D3D12_CPU_DESCRIPTOR_HANDLE srcDescriptorRangeStarts[GPU_MAX_UA_BINDED]; + DxShaderHeader& header = _currentCompute ? ((GPUShaderProgramCSDX12*)_currentCompute)->Header : _currentState->Header; + D3D12_CPU_DESCRIPTOR_HANDLE srcDescriptorRangeStarts[GPU_MAX_UA_BINDED + 1]; for (uint32 i = 0; i < uaCount; i++) { const auto handle = _uaHandles[i]; - if (handle != nullptr) + const auto dimensions = (D3D12_UAV_DIMENSION)header.UaDimensions[i]; + if (uaMask & (1 << i) && handle != nullptr) { + ASSERT(handle->UavDimension == dimensions); srcDescriptorRangeStarts[i] = handle->UAV(); SetResourceState(handle->GetResourceOwner(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS); } @@ -752,15 +743,8 @@ void GPUContextDX12::SetRenderTarget(GPUTextureView* rt, GPUBuffer* uaOutput) // Set render target normally SetRenderTarget(nullptr, rt); - // Bind UAV output to the last slot - const int32 slot = ARRAY_COUNT(_uaHandles) - 1; - IShaderResourceDX12** lastSlot = &_uaHandles[slot]; - if (*lastSlot != uaOutputDX12) - { - *lastSlot = uaOutputDX12; - _srMaskDirtyGraphics |= 1 << slot; - _srMaskDirtyCompute |= 1 << slot; - } + // Bind UAV output to the 2nd slot (after render target to match DX11 binding model) + _uaHandles[1] = uaOutputDX12; } void GPUContextDX12::ResetSR() @@ -824,13 +808,7 @@ void GPUContextDX12::BindSR(int32 slot, GPUResourceView* view) void GPUContextDX12::BindUA(int32 slot, GPUResourceView* view) { ASSERT(slot >= 0 && slot < GPU_MAX_UA_BINDED); - auto handle = view ? (IShaderResourceDX12*)view->GetNativePtr() : nullptr; - if (_uaHandles[slot] != handle || !handle) - { - _uaMaskDirtyGraphics |= 1 << slot; - _uaMaskDirtyCompute |= 1 << slot; - _uaHandles[slot] = handle; - } + _uaHandles[slot] = view ? (IShaderResourceDX12*)view->GetNativePtr() : nullptr; } void GPUContextDX12::BindVB(const Span& vertexBuffers, const uint32* vertexBuffersOffsets) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h index 7e9994add..1551edc76 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h @@ -47,9 +47,6 @@ private: uint32 _srMaskDirtyGraphics; uint32 _srMaskDirtyCompute; - uint32 _uaMaskDirtyGraphics; - uint32 _uaMaskDirtyCompute; - int32 _isCompute : 1; int32 _rtDirtyFlag : 1; int32 _psDirtyFlag : 1; @@ -58,7 +55,7 @@ private: GPUTextureViewDX12* _rtDepth; GPUTextureViewDX12* _rtHandles[GPU_MAX_RT_BINDED]; IShaderResourceDX12* _srHandles[GPU_MAX_SR_BINDED]; - IShaderResourceDX12* _uaHandles[GPU_MAX_UA_BINDED + 1]; + IShaderResourceDX12* _uaHandles[GPU_MAX_UA_BINDED]; GPUBufferDX12* _ibHandle; GPUBufferDX12* _vbHandles[GPU_MAX_VB_BINDED]; D3D12_INDEX_BUFFER_VIEW _ibView; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index edaceba26..40f860a87 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -467,12 +467,11 @@ bool GPUDeviceDX12::Init() { D3D12_DESCRIPTOR_RANGE& range = r[1]; range.RangeType = D3D12_DESCRIPTOR_RANGE_TYPE_UAV; - range.NumDescriptors = GPU_MAX_UA_BINDED + 1; // the last (additional) UAV register is used as a UAV output (hidden internally) + range.NumDescriptors = GPU_MAX_UA_BINDED; range.BaseShaderRegister = 0; range.RegisterSpace = 0; range.OffsetInDescriptorsFromTableStart = D3D12_DESCRIPTOR_RANGE_OFFSET_APPEND; } - static_assert(GPU_MAX_UA_BINDED == 2, "DX12 backend uses hardcoded single UAV register slot. Update code to support more."); // Root parameters D3D12_ROOT_PARAMETER rootParameters[4]; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.cpp index 08928e94b..0491f8ca2 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUPipelineStateDX12.cpp @@ -139,6 +139,10 @@ bool GPUPipelineStateDX12::Init(const Description& desc) for (uint32 i = 0; i < srCount; i++) \ if (shader->Header.SrDimensions[i]) \ Header.SrDimensions[i] = shader->Header.SrDimensions[i]; \ + auto uaCount = Math::FloorLog2(shader->GetBindings().UsedUAsMask) + 1; \ + for (uint32 i = 0; i < uaCount; i++) \ + if (shader->Header.UaDimensions[i]) \ + Header.UaDimensions[i] = shader->Header.UaDimensions[i]; \ } INIT_SHADER_STAGE(HS, GPUShaderProgramHSDX12); INIT_SHADER_STAGE(DS, GPUShaderProgramDSDX12); diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp index 37058884a..1044ff41b 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUTextureDX12.cpp @@ -280,6 +280,7 @@ void GPUTextureViewDX12::SetDSV(D3D12_DEPTH_STENCIL_VIEW_DESC& dsvDesc) void GPUTextureViewDX12::SetUAV(D3D12_UNORDERED_ACCESS_VIEW_DESC& uavDesc, ID3D12Resource* counterResource) { + UavDimension = uavDesc.ViewDimension; _uav.CreateUAV(_device, _owner->GetResource(), &uavDesc, counterResource); } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/IShaderResourceDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/IShaderResourceDX12.h index 445d83f2a..64c6cf580 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/IShaderResourceDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/IShaderResourceDX12.h @@ -33,6 +33,7 @@ public: int32 SubresourceIndex; D3D12_SRV_DIMENSION SrvDimension = D3D12_SRV_DIMENSION_UNKNOWN; + D3D12_UAV_DIMENSION UavDimension = D3D12_UAV_DIMENSION_UNKNOWN; public: diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/Types.h b/Source/Engine/GraphicsDevice/DirectX/DX12/Types.h index a4f49d793..2a39c400d 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/Types.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/Types.h @@ -13,6 +13,11 @@ struct DxShaderHeader /// byte SrDimensions[32]; + /// + /// The UAV dimensions per-slot. + /// + byte UaDimensions[4]; + // .. rest is just a actual data array }; diff --git a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp index e557a1b98..c03c10ae7 100644 --- a/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp +++ b/Source/Engine/ShadersCompilation/DirectX/ShaderCompilerDX.cpp @@ -379,6 +379,30 @@ bool ShaderCompilerDX::CompileShader(ShaderFunctionMeta& meta, WritePermutationD case D3D_SIT_UAV_CONSUME_STRUCTURED: case D3D_SIT_UAV_RWSTRUCTURED_WITH_COUNTER: bindings.UsedUAsMask |= 1 << resDesc.BindPoint; + switch (resDesc.Dimension) + { + case D3D_SRV_DIMENSION_BUFFER: + header.UaDimensions[resDesc.BindPoint] = 1; // D3D12_UAV_DIMENSION_BUFFER; + break; + case D3D_SRV_DIMENSION_TEXTURE1D: + header.UaDimensions[resDesc.BindPoint] = 2; // D3D12_UAV_DIMENSION_TEXTURE1D; + break; + case D3D_SRV_DIMENSION_TEXTURE1DARRAY: + header.UaDimensions[resDesc.BindPoint] = 3; // D3D12_UAV_DIMENSION_TEXTURE1DARRAY; + break; + case D3D_SRV_DIMENSION_TEXTURE2D: + header.UaDimensions[resDesc.BindPoint] = 4; // D3D12_UAV_DIMENSION_TEXTURE2D; + break; + case D3D_SRV_DIMENSION_TEXTURE2DARRAY: + header.UaDimensions[resDesc.BindPoint] = 5; // D3D12_UAV_DIMENSION_TEXTURE2DARRAY; + break; + case D3D_SRV_DIMENSION_TEXTURE3D: + header.UaDimensions[resDesc.BindPoint] = 8; // D3D12_UAV_DIMENSION_TEXTURE3D; + break; + default: + LOG(Error, "Unknown UAV resource {2} of type {0} at slot {1}", resDesc.Dimension, resDesc.BindPoint, String(resDesc.Name)); + return true; + } break; } } From 4c6fd783e1f673b5afee993ca40cd5c783890715 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 8 Jun 2021 15:18:33 +0200 Subject: [PATCH 111/198] Add Bokeh DoF rendering on D3D12 (works fine after fixes) --- Source/Engine/Renderer/DepthOfFieldPass.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/Engine/Renderer/DepthOfFieldPass.cpp b/Source/Engine/Renderer/DepthOfFieldPass.cpp index d084a6a67..8ebcbb53f 100644 --- a/Source/Engine/Renderer/DepthOfFieldPass.cpp +++ b/Source/Engine/Renderer/DepthOfFieldPass.cpp @@ -38,7 +38,6 @@ bool DepthOfFieldPass::Init() _platformSupportsDoF = limits.HasCompute; _platformSupportsBokeh = _platformSupportsDoF && limits.HasGeometryShaders && limits.HasDrawIndirect && limits.HasAppendConsumeBuffers; - _platformSupportsBokeh &= GPUDevice::Instance->GetRendererType() != RendererType::DirectX12; // TODO: fix bokeh crash on d3d12 (driver issue probably - started to happen recently) _platformSupportsBokeh &= GPUDevice::Instance->GetRendererType() != RendererType::Vulkan; // TODO: add bokeh on Vulkan (draw indirect with UA output from PS) // Create pipeline states From 39f50726a629d5a6d8511039050d377dcb769b4f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 8 Jun 2021 15:21:37 +0200 Subject: [PATCH 112/198] Fix lightmaps bake on D3D12 --- Content/Shaders/BakeLightmap.flax | 4 ++-- Source/Engine/ShadowsOfMordor/Builder.DoWork.cpp | 12 ++++++++++-- .../Engine/ShadowsOfMordor/Builder.Hemispheres.cpp | 2 ++ Source/Engine/ShadowsOfMordor/Builder.cpp | 2 +- Source/Shaders/BakeLightmap.shader | 11 +++++++---- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Content/Shaders/BakeLightmap.flax b/Content/Shaders/BakeLightmap.flax index fac992469..37ae66747 100644 --- a/Content/Shaders/BakeLightmap.flax +++ b/Content/Shaders/BakeLightmap.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc4b141137661d995ff571191150c5997fa6f6576572b5c2281c395a12772d7c -size 16095 +oid sha256:6f3f83f05ee829a7981dc8b832f5b1160f23cf779aa84a7838c63aa7ff0bb336 +size 16284 diff --git a/Source/Engine/ShadowsOfMordor/Builder.DoWork.cpp b/Source/Engine/ShadowsOfMordor/Builder.DoWork.cpp index b144a0d5a..fcacc9de8 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.DoWork.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.DoWork.cpp @@ -188,7 +188,12 @@ bool ShadowsOfMordor::Builder::doWorkInner(DateTime buildStart) tempDesc.Format = HemispheresFormatToPixelFormat[CACHE_NORMALS_FORMAT]; _cacheNormals = RenderTargetPool::Get(tempDesc); if (_cachePositions == nullptr || _cacheNormals == nullptr) + { + LOG(Warning, "Failed to get textures for cache."); + _wasBuildCalled = false; + _isActive = false; return true; + } generateHemispheres(); @@ -228,6 +233,8 @@ bool ShadowsOfMordor::Builder::doWorkInner(DateTime buildStart) if (bounceCount <= 0 || hemispheresCount <= 0) { LOG(Warning, "No data to render"); + _wasBuildCalled = false; + _isActive = false; return true; } @@ -284,8 +291,8 @@ bool ShadowsOfMordor::Builder::doWorkInner(DateTime buildStart) reportProgress(BuildProgressStep::RenderHemispheres, 1.0f); #if DEBUG_EXPORT_HEMISPHERES_PREVIEW - for (int32 sceneIndex = 0; sceneIndex < _scenes.Count(); sceneIndex++) - downloadDebugHemisphereAtlases(_scenes[sceneIndex]); + for (int32 sceneIndex = 0; sceneIndex < _scenes.Count(); sceneIndex++) + downloadDebugHemisphereAtlases(_scenes[sceneIndex]); #endif // References: @@ -391,6 +398,7 @@ int32 ShadowsOfMordor::Builder::doWork() buildFailed = doWorkInner(buildStart); if (buildFailed && !checkBuildCancelled()) { + IsBakingLightmaps = false; OnBuildFinished(buildFailed); return 0; } diff --git a/Source/Engine/ShadowsOfMordor/Builder.Hemispheres.cpp b/Source/Engine/ShadowsOfMordor/Builder.Hemispheres.cpp index 560d922b9..f04d790a8 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Hemispheres.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Hemispheres.cpp @@ -78,6 +78,8 @@ void ShadowsOfMordor::Builder::generateHemispheres() // Fill cache if (runStage(RenderCache)) return; + if (waitForJobDataSync()) + return; // Post-process cache if (runStage(PostprocessCache)) diff --git a/Source/Engine/ShadowsOfMordor/Builder.cpp b/Source/Engine/ShadowsOfMordor/Builder.cpp index 5d90a4ec8..dbbc8e14b 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.cpp @@ -521,7 +521,7 @@ void ShadowsOfMordor::Builder::releaseResources() bool ShadowsOfMordor::Builder::waitForJobDataSync() { bool wasCancelled = false; - const int32 framesToSyncCount = 3; + const int32 framesToSyncCount = 4; while (!wasCancelled) { diff --git a/Source/Shaders/BakeLightmap.shader b/Source/Shaders/BakeLightmap.shader index 14f99b4f4..a1dc90f73 100644 --- a/Source/Shaders/BakeLightmap.shader +++ b/Source/Shaders/BakeLightmap.shader @@ -325,6 +325,8 @@ META_CS(true, FEATURE_LEVEL_SM5) [numthreads(1, 1, 1)] void CS_BlurEmpty(uint3 GroupID : SV_GroupID, uint3 GroupThreadID : SV_GroupThreadID) { + if (GroupID.x >= AtlasSize || GroupID.y > AtlasSize) + return; const int2 location = int2(GroupID.x, GroupID.y); const uint texelAdress = (location.y * AtlasSize + location.x) * NUM_SH_TARGETS; @@ -396,7 +398,7 @@ void CS_BlurEmpty(uint3 GroupID : SV_GroupID, uint3 GroupThreadID : SV_GroupThre #elif defined(_CS_Dilate) -Buffer InputBuffer : register(t0); +Buffer InputBuffer : register(t0); RWBuffer OutputBuffer : register(u0); // Fills the empty lightmap texels with blurred data of the surroundings texels (uses only valid ones) @@ -404,6 +406,8 @@ META_CS(true, FEATURE_LEVEL_SM5) [numthreads(1, 1, 1)] void CS_Dilate(uint3 GroupID : SV_GroupID, uint3 GroupThreadID : SV_GroupThreadID) { + if (GroupID.x >= AtlasSize || GroupID.y > AtlasSize) + return; const int2 location = int2(GroupID.x, GroupID.y); const uint texelAdress = (location.y * AtlasSize + location.x) * NUM_SH_TARGETS; @@ -431,11 +435,9 @@ void CS_Dilate(uint3 GroupID : SV_GroupID, uint3 GroupThreadID : SV_GroupThreadI for (int sampleIndex = 0; sampleIndex < 9; sampleIndex++) { int2 sampleLocation = location + int2(OffsetX[sampleIndex], OffsetY[sampleIndex]); - if (sampleLocation.x >= 0 && sampleLocation.x < AtlasSize && sampleLocation.y >= 0 && sampleLocation.y < AtlasSize) { uint sampleAdress = (sampleLocation.y * AtlasSize + sampleLocation.x) * NUM_SH_TARGETS; - float4 sample0 = InputBuffer[sampleAdress + 0]; float4 sample1 = InputBuffer[sampleAdress + 1]; float4 sample2 = InputBuffer[sampleAdress + 2]; @@ -454,7 +456,6 @@ void CS_Dilate(uint3 GroupID : SV_GroupID, uint3 GroupThreadID : SV_GroupThreadI if (total > 0) { total = 1.0f / total; - OutputBuffer[texelAdress + 0] = total0 * total; OutputBuffer[texelAdress + 1] = total1 * total; OutputBuffer[texelAdress + 2] = total2 * total; @@ -470,6 +471,8 @@ META_CS(true, FEATURE_LEVEL_SM5) [numthreads(1, 1, 1)] void CS_Finalize(uint3 GroupID : SV_GroupID, uint3 GroupThreadID : SV_GroupThreadID) { + if (GroupID.x >= AtlasSize || GroupID.y > AtlasSize) + return; const int2 location = int2(GroupID.x, GroupID.y); const uint texelAdress = (location.y * AtlasSize + location.x) * NUM_SH_TARGETS; From 2bc6ca089fec0de626130decd3ed7d9b8f1ffe39 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 8 Jun 2021 16:11:27 +0200 Subject: [PATCH 113/198] Add `HasTypedUAVLoad` to GPULimits (use for GPU lightmaps support baking detection) #526 --- .../Handlers/BakeLightmapsProgress.cs | 16 +++++--------- Source/Engine/Graphics/GPULimits.h | 5 +++++ .../DirectX/DX11/GPUDeviceDX11.cpp | 22 ++++++------------- .../DirectX/DX12/GPUDeviceDX12.cpp | 1 + .../GraphicsDevice/Null/GPUDeviceNull.cpp | 1 + .../GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp | 1 + Source/Engine/Renderer/DepthOfFieldPass.cpp | 2 -- Source/Engine/Renderer/ShadowsPass.cpp | 12 +++++++--- 8 files changed, 29 insertions(+), 31 deletions(-) diff --git a/Source/Editor/Progress/Handlers/BakeLightmapsProgress.cs b/Source/Editor/Progress/Handlers/BakeLightmapsProgress.cs index 39e46d19e..c2f23285f 100644 --- a/Source/Editor/Progress/Handlers/BakeLightmapsProgress.cs +++ b/Source/Editor/Progress/Handlers/BakeLightmapsProgress.cs @@ -10,8 +10,6 @@ namespace FlaxEditor.Progress.Handlers /// public sealed class BakeLightmapsProgress : ProgressHandler { - private static int _canBake; - /// /// Gets a value indicating whether GPU lightmaps baking is supported on this device. /// @@ -19,15 +17,11 @@ namespace FlaxEditor.Progress.Handlers { get { - if (_canBake == 0) - { - var instance = GPUDevice.Instance; - if (instance == null) - return false; - var limits = instance.Limits; - _canBake = limits.HasCompute && limits.MaximumTexture2DSize >= 8 * 1024 && instance.TotalGraphicsMemory >= 2 * 1024 ? 1 : 2; - } - return _canBake == 1; + var instance = GPUDevice.Instance; + if (instance == null) + return false; + var limits = instance.Limits; + return limits.HasCompute && limits.HasTypedUAVLoad && limits.MaximumTexture2DSize >= 8 * 1024 && instance.TotalGraphicsMemory >= 2 * 1024; } } diff --git a/Source/Engine/Graphics/GPULimits.h b/Source/Engine/Graphics/GPULimits.h index b50714fcd..401ce5178 100644 --- a/Source/Engine/Graphics/GPULimits.h +++ b/Source/Engine/Graphics/GPULimits.h @@ -275,6 +275,11 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(GPULimits); /// API_FIELD() bool HasMultisampleDepthAsSRV; + /// + /// True if device supports reading from typed UAV in shader (common types such as R32G32B32A32, R16G16B16A16, R16, R8). This doesn't apply to single-component 32-bit formats. + /// + API_FIELD() bool HasTypedUAVLoad; + /// /// The maximum amount of texture mip levels. /// diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp index 938570e9f..2f7560141 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUDeviceDX11.cpp @@ -296,26 +296,16 @@ bool GPUDeviceDX11::Init() ASSERT(createdFeatureLevel == targetFeatureLevel); _state = DeviceState::Created; - // Verify compute shader is supported on DirectX 11 - if (createdFeatureLevel >= D3D_FEATURE_LEVEL_11_0) - { - D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS options = { 0 }; - _device->CheckFeatureSupport(D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS, &options, sizeof(options)); - if (!options.ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x) - { - _device->Release(); - _device = nullptr; - LOG(Fatal, "DirectCompute is not supported by this device (DirectX 11 level)."); - return true; - } - } - // Init device limits { auto& limits = Limits; if (createdFeatureLevel >= D3D_FEATURE_LEVEL_11_0) { - limits.HasCompute = true; + D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS d3D10XHardwareOptions = {}; + _device->CheckFeatureSupport(D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS, &d3D10XHardwareOptions, sizeof(d3D10XHardwareOptions)); + D3D11_FEATURE_DATA_D3D11_OPTIONS2 featureDataD3D11Options2 = {}; + _device->CheckFeatureSupport(D3D11_FEATURE_D3D11_OPTIONS2, &featureDataD3D11Options2, sizeof(featureDataD3D11Options2)); + limits.HasCompute = d3D10XHardwareOptions.ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x != 0; limits.HasTessellation = true; limits.HasGeometryShaders = true; limits.HasInstancing = true; @@ -326,6 +316,7 @@ bool GPUDeviceDX11::Init() limits.HasDepthAsSRV = true; limits.HasReadOnlyDepth = true; limits.HasMultisampleDepthAsSRV = true; + limits.HasTypedUAVLoad = featureDataD3D11Options2.TypedUAVLoadAdditionalFormats != 0; limits.MaximumMipLevelsCount = D3D11_REQ_MIP_LEVELS; limits.MaximumTexture1DSize = D3D11_REQ_TEXTURE1D_U_DIMENSION; limits.MaximumTexture1DArraySize = D3D11_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION; @@ -347,6 +338,7 @@ bool GPUDeviceDX11::Init() limits.HasDepthAsSRV = false; limits.HasReadOnlyDepth = createdFeatureLevel == D3D_FEATURE_LEVEL_10_1; limits.HasMultisampleDepthAsSRV = false; + limits.HasTypedUAVLoad = false; limits.MaximumMipLevelsCount = D3D10_REQ_MIP_LEVELS; limits.MaximumTexture1DSize = D3D10_REQ_TEXTURE1D_U_DIMENSION; limits.MaximumTexture1DArraySize = D3D10_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION; diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index 40f860a87..caab244de 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -339,6 +339,7 @@ bool GPUDeviceDX12::Init() limits.HasDepthAsSRV = true; limits.HasReadOnlyDepth = true; limits.HasMultisampleDepthAsSRV = true; + limits.HasTypedUAVLoad = options.TypedUAVLoadAdditionalFormats != 0; limits.MaximumMipLevelsCount = D3D12_REQ_MIP_LEVELS; limits.MaximumTexture1DSize = D3D12_REQ_TEXTURE1D_U_DIMENSION; limits.MaximumTexture1DArraySize = D3D12_REQ_TEXTURE1D_ARRAY_AXIS_DIMENSION; diff --git a/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp b/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp index e20fe0ac0..fa266a34a 100644 --- a/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp +++ b/Source/Engine/GraphicsDevice/Null/GPUDeviceNull.cpp @@ -58,6 +58,7 @@ bool GPUDeviceNull::Init() limits.HasDepthAsSRV = false; limits.HasReadOnlyDepth = false; limits.HasMultisampleDepthAsSRV = false; + limits.HasTypedUAVLoad = false; limits.MaximumMipLevelsCount = 14; limits.MaximumTexture1DSize = 8192; limits.MaximumTexture1DArraySize = 512; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp index 9ca399dad..5c9a7032a 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUDeviceVulkan.cpp @@ -1684,6 +1684,7 @@ bool GPUDeviceVulkan::Init() limits.HasDepthAsSRV = true; limits.HasReadOnlyDepth = true; limits.HasMultisampleDepthAsSRV = !!PhysicalDeviceFeatures.sampleRateShading; + limits.HasTypedUAVLoad = true; limits.MaximumMipLevelsCount = Math::Min(static_cast(log2(PhysicalDeviceLimits.maxImageDimension2D)), GPU_MAX_TEXTURE_MIP_LEVELS); limits.MaximumTexture1DSize = PhysicalDeviceLimits.maxImageDimension1D; limits.MaximumTexture1DArraySize = PhysicalDeviceLimits.maxImageArrayLayers; diff --git a/Source/Engine/Renderer/DepthOfFieldPass.cpp b/Source/Engine/Renderer/DepthOfFieldPass.cpp index 8ebcbb53f..3affcdfda 100644 --- a/Source/Engine/Renderer/DepthOfFieldPass.cpp +++ b/Source/Engine/Renderer/DepthOfFieldPass.cpp @@ -38,8 +38,6 @@ bool DepthOfFieldPass::Init() _platformSupportsDoF = limits.HasCompute; _platformSupportsBokeh = _platformSupportsDoF && limits.HasGeometryShaders && limits.HasDrawIndirect && limits.HasAppendConsumeBuffers; - _platformSupportsBokeh &= GPUDevice::Instance->GetRendererType() != RendererType::Vulkan; // TODO: add bokeh on Vulkan (draw indirect with UA output from PS) - // Create pipeline states if (_platformSupportsDoF) { diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index 3f724dfaa..707aa461d 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -314,11 +314,13 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererPointLightD context->ResetSR(); context->ResetRenderTarget(); const Viewport viewport = renderContext.Task->GetViewport(); + GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer; + GPUTextureView* depthBufferHandle = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View(); context->SetViewportAndScissors(viewport); context->BindSR(0, renderContext.Buffers->GBuffer0); context->BindSR(1, renderContext.Buffers->GBuffer1); context->BindSR(2, renderContext.Buffers->GBuffer2); - context->BindSR(3, renderContext.Buffers->DepthBuffer); + context->BindSR(3, depthBufferHandle); context->BindSR(4, renderContext.Buffers->GBuffer3); // Setup shader data @@ -414,11 +416,13 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererSpotLightDa context->ResetSR(); context->ResetRenderTarget(); const Viewport viewport = renderContext.Task->GetViewport(); + GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer; + GPUTextureView* depthBufferHandle = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View(); context->SetViewportAndScissors(viewport); context->BindSR(0, renderContext.Buffers->GBuffer0); context->BindSR(1, renderContext.Buffers->GBuffer1); context->BindSR(2, renderContext.Buffers->GBuffer2); - context->BindSR(3, renderContext.Buffers->DepthBuffer); + context->BindSR(3, depthBufferHandle); context->BindSR(4, renderContext.Buffers->GBuffer3); // Setup shader data @@ -688,11 +692,13 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectional context->ResetSR(); context->ResetRenderTarget(); const Viewport viewport = renderContext.Task->GetViewport(); + GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer; + GPUTextureView* depthBufferHandle = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View(); context->SetViewportAndScissors(viewport); context->BindSR(0, renderContext.Buffers->GBuffer0); context->BindSR(1, renderContext.Buffers->GBuffer1); context->BindSR(2, renderContext.Buffers->GBuffer2); - context->BindSR(3, renderContext.Buffers->DepthBuffer); + context->BindSR(3, depthBufferHandle); context->BindSR(4, renderContext.Buffers->GBuffer3); // Setup shader data From 484bc409d47b8c0e7780764322422010406ce538 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 9 Jun 2021 10:58:54 +0200 Subject: [PATCH 114/198] Add support for `NotNullItems` option in collections editors #542 --- Source/Editor/CustomEditors/CustomEditor.cs | 3 +- .../CustomEditors/Editors/ArrayEditor.cs | 6 +-- .../CustomEditors/Editors/CollectionEditor.cs | 47 ++++++++++++------- .../CustomEditors/Editors/DictionaryEditor.cs | 42 +++++++---------- .../CustomEditors/Editors/ListEditor.cs | 6 +-- .../Editor/CustomEditors/SyncPointEditor.cs | 4 +- Source/Editor/Options/OptionsModule.cs | 2 +- Source/Editor/Scripting/TypeUtils.cs | 5 +- .../Attributes/CollectionAttribute.cs | 3 +- Source/Engine/Scripting/Scripting.cs | 2 +- Source/Engine/UI/GUI/Style.cs | 1 + 11 files changed, 64 insertions(+), 57 deletions(-) diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs index ca45652b9..caf03bc2f 100644 --- a/Source/Editor/CustomEditors/CustomEditor.cs +++ b/Source/Editor/CustomEditors/CustomEditor.cs @@ -789,8 +789,7 @@ namespace FlaxEditor.CustomEditors /// True if allow to handle this event, otherwise false. protected virtual bool OnDirty(CustomEditor editor, object value, object token = null) { - ParentEditor.OnDirty(editor, value, token); - return true; + return ParentEditor.OnDirty(editor, value, token); } /// diff --git a/Source/Editor/CustomEditors/Editors/ArrayEditor.cs b/Source/Editor/CustomEditors/Editors/ArrayEditor.cs index af7bff167..7f023e8bc 100644 --- a/Source/Editor/CustomEditors/Editors/ArrayEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ArrayEditor.cs @@ -43,7 +43,7 @@ namespace FlaxEditor.CustomEditors.Editors // Copy old values Array.Copy(array, 0, newValues, 0, sharedCount); - if (elementType.IsValueType) + if (elementType.IsValueType || NotNullItems) { // Fill new entries with the last value for (int i = oldSize; i < newSize; i++) @@ -52,7 +52,7 @@ namespace FlaxEditor.CustomEditors.Editors else { // Initialize new entries with default values - var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType), NotNullItems); + var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType)); for (int i = oldSize; i < newSize; i++) newValues.SetValue(defaultValue, i); } @@ -60,7 +60,7 @@ namespace FlaxEditor.CustomEditors.Editors else if (newSize > 0) { // Initialize new entries with default values - var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType), NotNullItems); + var defaultValue = TypeUtils.GetDefaultValue(new ScriptType(elementType)); for (int i = 0; i < newSize; i++) newValues.SetValue(defaultValue, i); } diff --git a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs index 424ff83ca..6c3dda04e 100644 --- a/Source/Editor/CustomEditors/Editors/CollectionEditor.cs +++ b/Source/Editor/CustomEditors/Editors/CollectionEditor.cs @@ -81,6 +81,7 @@ namespace FlaxEditor.CustomEditors.Editors /// Determines if value of collection can be null. /// protected bool NotNullItems; + private IntegerValueElement _size; private Color _background; private int _elementsCount; @@ -107,40 +108,34 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - _readOnly = false; - _canReorderItems = true; - NotNullItems = false; - // No support for different collections for now if (HasDifferentValues || HasDifferentTypes) return; var size = Count; + _readOnly = false; + _canReorderItems = true; + _background = FlaxEngine.GUI.Style.Current.CollectionBackgroundColor; + NotNullItems = false; // Try get CollectionAttribute for collection editor meta var attributes = Values.GetAttributes(); Type overrideEditorType = null; float spacing = 10.0f; var collection = (CollectionAttribute)attributes?.FirstOrDefault(x => x is CollectionAttribute); - if (collection is null) + if (collection != null) { - _readOnly = false; - NotNullItems = false; - _background = FlaxEngine.GUI.Style.Current.CollectionBackgroundColor; - } - else - { - // TODO: handle NotNullItems by filtering child editors SetValue _readOnly = collection.ReadOnly; _canReorderItems = collection.CanReorderItems; NotNullItems = collection.NotNullItems; - _background = collection.BackgroundColor; + if (collection.BackgroundColor.HasValue) + _background = collection.BackgroundColor.Value; overrideEditorType = TypeUtils.GetType(collection.OverrideEditorTypeName).Type; spacing = collection.Spacing; } // Size - if (_readOnly) + if (_readOnly || (NotNullItems && size == 0)) { layout.Label("Size", size.ToString()); } @@ -214,7 +209,8 @@ namespace FlaxEditor.CustomEditors.Editors Text = "+", TooltipText = "Add new item", AnchorPreset = AnchorPresets.TopRight, - Parent = area.ContainerControl + Parent = area.ContainerControl, + Enabled = !NotNullItems || size > 0, }; addButton.Clicked += () => { @@ -229,7 +225,7 @@ namespace FlaxEditor.CustomEditors.Editors TooltipText = "Remove last item", AnchorPreset = AnchorPresets.TopRight, Parent = area.ContainerControl, - Enabled = size > 0 + Enabled = size > 0, }; removeButton.Clicked += () => { @@ -345,5 +341,24 @@ namespace FlaxEditor.CustomEditors.Editors RebuildParentCollection(); } } + + /// + protected override bool OnDirty(CustomEditor editor, object value, object token = null) + { + if (NotNullItems) + { + if (value == null && editor.ParentEditor == this) + return false; + if (editor == this && value is IList list) + { + for (int i = 0; i < list.Count; i++) + { + if (list[i] == null) + return false; + } + } + } + return base.OnDirty(editor, value, token); + } } } diff --git a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs index 99e84b225..ba66aa0fa 100644 --- a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs @@ -165,7 +165,6 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Initialize(LayoutElementsContainer layout) { - // No support for different collections for now if (HasDifferentValues || HasDifferentTypes) return; @@ -176,29 +175,23 @@ namespace FlaxEditor.CustomEditors.Editors var keyType = argTypes[0]; var valueType = argTypes[1]; _canEditKeys = keyType == typeof(string) || keyType.IsPrimitive || keyType.IsEnum; + _background = FlaxEngine.GUI.Style.Current.CollectionBackgroundColor; + _readOnly = false; + _notNullItems = false; // Try get CollectionAttribute for collection editor meta var attributes = Values.GetAttributes(); Type overrideEditorType = null; float spacing = 0.0f; - if (attributes != null) + var collection = (CollectionAttribute)attributes?.FirstOrDefault(x => x is CollectionAttribute); + if (collection != null) { - var collection = (CollectionAttribute)attributes.FirstOrDefault(x => x is CollectionAttribute); - if (collection is null) - { - _readOnly = false; - _notNullItems = false; - _background = FlaxEngine.GUI.Style.Current.CollectionBackgroundColor; - } - else - { - // TODO: handle ReadOnly and NotNullItems by filtering child editors SetValue - _readOnly = collection.ReadOnly; - _notNullItems = collection.NotNullItems; - _background = collection.BackgroundColor; - overrideEditorType = TypeUtils.GetType(collection.OverrideEditorTypeName).Type; - spacing = collection.Spacing; - } + _readOnly = collection.ReadOnly; + _notNullItems = collection.NotNullItems; + if (collection.BackgroundColor.HasValue) + _background = collection.BackgroundColor.Value; + overrideEditorType = TypeUtils.GetType(collection.OverrideEditorTypeName).Type; + spacing = collection.Spacing; } // Size @@ -210,7 +203,7 @@ namespace FlaxEditor.CustomEditors.Editors { _size = layout.IntegerValue("Size"); _size.IntValue.MinValue = 0; - _size.IntValue.MaxValue = ushort.MaxValue; + _size.IntValue.MaxValue = _notNullItems ? size : ushort.MaxValue; _size.IntValue.Value = size; _size.IntValue.ValueChanged += OnSizeChanged; } @@ -259,7 +252,8 @@ namespace FlaxEditor.CustomEditors.Editors Text = "+", TooltipText = "Add new item", AnchorPreset = AnchorPresets.TopRight, - Parent = area.ContainerControl + Parent = area.ContainerControl, + Enabled = !_notNullItems, }; addButton.Clicked += () => { @@ -274,7 +268,7 @@ namespace FlaxEditor.CustomEditors.Editors TooltipText = "Remove last item", AnchorPreset = AnchorPresets.TopRight, Parent = area.ContainerControl, - Enabled = size > 0 + Enabled = size > 0, }; removeButton.Clicked += () => { @@ -414,7 +408,7 @@ namespace FlaxEditor.CustomEditors.Editors } } while (!isUnique); - newValues[Convert.ChangeType(uniqueKey, keyType)] = TypeUtils.GetDefaultValue(new ScriptType(valueType), _notNullItems); + newValues[Convert.ChangeType(uniqueKey, keyType)] = TypeUtils.GetDefaultValue(new ScriptType(valueType)); } else if (keyType.IsEnum) { @@ -435,7 +429,7 @@ namespace FlaxEditor.CustomEditors.Editors } } while (!isUnique && uniqueKeyIndex < enumValues.Length); - newValues[enumValues.GetValue(uniqueKeyIndex)] = TypeUtils.GetDefaultValue(new ScriptType(valueType), _notNullItems); + newValues[enumValues.GetValue(uniqueKeyIndex)] = TypeUtils.GetDefaultValue(new ScriptType(valueType)); } else if (keyType == typeof(string)) { @@ -455,7 +449,7 @@ namespace FlaxEditor.CustomEditors.Editors } } while (!isUnique); - newValues[uniqueKey] = TypeUtils.GetDefaultValue(new ScriptType(valueType), _notNullItems); + newValues[uniqueKey] = TypeUtils.GetDefaultValue(new ScriptType(valueType)); } else { diff --git a/Source/Editor/CustomEditors/Editors/ListEditor.cs b/Source/Editor/CustomEditors/Editors/ListEditor.cs index a8871329a..afae52139 100644 --- a/Source/Editor/CustomEditors/Editors/ListEditor.cs +++ b/Source/Editor/CustomEditors/Editors/ListEditor.cs @@ -46,7 +46,7 @@ namespace FlaxEditor.CustomEditors.Editors for (int i = 0; i < sharedCount; i++) newValues.Add(list[i]); - if (elementType.IsValueType) + if (elementType.IsValueType || NotNullItems) { // Fill new entries with the last value for (int i = oldSize; i < newSize; i++) @@ -55,7 +55,7 @@ namespace FlaxEditor.CustomEditors.Editors else { // Initialize new entries with default values - var defaultValue = Scripting.TypeUtils.GetDefaultValue(elementType, NotNullItems); + var defaultValue = Scripting.TypeUtils.GetDefaultValue(elementType); for (int i = oldSize; i < newSize; i++) newValues.Add(defaultValue); } @@ -63,7 +63,7 @@ namespace FlaxEditor.CustomEditors.Editors else if (newSize > 0) { // Fill new entries with default value - var defaultValue = Scripting.TypeUtils.GetDefaultValue(elementType, NotNullItems); + var defaultValue = Scripting.TypeUtils.GetDefaultValue(elementType); for (int i = oldSize; i < newSize; i++) newValues.Add(defaultValue); } diff --git a/Source/Editor/CustomEditors/SyncPointEditor.cs b/Source/Editor/CustomEditors/SyncPointEditor.cs index 120648230..3555aaea4 100644 --- a/Source/Editor/CustomEditors/SyncPointEditor.cs +++ b/Source/Editor/CustomEditors/SyncPointEditor.cs @@ -112,9 +112,9 @@ namespace FlaxEditor.CustomEditors EndUndoRecord(); _setValueToken = token; - // Mark as modified and don't pass event further + // Mark as modified and don't pass event further to the higher editors (don't call parent) _isDirty = true; - return false; + return true; } /// diff --git a/Source/Editor/Options/OptionsModule.cs b/Source/Editor/Options/OptionsModule.cs index a7f9a6dd0..fa1b46f30 100644 --- a/Source/Editor/Options/OptionsModule.cs +++ b/Source/Editor/Options/OptionsModule.cs @@ -232,7 +232,7 @@ namespace FlaxEditor.Options BorderNormal = Color.FromBgra(0xFF54545C), TextBoxBackground = Color.FromBgra(0xFF333337), TextBoxBackgroundSelected = Color.FromBgra(0xFF3F3F46), - CollectionBackgroundColor = Color.FromBgra(0x16FFFFFF), + CollectionBackgroundColor = Color.FromBgra(0x14CCCCCC), ProgressNormal = Color.FromBgra(0xFF0ad328), // Fonts diff --git a/Source/Editor/Scripting/TypeUtils.cs b/Source/Editor/Scripting/TypeUtils.cs index 3d6362d5d..60f64ab8e 100644 --- a/Source/Editor/Scripting/TypeUtils.cs +++ b/Source/Editor/Scripting/TypeUtils.cs @@ -37,9 +37,8 @@ namespace FlaxEditor.Scripting /// Gets the default value for the given type (can be value type or reference type). /// /// The type. - /// Whether the value can be empty. If that's true, it can't. /// The created instance. - public static object GetDefaultValue(ScriptType type, bool notNull = false) + public static object GetDefaultValue(ScriptType type) { if (type.Type == typeof(string)) return string.Empty; @@ -65,7 +64,7 @@ namespace FlaxEditor.Scripting Utilities.Utils.InitDefaultValues(value); return value; } - if (!notNull && new ScriptType(typeof(object)).IsAssignableFrom(type)) + if (new ScriptType(typeof(object)).IsAssignableFrom(type)) return null; if (type.CanCreateInstance) { diff --git a/Source/Engine/Scripting/Attributes/CollectionAttribute.cs b/Source/Engine/Scripting/Attributes/CollectionAttribute.cs index 7d854d582..8094b35aa 100644 --- a/Source/Engine/Scripting/Attributes/CollectionAttribute.cs +++ b/Source/Engine/Scripting/Attributes/CollectionAttribute.cs @@ -1,7 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; -using FlaxEngine.GUI; namespace FlaxEngine { @@ -39,6 +38,6 @@ namespace FlaxEngine /// /// The collection background color. /// - public Color BackgroundColor = Style.Current.CollectionBackgroundColor; + public Color? BackgroundColor; } } diff --git a/Source/Engine/Scripting/Scripting.cs b/Source/Engine/Scripting/Scripting.cs index baafffd56..4d79324f9 100644 --- a/Source/Engine/Scripting/Scripting.cs +++ b/Source/Engine/Scripting/Scripting.cs @@ -221,7 +221,7 @@ namespace FlaxEngine TextBoxBackground = Color.FromBgra(0xFF333337), ProgressNormal = Color.FromBgra(0xFF0ad328), TextBoxBackgroundSelected = Color.FromBgra(0xFF3F3F46), - CollectionBackgroundColor = Color.FromBgra(0x16FFFFFF), + CollectionBackgroundColor = Color.FromBgra(0x14CCCCCC), SharedTooltip = new Tooltip(), }; style.DragWindow = style.BackgroundSelected * 0.7f; diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index b6ca35f88..a945d1602 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -157,6 +157,7 @@ namespace FlaxEngine.GUI /// [EditorOrder(195)] public Color CollectionBackgroundColor; + /// /// The progress normal color. /// From 9614b7b0141d82379c731545e8579f9a296d9ca3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 9 Jun 2021 10:59:15 +0200 Subject: [PATCH 115/198] Fix scripts projects opening to support adync open when uisng Editor menu option --- Source/Editor/Scripting/CodeEditor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Scripting/CodeEditor.cpp b/Source/Editor/Scripting/CodeEditor.cpp index a95334554..cab5ace17 100644 --- a/Source/Editor/Scripting/CodeEditor.cpp +++ b/Source/Editor/Scripting/CodeEditor.cpp @@ -180,7 +180,7 @@ void CodeEditingManager::OpenSolution(CodeEditorTypes editorType) const auto editor = GetCodeEditor(editorType); if (editor) { - editor->OpenSolution(); + OpenSolution(editor); } else { From 79b03f6b5cb9c747e0faff634be552644cb6cfee Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 9 Jun 2021 11:11:32 +0200 Subject: [PATCH 116/198] Fix cached font usage to use proper unmanaged object validation check (not only managed object ref) #546 --- Source/Engine/Render2D/FontReference.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Render2D/FontReference.cs b/Source/Engine/Render2D/FontReference.cs index 0d725a5a6..98bd0d092 100644 --- a/Source/Engine/Render2D/FontReference.cs +++ b/Source/Engine/Render2D/FontReference.cs @@ -36,8 +36,16 @@ namespace FlaxEngine /// The font. public FontReference(Font font) { - _font = font?.Asset; - _size = font?.Size ?? 30; + if (font) + { + _font = font.Asset; + _size = font.Size; + } + else + { + _font = null; + _size = 30; + } _cachedFont = font; } @@ -85,7 +93,11 @@ namespace FlaxEngine /// Th font or null if descriptor is invalid. public Font GetFont() { - return _cachedFont ?? (_cachedFont = _font?.CreateFont(_size)); + if (_cachedFont) + return _cachedFont; + if (_font) + _cachedFont = _font.CreateFont(_size); + return _cachedFont; } /// From a6ff0c3ed1119fe0f28c0a97b62328c192f63472 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 9 Jun 2021 11:22:13 +0200 Subject: [PATCH 117/198] Fix selecting multiple different UI controls in Editor #548 --- .../Dedicated/UIControlEditor.cs | 51 ++++++++++--------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index dcd70a340..dd43de1ee 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -412,6 +412,13 @@ namespace FlaxEditor.CustomEditors.Dedicated public override void Initialize(LayoutElementsContainer layout) { _cachedType = null; + if (Values.HasNull) + return; + if (HasDifferentTypes) + { + // TODO: support stable editing multiple different control types (via generic way or for transform-only) + return; + } // Set control type button var space = layout.Space(20); @@ -600,34 +607,28 @@ namespace FlaxEditor.CustomEditors.Dedicated private bool _cachedXEq; private bool _cachedYEq; - /// - /// Refreshes if equality of anchors does not correspond to the cached equality - /// - public void RefreshBaseOnAnchorsEquality() - { - if (Values.HasNull) - return; - - GetAnchorEquality(out bool xEq, out bool yEq, ValuesTypes); - if (xEq != _cachedXEq || yEq != _cachedYEq) - { - RebuildLayout(); - return; - } - } - /// public override void Refresh() { - // Automatic layout rebuild if control type gets changed - var type = Values.HasNull ? null : Values[0].GetType(); - if (type != _cachedType) + if (_cachedType != null) { - RebuildLayout(); - return; + // Automatic layout rebuild if control type gets changed + var type = Values.HasNull ? null : Values[0].GetType(); + if (type != _cachedType) + { + RebuildLayout(); + return; + } + + // Refresh anchors + GetAnchorEquality(out bool xEq, out bool yEq, ValuesTypes); + if (xEq != _cachedXEq || yEq != _cachedYEq) + { + RebuildLayout(); + } + + //RefreshValues(); } - RefreshBaseOnAnchorsEquality(); - //RefreshValues(); base.Refresh(); } @@ -672,7 +673,7 @@ namespace FlaxEditor.CustomEditors.Dedicated for (int i = 0; i < uiControls.Count; i++) { var uiControl = (UIControl)uiControls[i]; - string previousName = uiControl.Control?.GetType()?.Name ?? typeof(UIControl).Name; + string previousName = uiControl.Control?.GetType().Name ?? nameof(UIControl); uiControl.Control = (Control)controlType.CreateInstance(); if (uiControl.Name.StartsWith(previousName)) { @@ -687,7 +688,7 @@ namespace FlaxEditor.CustomEditors.Dedicated for (int i = 0; i < uiControls.Count; i++) { var uiControl = (UIControl)uiControls[i]; - string previousName = uiControl.Control?.GetType()?.Name ?? typeof(UIControl).Name; + string previousName = uiControl.Control?.GetType().Name ?? nameof(UIControl); uiControl.Control = (Control)controlType.CreateInstance(); if (uiControl.Name.StartsWith(previousName)) { From f237d5d87d2c19b3c435adb3c966fabb234e4fa2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 9 Jun 2021 12:16:50 +0200 Subject: [PATCH 118/198] Post merge fixes to font reference as class #420 --- Source/Engine/Render2D/FontReference.cs | 22 +++++++++++++--------- Source/Engine/UI/GUI/Common/Label.cs | 8 ++++---- Source/Engine/UI/GUI/Common/RichTextBox.cs | 18 ++++++++++++------ Source/Engine/UI/GUI/Style.cs | 8 ++++---- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/Source/Engine/Render2D/FontReference.cs b/Source/Engine/Render2D/FontReference.cs index 449134b94..22b0e02c8 100644 --- a/Source/Engine/Render2D/FontReference.cs +++ b/Source/Engine/Render2D/FontReference.cs @@ -18,6 +18,16 @@ namespace FlaxEngine [NoSerialize] private Font _cachedFont; + /// + /// Initializes a new instance of the struct. + /// + public FontReference() + { + _font = null; + _size = 30; + _cachedFont = null; + } + /// /// Initializes a new instance of the struct. /// @@ -61,9 +71,7 @@ namespace FlaxEngine if (_font != value) { _font = value; - - if (_cachedFont) - _cachedFont = null; + _cachedFont = null; } } } @@ -80,9 +88,7 @@ namespace FlaxEngine if (_size != value) { _size = value; - - if (_cachedFont) - _cachedFont = null; + _cachedFont = null; } } } @@ -104,9 +110,7 @@ namespace FlaxEngine /// Determines whether the specified is equal to this instance. /// /// The to compare with this instance. - /// - /// true if the specified is equal to this instance; otherwise, false. - /// + /// true if the specified is equal to this instance; otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(FontReference other) { diff --git a/Source/Engine/UI/GUI/Common/Label.cs b/Source/Engine/UI/GUI/Common/Label.cs index 1b06e1867..ba5763f31 100644 --- a/Source/Engine/UI/GUI/Common/Label.cs +++ b/Source/Engine/UI/GUI/Common/Label.cs @@ -185,8 +185,8 @@ namespace FlaxEngine.GUI AutoFocus = false; var style = Style.Current; Font = new FontReference(style.FontMedium); - TextColor = Style.Current.Foreground; - TextColorHighlighted = Style.Current.Foreground; + TextColor = style.Foreground; + TextColorHighlighted = style.Foreground; } /// @@ -196,8 +196,8 @@ namespace FlaxEngine.GUI AutoFocus = false; var style = Style.Current; Font = new FontReference(style.FontMedium); - TextColor = Style.Current.Foreground; - TextColorHighlighted = Style.Current.Foreground; + TextColor = style.Foreground; + TextColorHighlighted = style.Foreground; } /// diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.cs b/Source/Engine/UI/GUI/Common/RichTextBox.cs index 837ec0ec7..8b2cc5cb3 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.cs @@ -9,12 +9,7 @@ namespace FlaxEngine.GUI /// public class RichTextBox : RichTextBoxBase { - private TextBlockStyle _textStyle = new TextBlockStyle - { - Font = new FontReference(Style.Current.FontMedium), - Color = Style.Current.Foreground, - BackgroundSelectedBrush = new SolidColorBrush(Style.Current.BackgroundSelected), - }; + private TextBlockStyle _textStyle; /// /// The text style applied to the whole text. @@ -30,6 +25,17 @@ namespace FlaxEngine.GUI } } + public RichTextBox() + { + var style = Style.Current; + _textStyle = new TextBlockStyle + { + Font = new FontReference(style.FontMedium), + Color = style.Foreground, + BackgroundSelectedBrush = new SolidColorBrush(style.BackgroundSelected), + }; + } + /// protected override void OnParseTextBlocks() { diff --git a/Source/Engine/UI/GUI/Style.cs b/Source/Engine/UI/GUI/Style.cs index a945d1602..5b77e8682 100644 --- a/Source/Engine/UI/GUI/Style.cs +++ b/Source/Engine/UI/GUI/Style.cs @@ -22,7 +22,7 @@ namespace FlaxEngine.GUI [EditorOrder(10)] public Font FontTitle { - get => _fontTitle.GetFont(); + get => _fontTitle?.GetFont(); set => _fontTitle = new FontReference(value); } @@ -36,7 +36,7 @@ namespace FlaxEngine.GUI [EditorOrder(20)] public Font FontLarge { - get => _fontLarge.GetFont(); + get => _fontLarge?.GetFont(); set => _fontLarge = new FontReference(value); } @@ -50,7 +50,7 @@ namespace FlaxEngine.GUI [EditorOrder(30)] public Font FontMedium { - get => _fontMedium.GetFont(); + get => _fontMedium?.GetFont(); set => _fontMedium = new FontReference(value); } @@ -64,7 +64,7 @@ namespace FlaxEngine.GUI [EditorOrder(40)] public Font FontSmall { - get => _fontSmall.GetFont(); + get => _fontSmall?.GetFont(); set => _fontSmall = new FontReference(value); } From 384172050d7498865c39db4810ecfd66d5dbdffe Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 9 Jun 2021 13:25:04 +0200 Subject: [PATCH 119/198] Add missing doc comment --- Source/Engine/UI/GUI/Common/RichTextBox.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Engine/UI/GUI/Common/RichTextBox.cs b/Source/Engine/UI/GUI/Common/RichTextBox.cs index 8b2cc5cb3..3e89ed584 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBox.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBox.cs @@ -25,6 +25,9 @@ namespace FlaxEngine.GUI } } + /// + /// Initializes a new instance of the class. + /// public RichTextBox() { var style = Style.Current; From c38b81d68264aab0f4b6271228abb9d80820dc4e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 9 Jun 2021 13:27:37 +0200 Subject: [PATCH 120/198] Fix errors when changing UIControl type in prefab #548 --- Source/Editor/CustomEditors/CustomEditor.cs | 4 ++ .../Dedicated/UIControlEditor.cs | 39 +++++++------------ .../CustomEditors/Values/ValueContainer.cs | 11 +++++- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/Source/Editor/CustomEditors/CustomEditor.cs b/Source/Editor/CustomEditors/CustomEditor.cs index caf03bc2f..c1c004d43 100644 --- a/Source/Editor/CustomEditors/CustomEditor.cs +++ b/Source/Editor/CustomEditors/CustomEditor.cs @@ -142,6 +142,10 @@ namespace FlaxEditor.CustomEditors /// public void RebuildLayout() { + // Skip rebuilding during init + if (CurrentCustomEditor == this) + return; + // Special case for root objects to run normal layout build if (_presenter.Selection == Values) { diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index dd43de1ee..b860d9826 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -412,8 +412,6 @@ namespace FlaxEditor.CustomEditors.Dedicated public override void Initialize(LayoutElementsContainer layout) { _cachedType = null; - if (Values.HasNull) - return; if (HasDifferentTypes) { // TODO: support stable editing multiple different control types (via generic way or for transform-only) @@ -626,8 +624,6 @@ namespace FlaxEditor.CustomEditors.Dedicated { RebuildLayout(); } - - //RefreshValues(); } base.Refresh(); @@ -663,39 +659,32 @@ namespace FlaxEditor.CustomEditors.Dedicated cm.Show(button.Parent, button.BottomLeft); } + private void SetType(ref ScriptType controlType, UIControl uiControl) + { + string previousName = uiControl.Control?.GetType().Name ?? nameof(UIControl); + uiControl.Control = (Control)controlType.CreateInstance(); + if (uiControl.Name.StartsWith(previousName)) + { + string newName = controlType.Name + uiControl.Name.Substring(previousName.Length); + uiControl.Name = StringUtils.IncrementNameNumber(newName, x => uiControl.Parent.GetChild(x) == null); + } + } + private void SetType(ScriptType controlType) { var uiControls = ParentEditor.Values; - if (Presenter.Undo != null) + if (Presenter.Undo?.Enabled ?? false) { using (new UndoMultiBlock(Presenter.Undo, uiControls, "Set Control Type")) { for (int i = 0; i < uiControls.Count; i++) - { - var uiControl = (UIControl)uiControls[i]; - string previousName = uiControl.Control?.GetType().Name ?? nameof(UIControl); - uiControl.Control = (Control)controlType.CreateInstance(); - if (uiControl.Name.StartsWith(previousName)) - { - string newName = controlType.Name + uiControl.Name.Substring(previousName.Length); - uiControl.Name = StringUtils.IncrementNameNumber(newName, x => uiControl.Parent.GetChild(x) == null); - } - } + SetType(ref controlType, (UIControl)uiControls[i]); } } else { for (int i = 0; i < uiControls.Count; i++) - { - var uiControl = (UIControl)uiControls[i]; - string previousName = uiControl.Control?.GetType().Name ?? nameof(UIControl); - uiControl.Control = (Control)controlType.CreateInstance(); - if (uiControl.Name.StartsWith(previousName)) - { - string newName = controlType.Name + uiControl.Name.Substring(previousName.Length); - uiControl.Name = StringUtils.IncrementNameNumber(newName, x => uiControl.Parent.GetChild(x) == null); - } - } + SetType(ref controlType, (UIControl)uiControls[i]); } ParentEditor.RebuildLayout(); diff --git a/Source/Editor/CustomEditors/Values/ValueContainer.cs b/Source/Editor/CustomEditors/Values/ValueContainer.cs index 49d3b5591..9bdbc4bcf 100644 --- a/Source/Editor/CustomEditors/Values/ValueContainer.cs +++ b/Source/Editor/CustomEditors/Values/ValueContainer.cs @@ -255,8 +255,15 @@ namespace FlaxEditor.CustomEditors if (instanceValues._referenceValue == null && !instanceValues.Type.IsValueType) return; - _referenceValue = Info.GetValue(instanceValues._referenceValue); - _hasReferenceValue = true; + try + { + _referenceValue = Info.GetValue(instanceValues._referenceValue); + _hasReferenceValue = true; + } + catch + { + // Ignore error if reference value has different type or is invalid for this member + } } } From 5a710ccd7a4a65a563c68a291614c568a3d7d729 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 9 Jun 2021 14:49:36 +0200 Subject: [PATCH 121/198] Hide GetPxShape impl --- Source/Engine/Physics/Colliders/Collider.cpp | 5 +++++ Source/Engine/Physics/Colliders/Collider.h | 6 +----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Source/Engine/Physics/Colliders/Collider.cpp b/Source/Engine/Physics/Colliders/Collider.cpp index acf6af5d1..cc45f3f51 100644 --- a/Source/Engine/Physics/Colliders/Collider.cpp +++ b/Source/Engine/Physics/Colliders/Collider.cpp @@ -31,6 +31,11 @@ Collider::Collider(const SpawnParams& params) Material.Changed.Bind(this); } +PxShape* Collider::GetPxShape() const +{ + return _shape; +} + void Collider::SetIsTrigger(bool value) { if (value == _isTrigger || !CanBeTrigger()) diff --git a/Source/Engine/Physics/Colliders/Collider.h b/Source/Engine/Physics/Colliders/Collider.h index ca88ea66c..50ac3b8db 100644 --- a/Source/Engine/Physics/Colliders/Collider.h +++ b/Source/Engine/Physics/Colliders/Collider.h @@ -32,11 +32,7 @@ public: /// /// Gets the collider shape PhysX object. /// - /// The PhysX Shape object or null. - FORCE_INLINE PxShape* GetPxShape() const - { - return _shape; - } + PxShape* GetPxShape() const; /// /// Gets the 'IsTrigger' flag. From 05d095b35e7aa5561ea474cfced711612d5cfc0d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 9 Jun 2021 17:15:32 +0200 Subject: [PATCH 122/198] Add support for using pointers in scripting API events --- .../Bindings/BindingsGenerator.CSharp.cs | 56 ++++++++++++++----- .../Bindings/BindingsGenerator.Cpp.cs | 34 +++++++---- Source/Tools/Flax.Build/Bindings/TypeInfo.cs | 2 +- 3 files changed, 66 insertions(+), 26 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 41b4eb796..65fa1c6f7 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -225,8 +225,7 @@ namespace Flax.Build.Bindings if (apiType.IsScriptingObject) return typeInfo.Type.Replace("::", "."); - - if (typeInfo.IsPtr && (apiType is LangType || apiType.IsPod)) + if (typeInfo.IsPtr && apiType.IsPod) return typeInfo.Type.Replace("::", ".") + '*'; } @@ -581,6 +580,46 @@ namespace Flax.Build.Bindings contents.AppendLine(); + var useCustomDelegateSignature = false; + for (var i = 0; i < paramsCount; i++) + { + var result = GenerateCSharpNativeToManaged(buildData, eventInfo.Type.GenericArgs[i], classInfo); + if (result[result.Length - 1] == '*') + useCustomDelegateSignature = true; + CppParamsWrappersCache[i] = result; + } + + string eventSignature; + if (useCustomDelegateSignature) + { + contents.Append(indent).Append($"/// The delegate for event {eventInfo.Name}.").AppendLine(); + contents.Append(indent).Append("public delegate void ").Append(eventInfo.Name).Append("Delegate("); + for (var i = 0; i < paramsCount; i++) + { + if (i != 0) + contents.Append(", "); + contents.Append(CppParamsWrappersCache[i]).Append(" arg").Append(i); + } + contents.Append(");").AppendLine().AppendLine(); + eventSignature = "event " + eventInfo.Name + "Delegate"; + } + else + { + eventSignature = "event Action"; + if (paramsCount != 0) + { + eventSignature += '<'; + for (var i = 0; i < paramsCount; i++) + { + if (i != 0) + eventSignature += ", "; + CppParamsWrappersCache[i] = GenerateCSharpNativeToManaged(buildData, eventInfo.Type.GenericArgs[i], classInfo); + eventSignature += CppParamsWrappersCache[i]; + } + eventSignature += '>'; + } + } + foreach (var comment in eventInfo.Comment) { if (comment.Contains("/// ")) @@ -598,19 +637,6 @@ namespace Flax.Build.Bindings contents.Append("private "); if (eventInfo.IsStatic) contents.Append("static "); - string eventSignature = "event Action"; - if (paramsCount != 0) - { - eventSignature += '<'; - for (var i = 0; i < paramsCount; i++) - { - if (i != 0) - eventSignature += ", "; - CppParamsWrappersCache[i] = GenerateCSharpNativeToManaged(buildData, eventInfo.Type.GenericArgs[i], classInfo); - eventSignature += CppParamsWrappersCache[i]; - } - eventSignature += '>'; - } contents.Append(eventSignature); contents.Append(' ').AppendLine(eventInfo.Name); contents.Append(indent).Append('{').AppendLine(); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 70627d97e..721e58347 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -75,24 +75,38 @@ namespace Flax.Build.Bindings private static string GenerateCppWrapperNativeToManagedParam(BuildData buildData, StringBuilder contents, TypeInfo paramType, string paramName, ApiTypeInfo caller) { - var nativeToManaged = GenerateCppWrapperNativeToManaged(buildData, paramType, caller, out _, null); + var nativeToManaged = GenerateCppWrapperNativeToManaged(buildData, paramType, caller, out var managedTypeAsNative, null); + string result; if (!string.IsNullOrEmpty(nativeToManaged)) - nativeToManaged = string.Format(nativeToManaged, paramName); - else - nativeToManaged = paramName; - if ((!paramType.IsPtr && paramType.Type != "StringView" && paramType.Type != "StringAnsiView") || paramType.IsPod(buildData, caller)) { - if (nativeToManaged == paramName) + result = string.Format(nativeToManaged, paramName); + if (managedTypeAsNative[managedTypeAsNative.Length - 1] == '*') { - nativeToManaged = '&' + nativeToManaged; + // Pass pointer value } else { - contents.Append($" auto __param{paramName} = {nativeToManaged};").AppendLine(); - nativeToManaged = $"&__param{paramName}"; + // Pass as pointer to local variable converted for managed runtime + if (paramType.IsPtr) + result = string.Format(nativeToManaged, '*' + paramName); + contents.Append($" auto __param_{paramName} = {result};").AppendLine(); + result = $"&__param_{paramName}"; } } - return $"(void*){nativeToManaged}"; + else + { + result = paramName; + if (paramType.IsPtr || managedTypeAsNative[managedTypeAsNative.Length - 1] == '*') + { + // Pass pointer value + } + else + { + // Pass as pointer to value + result = '&' + result; + } + } + return $"(void*){result}"; } public static string GenerateCppWrapperNativeToVariant(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller, string value) diff --git a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs index 9268e5998..bfead9218 100644 --- a/Source/Tools/Flax.Build/Bindings/TypeInfo.cs +++ b/Source/Tools/Flax.Build/Bindings/TypeInfo.cs @@ -44,7 +44,7 @@ namespace Flax.Build.Bindings return true; // Hardcoded cases - if (Type == "String" || Type == "Array") + if (Type == "String" || Type == "Array" || Type == "StringView" || Type == "StringAnsi" || Type == "StringAnsiView") return false; // Fallback to default From 9fd62cf9aab4902704f319de176f0c738fe37e6e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 10 Jun 2021 10:26:57 +0200 Subject: [PATCH 123/198] Add support for using references in scripting API events --- .../Bindings/BindingsGenerator.CSharp.cs | 14 ++++++++++++-- .../Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 13 +++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index 65fa1c6f7..d3d6eb0f6 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -583,8 +583,9 @@ namespace Flax.Build.Bindings var useCustomDelegateSignature = false; for (var i = 0; i < paramsCount; i++) { - var result = GenerateCSharpNativeToManaged(buildData, eventInfo.Type.GenericArgs[i], classInfo); - if (result[result.Length - 1] == '*') + var paramType = eventInfo.Type.GenericArgs[i]; + var result = GenerateCSharpNativeToManaged(buildData, paramType, classInfo); + if ((paramType.IsRef && !paramType.IsConst && paramType.IsPod(buildData, classInfo)) || result[result.Length - 1] == '*') useCustomDelegateSignature = true; CppParamsWrappersCache[i] = result; } @@ -596,8 +597,11 @@ namespace Flax.Build.Bindings contents.Append(indent).Append("public delegate void ").Append(eventInfo.Name).Append("Delegate("); for (var i = 0; i < paramsCount; i++) { + var paramType = eventInfo.Type.GenericArgs[i]; if (i != 0) contents.Append(", "); + if (paramType.IsRef && !paramType.IsConst && paramType.IsPod(buildData, classInfo)) + contents.Append("ref "); contents.Append(CppParamsWrappersCache[i]).Append(" arg").Append(i); } contents.Append(");").AppendLine().AppendLine(); @@ -668,9 +672,12 @@ namespace Flax.Build.Bindings contents.Append($"void Internal_{eventInfo.Name}_Invoke("); for (var i = 0; i < paramsCount; i++) { + var paramType = eventInfo.Type.GenericArgs[i]; if (i != 0) contents.Append(", "); contents.Append(CppParamsWrappersCache[i]); + if (paramType.IsRef && !paramType.IsConst && paramType.IsPod(buildData, classInfo)) + contents.Append("*"); contents.Append(" arg").Append(i); } contents.Append(')').AppendLine(); @@ -679,8 +686,11 @@ namespace Flax.Build.Bindings contents.Append('('); for (var i = 0; i < paramsCount; i++) { + var paramType = eventInfo.Type.GenericArgs[i]; if (i != 0) contents.Append(", "); + if (paramType.IsRef && !paramType.IsConst && paramType.IsPod(buildData, classInfo)) + contents.Append("ref *"); contents.Append("arg").Append(i); } contents.Append(");").AppendLine(); diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 721e58347..16ef34de8 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -73,7 +73,7 @@ namespace Flax.Build.Bindings return sb.ToString(); } - private static string GenerateCppWrapperNativeToManagedParam(BuildData buildData, StringBuilder contents, TypeInfo paramType, string paramName, ApiTypeInfo caller) + private static string GenerateCppWrapperNativeToManagedParam(BuildData buildData, StringBuilder contents, TypeInfo paramType, string paramName, ApiTypeInfo caller, bool isOut) { var nativeToManaged = GenerateCppWrapperNativeToManaged(buildData, paramType, caller, out var managedTypeAsNative, null); string result; @@ -96,7 +96,12 @@ namespace Flax.Build.Bindings else { result = paramName; - if (paramType.IsPtr || managedTypeAsNative[managedTypeAsNative.Length - 1] == '*') + if (paramType.IsRef && !paramType.IsConst && !isOut) + { + // Pass reference as a pointer + result = '&' + result; + } + else if (paramType.IsPtr || managedTypeAsNative[managedTypeAsNative.Length - 1] == '*') { // Pass pointer value } @@ -1038,7 +1043,7 @@ namespace Flax.Build.Bindings for (var i = 0; i < functionInfo.Parameters.Count; i++) { var parameterInfo = functionInfo.Parameters[i]; - var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, parameterInfo.Type, parameterInfo.Name, classInfo); + var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, parameterInfo.Type, parameterInfo.Name, classInfo, parameterInfo.IsOut); contents.Append($" params[{i}] = {paramValue};").AppendLine(); } @@ -1295,7 +1300,7 @@ namespace Flax.Build.Bindings { var paramType = eventInfo.Type.GenericArgs[i]; var paramName = "arg" + i; - var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, paramType, paramName, classInfo); + var paramValue = GenerateCppWrapperNativeToManagedParam(buildData, contents, paramType, paramName, classInfo, false); contents.Append($" params[{i}] = {paramValue};").AppendLine(); } if (eventInfo.IsStatic) From 907f289ea7583b9144a50dc31e4cc05bb0936cc2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 10 Jun 2021 11:01:35 +0200 Subject: [PATCH 124/198] Cleanup asset references code --- Source/Engine/Content/Asset.cpp | 85 +++++++++++++++++++++ Source/Engine/Content/AssetReference.h | 75 ++---------------- Source/Engine/Content/WeakAssetReference.h | 52 ++----------- Source/Engine/Scripting/ScriptingObject.cpp | 11 +++ Source/Engine/Scripting/ScriptingObject.h | 17 +---- 5 files changed, 110 insertions(+), 130 deletions(-) diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index 7fcd2f405..d2e150ac5 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -13,6 +13,91 @@ #include "Engine/Threading/ConcurrentTaskQueue.h" #include +AssetReferenceBase::~AssetReferenceBase() +{ + if (_asset) + { + _asset->OnLoaded.Unbind(this); + _asset->OnUnloaded.Unbind(this); + _asset->RemoveReference(); + _asset = nullptr; + } +} + +String AssetReferenceBase::ToString() const +{ + return _asset ? _asset->ToString() : TEXT(""); +} + +void AssetReferenceBase::OnSet(Asset* asset) +{ + auto e = _asset; + if (e != asset) + { + if (e) + { + e->OnLoaded.Unbind(this); + e->OnUnloaded.Unbind(this); + e->RemoveReference(); + } + _asset = e = asset; + if (e) + { + e->AddReference(); + e->OnLoaded.Bind(this); + e->OnUnloaded.Bind(this); + } + Changed(); + if (e && e->IsLoaded()) + Loaded(); + } +} + +void AssetReferenceBase::OnLoaded(Asset* asset) +{ + ASSERT(_asset == asset); + Loaded(); +} + +void AssetReferenceBase::OnUnloaded(Asset* asset) +{ + ASSERT(_asset == asset); + Unload(); + OnSet(nullptr); +} + +WeakAssetReferenceBase::~WeakAssetReferenceBase() +{ + if (_asset) + _asset->OnUnloaded.Unbind(this); +} + +String WeakAssetReferenceBase::ToString() const +{ + return _asset ? _asset->ToString() : TEXT(""); +} + +void WeakAssetReferenceBase::OnSet(Asset* asset) +{ + auto e = _asset; + if (e != asset) + { + if (e) + e->OnUnloaded.Unbind(this); + _asset = e = asset; + if (e) + e->OnUnloaded.Bind(this); + } +} + +void WeakAssetReferenceBase::OnUnloaded(Asset* asset) +{ + ASSERT(_asset == asset); + Unload(); + asset->OnUnloaded.Unbind(this); + asset = nullptr; +} + Asset::Asset(const SpawnParams& params, const AssetInfo* info) : ManagedScriptingObject(params) , _refCount(0) diff --git a/Source/Engine/Content/AssetReference.h b/Source/Engine/Content/AssetReference.h index 0956e82be..68adf4a25 100644 --- a/Source/Engine/Content/AssetReference.h +++ b/Source/Engine/Content/AssetReference.h @@ -15,7 +15,7 @@ public: protected: - Asset* _asset; + Asset* _asset = nullptr; public: @@ -39,31 +39,18 @@ public: /// /// Initializes a new instance of the class. /// - AssetReferenceBase() - : _asset(nullptr) - { - } + AssetReferenceBase() = default; /// /// Finalizes an instance of the class. /// - ~AssetReferenceBase() - { - if (_asset) - { - _asset->OnLoaded.Unbind(this); - _asset->OnUnloaded.Unbind(this); - _asset->RemoveReference(); - _asset = nullptr; - } - } + ~AssetReferenceBase(); public: /// /// Gets the asset ID or Guid::Empty if not set. /// - /// The asset ID or Guid::Empty if not set. FORCE_INLINE Guid GetID() const { return _asset ? _asset->GetID() : Guid::Empty; @@ -72,7 +59,6 @@ public: /// /// Gets managed instance object (or null if no asset set). /// - /// Mono managed object FORCE_INLINE MonoObject* GetManagedInstance() const { return _asset ? _asset->GetOrCreateManagedInstance() : nullptr; @@ -81,55 +67,13 @@ public: /// /// Gets the asset property value as string. /// - /// The string. - String ToString() const - { - static String NullStr = TEXT(""); - return _asset ? _asset->ToString() : NullStr; - } + String ToString() const; protected: - void OnSet(Asset* asset) - { - auto e = _asset; - if (e != asset) - { - if (e) - { - e->OnLoaded.Unbind(this); - e->OnUnloaded.Unbind(this); - e->RemoveReference(); - } - _asset = e = asset; - if (e) - { - e->AddReference(); - e->OnLoaded.Bind(this); - e->OnUnloaded.Bind(this); - } - Changed(); - if (e && e->IsLoaded()) - Loaded(); - } - } - - void OnAssetLoaded(Asset* asset) - { - if (_asset == asset) - { - Loaded(); - } - } - - void OnAssetUnloaded(Asset* asset) - { - if (_asset == asset) - { - Unload(); - OnSet(nullptr); - } - } + void OnSet(Asset* asset); + void OnLoaded(Asset* asset); + void OnUnloaded(Asset* asset); }; /// @@ -220,7 +164,6 @@ public: /// /// Implicit conversion to the bool. /// - /// The asset. FORCE_INLINE operator T*() const { return (T*)_asset; @@ -229,7 +172,6 @@ public: /// /// Implicit conversion to the asset. /// - /// True if asset has been set, otherwise false. FORCE_INLINE operator bool() const { return _asset != nullptr; @@ -238,7 +180,6 @@ public: /// /// Implicit conversion to the asset. /// - /// The asset. FORCE_INLINE T* operator->() const { return (T*)_asset; @@ -247,7 +188,6 @@ public: /// /// Gets the asset. /// - /// The asset. FORCE_INLINE T* Get() const { return (T*)_asset; @@ -256,7 +196,6 @@ public: /// /// Gets the asset as a given type (static cast). /// - /// The asset. template FORCE_INLINE U* As() const { diff --git a/Source/Engine/Content/WeakAssetReference.h b/Source/Engine/Content/WeakAssetReference.h index 49401bf25..7e4202fe4 100644 --- a/Source/Engine/Content/WeakAssetReference.h +++ b/Source/Engine/Content/WeakAssetReference.h @@ -15,7 +15,7 @@ public: protected: - Asset* _asset; + Asset* _asset = nullptr; public: @@ -29,29 +29,18 @@ public: /// /// Initializes a new instance of the class. /// - WeakAssetReferenceBase() - : _asset(nullptr) - { - } + WeakAssetReferenceBase() = default; /// /// Finalizes an instance of the class. /// - ~WeakAssetReferenceBase() - { - if (_asset) - { - _asset->OnUnloaded.Unbind(this); - _asset = nullptr; - } - } + ~WeakAssetReferenceBase(); public: /// /// Gets the asset ID or Guid::Empty if not set. /// - /// The asset ID or Guid::Empty if not set. FORCE_INLINE Guid GetID() const { return _asset ? _asset->GetID() : Guid::Empty; @@ -60,7 +49,6 @@ public: /// /// Gets managed instance object (or null if no asset set). /// - /// Mono managed object FORCE_INLINE MonoObject* GetManagedInstance() const { return _asset ? _asset->GetOrCreateManagedInstance() : nullptr; @@ -69,35 +57,12 @@ public: /// /// Gets the asset property value as string. /// - /// The string. - String ToString() const - { - static String NullStr = TEXT(""); - return _asset ? _asset->ToString() : NullStr; - } + String ToString() const; protected: - void OnSet(Asset* asset) - { - auto e = _asset; - if (e != asset) - { - if (e) - e->OnUnloaded.Unbind(this); - _asset = e = asset; - if (e) - e->OnUnloaded.Bind(this); - } - } - - void OnAssetUnloaded(Asset* asset) - { - ASSERT(_asset == asset); - Unload(); - asset->OnUnloaded.Unbind(this); - asset = nullptr; - } + void OnSet(Asset* asset); + void OnUnloaded(Asset* asset); }; /// @@ -175,7 +140,6 @@ public: /// /// Implicit conversion to the bool. /// - /// The asset. FORCE_INLINE operator T*() const { return (T*)_asset; @@ -184,7 +148,6 @@ public: /// /// Implicit conversion to the asset. /// - /// True if asset has been set, otherwise false. FORCE_INLINE operator bool() const { return _asset != nullptr; @@ -193,7 +156,6 @@ public: /// /// Implicit conversion to the asset. /// - /// The asset. FORCE_INLINE T* operator->() const { return (T*)_asset; @@ -202,7 +164,6 @@ public: /// /// Gets the asset. /// - /// The asset. FORCE_INLINE T* Get() const { return (T*)_asset; @@ -211,7 +172,6 @@ public: /// /// Gets the asset as a given type (static cast). /// - /// The asset. template FORCE_INLINE U* As() const { diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index dc9e27865..5208b1560 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -53,6 +53,17 @@ MonoObject* ScriptingObject::GetManagedInstance() const return _gcHandle ? mono_gchandle_get_target(_gcHandle) : nullptr; } +MonoObject* ScriptingObject::GetOrCreateManagedInstance() const +{ + MonoObject* managedInstance = GetManagedInstance(); + if (!managedInstance) + { + const_cast(this)->CreateManaged(); + managedInstance = GetManagedInstance(); + } + return managedInstance; +} + MClass* ScriptingObject::GetClass() const { return _type ? _type.GetType().ManagedClass : nullptr; diff --git a/Source/Engine/Scripting/ScriptingObject.h b/Source/Engine/Scripting/ScriptingObject.h index 9eb375fc5..1d2365d64 100644 --- a/Source/Engine/Scripting/ScriptingObject.h +++ b/Source/Engine/Scripting/ScriptingObject.h @@ -68,22 +68,11 @@ public: /// /// Gets the managed instance object or creates it if missing. /// - /// The Mono managed object. - FORCE_INLINE MonoObject* GetOrCreateManagedInstance() const - { - MonoObject* managedInstance = GetManagedInstance(); - if (!managedInstance) - { - const_cast(this)->CreateManaged(); - managedInstance = GetManagedInstance(); - } - return managedInstance; - } + MonoObject* GetOrCreateManagedInstance() const; /// /// Determines whether managed instance is alive. /// - /// True if managed object has been created and exists, otherwise false. FORCE_INLINE bool HasManagedInstance() const { return GetManagedInstance() != nullptr; @@ -92,7 +81,6 @@ public: /// /// Gets the unique object ID. /// - /// The unique object ID. FORCE_INLINE const Guid& GetID() const { return _id; @@ -101,7 +89,6 @@ public: /// /// Gets the scripting type handle of this object. /// - /// The scripting type handle. FORCE_INLINE const ScriptingTypeHandle& GetTypeHandle() const { return _type; @@ -110,7 +97,6 @@ public: /// /// Gets the scripting type of this object. /// - /// The scripting type. FORCE_INLINE const ScriptingType& GetType() const { return _type.GetType(); @@ -119,7 +105,6 @@ public: /// /// Gets the type class of this object. /// - /// The Mono class. MClass* GetClass() const; public: From 101e803ca6387d1be8ea31fa1b1abafacae44724 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 10 Jun 2021 11:07:36 +0200 Subject: [PATCH 125/198] Fix WeakAssetReference handling asset unload --- Source/Engine/Content/Asset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index d2e150ac5..d3d5830b2 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -95,7 +95,7 @@ void WeakAssetReferenceBase::OnUnloaded(Asset* asset) ASSERT(_asset == asset); Unload(); asset->OnUnloaded.Unbind(this); - asset = nullptr; + _asset = nullptr; } Asset::Asset(const SpawnParams& params, const AssetInfo* info) From 82a39e5d98fb6c24fbc5c67f69834223c8d13090 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 10 Jun 2021 15:58:32 +0200 Subject: [PATCH 126/198] Fix `Asset::ToString()` to not depend on refs count and look simpler --- Source/Engine/Content/Asset.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index d3d5830b2..4614f33cf 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -111,7 +111,7 @@ Asset::Asset(const SpawnParams& params, const AssetInfo* info) String Asset::ToString() const { - return String::Format(TEXT("{0}: {1}, \'{2}\', Refs: {3}"), GetTypeName(), GetID(), GetPath(), GetReferencesCount()); + return String::Format(TEXT("{0}, {1}, {2}"), GetTypeName(), GetID(), GetPath()); } void Asset::OnDeleteObject() From 90d9f83f8c7933bb7ba6e72ba5e7597eeca39837 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 10 Jun 2021 15:59:01 +0200 Subject: [PATCH 127/198] Fix crash due to `SamplesBuffer::Add` bug --- .../Engine/Core/Collections/SamplesBuffer.h | 36 ++++--------------- 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/Source/Engine/Core/Collections/SamplesBuffer.h b/Source/Engine/Core/Collections/SamplesBuffer.h index 95a68bcd0..b6058701e 100644 --- a/Source/Engine/Core/Collections/SamplesBuffer.h +++ b/Source/Engine/Core/Collections/SamplesBuffer.h @@ -13,27 +13,14 @@ class SamplesBuffer { protected: - int32 _count; + int32 _count = 0; T _data[Size]; - // Note: first element is _data[0] - -public: - - /// - /// Initializes a new instance of the class. - /// - SamplesBuffer() - : _count(0) - { - } - public: /// /// Gets amount of elements in the collection. /// - /// The collection size. FORCE_INLINE int32 Count() const { return _count; @@ -42,7 +29,6 @@ public: /// /// Gets amount of elements that can be added to the collection. /// - /// The collection capacity. FORCE_INLINE int32 Capacity() const { return Size; @@ -51,7 +37,6 @@ public: /// /// Returns true if collection has any elements added. /// - /// True if collection has any elements added, otherwise false. FORCE_INLINE bool HasItems() const { return _count > 0; @@ -60,7 +45,6 @@ public: /// /// Returns true if collection is empty. /// - /// True if collection is empty, otherwise false. FORCE_INLINE bool IsEmpty() const { return _count < 1; @@ -69,7 +53,6 @@ public: /// /// Gets pointer to the first element in the collection. /// - /// Pointer to the first element in the collection. FORCE_INLINE T* Get() const { return _data; @@ -111,7 +94,6 @@ public: /// /// Gets the first element value. /// - /// The first element. FORCE_INLINE T First() const { ASSERT(HasItems()); @@ -121,7 +103,6 @@ public: /// /// Gets last element value. /// - /// The last element. FORCE_INLINE T Last() const { ASSERT(HasItems()); @@ -136,15 +117,13 @@ public: /// The value to add. void Add(const T& value) { - // Move previous elements - for (int32 i = _count - 1; i > 0; i--) - _data[i] = _data[i - 1]; - - // Update elements count if (_count != Size) _count++; - - // Insert element + if (_count > 1) + { + for (int32 i = _count - 1; i > 0; i--) + _data[i] = _data[i - 1]; + } _data[0] = value; } @@ -171,7 +150,6 @@ public: /// /// Gets the minimum value in the buffer. /// - /// The minimum value. T Minimum() const { ASSERT(HasItems()); @@ -187,7 +165,6 @@ public: /// /// Gets the maximum value in the buffer. /// - /// The maximum value. T Maximum() const { ASSERT(HasItems()); @@ -203,7 +180,6 @@ public: /// /// Gets the average value in the buffer. /// - /// The average value. T Average() const { ASSERT(HasItems()); From 14bc9501c7b7170d583074d9f4d83020759eb374 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 10 Jun 2021 16:59:59 +0200 Subject: [PATCH 128/198] Cleanup some code --- Source/Engine/Core/Math/BoundingFrustum.h | 2 +- Source/Engine/Core/Math/CollisionsHelper.cpp | 42 +- Source/Engine/Core/Math/CollisionsHelper.h | 2 - Source/Engine/Graphics/GPUDevice.cpp | 9 + Source/Engine/Graphics/GPUResource.h | 9 +- Source/Engine/Graphics/GPUResourceState.h | 19 - .../Engine/Graphics/Materials/MaterialInfo.h | 359 +----------------- .../Graphics/Materials/MaterialParams.cpp | 79 ++++ .../Graphics/Materials/MaterialParams.h | 8 - .../Handlers/ModelsStreamingHandler.cpp | 2 +- Source/Engine/Streaming/StreamableResource.h | 22 +- 11 files changed, 114 insertions(+), 439 deletions(-) diff --git a/Source/Engine/Core/Math/BoundingFrustum.h b/Source/Engine/Core/Math/BoundingFrustum.h index 9052521fc..6056fd82e 100644 --- a/Source/Engine/Core/Math/BoundingFrustum.h +++ b/Source/Engine/Core/Math/BoundingFrustum.h @@ -248,7 +248,7 @@ public: /// True if the current BoundingFrustum intersects a BoundingBox, otherwise false. FORCE_INLINE bool Intersects(const BoundingBox& box) const { - return CollisionsHelper::FrustumIntersectsBox(*this, box); + return CollisionsHelper::FrustumContainsBox(*this, box) != ContainmentType::Disjoint; } private: diff --git a/Source/Engine/Core/Math/CollisionsHelper.cpp b/Source/Engine/Core/Math/CollisionsHelper.cpp index d485e5fba..00ff0ea9b 100644 --- a/Source/Engine/Core/Math/CollisionsHelper.cpp +++ b/Source/Engine/Core/Math/CollisionsHelper.cpp @@ -1301,43 +1301,33 @@ bool CollisionsHelper::FrustumIntersectsBox(const BoundingFrustum& frustum, cons ContainmentType CollisionsHelper::FrustumContainsBox(const BoundingFrustum& frustum, const BoundingBox& box) { - Vector3 p, n; - Plane plane; auto result = ContainmentType::Contains; - for (int32 i = 0; i < 6; i++) { - plane = frustum.GetPlane(i); - GetBoxToPlanePVertexNVertex(box, plane.Normal, p, n); - + Plane plane = frustum.GetPlane(i); + Vector3 p = box.Minimum; + if (plane.Normal.X >= 0) + p.X = box.Maximum.X; + if (plane.Normal.Y >= 0) + p.Y = box.Maximum.Y; + if (plane.Normal.Z >= 0) + p.Z = box.Maximum.Z; if (PlaneIntersectsPoint(plane, p) == PlaneIntersectionType::Back) return ContainmentType::Disjoint; - if (PlaneIntersectsPoint(plane, n) == PlaneIntersectionType::Back) + p = box.Maximum; + if (plane.Normal.X >= 0) + p.X = box.Minimum.X; + if (plane.Normal.Y >= 0) + p.Y = box.Minimum.Y; + if (plane.Normal.Z >= 0) + p.Z = box.Minimum.Z; + if (PlaneIntersectsPoint(plane, p) == PlaneIntersectionType::Back) result = ContainmentType::Intersects; } return result; } -void CollisionsHelper::GetBoxToPlanePVertexNVertex(const BoundingBox& box, const Vector3& planeNormal, Vector3& p, Vector3& n) -{ - p = box.Minimum; - if (planeNormal.X >= 0) - p.X = box.Maximum.X; - if (planeNormal.Y >= 0) - p.Y = box.Maximum.Y; - if (planeNormal.Z >= 0) - p.Z = box.Maximum.Z; - - n = box.Maximum; - if (planeNormal.X >= 0) - n.X = box.Minimum.X; - if (planeNormal.Y >= 0) - n.Y = box.Minimum.Y; - if (planeNormal.Z >= 0) - n.Z = box.Minimum.Z; -} - bool CollisionsHelper::LineIntersectsLine(const Vector2& l1p1, const Vector2& l1p2, const Vector2& l2p1, const Vector2& l2p2) { float q = (l1p1.Y - l2p1.Y) * (l2p2.X - l2p1.X) - (l1p1.X - l2p1.X) * (l2p2.Y - l2p1.Y); diff --git a/Source/Engine/Core/Math/CollisionsHelper.h b/Source/Engine/Core/Math/CollisionsHelper.h index 692b86dbe..9ef940c3e 100644 --- a/Source/Engine/Core/Math/CollisionsHelper.h +++ b/Source/Engine/Core/Math/CollisionsHelper.h @@ -550,8 +550,6 @@ public: static ContainmentType FrustumContainsBox(const BoundingFrustum& frustum, const BoundingBox& box); - static void GetBoxToPlanePVertexNVertex(const BoundingBox& box, const Vector3& planeNormal, Vector3& p, Vector3& n); - /// /// Determines whether a line intersects with the other line. /// diff --git a/Source/Engine/Graphics/GPUDevice.cpp b/Source/Engine/Graphics/GPUDevice.cpp index f6be31923..cdf1da0d3 100644 --- a/Source/Engine/Graphics/GPUDevice.cpp +++ b/Source/Engine/Graphics/GPUDevice.cpp @@ -175,6 +175,15 @@ void GPUResource::OnReleaseGPU() { } +String GPUResource::ToString() const +{ +#if GPU_ENABLE_RESOURCE_NAMING + return GetName(); +#else + return TEXT("GPU Resource"); +#endif +} + void GPUResource::OnDeleteObject() { ReleaseGPU(); diff --git a/Source/Engine/Graphics/GPUResource.h b/Source/Engine/Graphics/GPUResource.h index 8f014d318..92fa9c769 100644 --- a/Source/Engine/Graphics/GPUResource.h +++ b/Source/Engine/Graphics/GPUResource.h @@ -126,14 +126,7 @@ protected: public: // [PersistentScriptingObject] - String ToString() const override - { -#if GPU_ENABLE_RESOURCE_NAMING - return GetName(); -#else - return TEXT("GPU Resource"); -#endif - } + String ToString() const override; void OnDeleteObject() override; }; diff --git a/Source/Engine/Graphics/GPUResourceState.h b/Source/Engine/Graphics/GPUResourceState.h index c5083b952..5ea11c3c3 100644 --- a/Source/Engine/Graphics/GPUResourceState.h +++ b/Source/Engine/Graphics/GPUResourceState.h @@ -45,12 +45,8 @@ public: void Initialize(uint32 subresourceCount, StateType initialState, bool usePerSubresourceTracking) { ASSERT(_subresourceState.IsEmpty() && subresourceCount > 0); - - // Initialize state _allSubresourcesSame = true; _resourceState = initialState; - - // Allocate space for per-subresource state tracking if (usePerSubresourceTracking && subresourceCount > 1) _subresourceState.Resize(subresourceCount, false); #if BUILD_DEBUG @@ -82,29 +78,19 @@ public: bool CheckResourceState(StateType state) const { if (_allSubresourcesSame) - { return state == _resourceState; - } - - // Check all subresources for (int32 i = 0; i < _subresourceState.Count(); i++) { if (_subresourceState[i] != state) - { return false; - } } - return true; } StateType GetSubresourceState(uint32 subresourceIndex) const { if (_allSubresourcesSame) - { return _resourceState; - } - ASSERT(subresourceIndex >= 0 && subresourceIndex < static_cast(_subresourceState.Count())); return _subresourceState[subresourceIndex]; } @@ -113,12 +99,9 @@ public: { _allSubresourcesSame = 1; _resourceState = state; - #if BUILD_DEBUG for (int32 i = 0; i < _subresourceState.Count(); i++) - { _subresourceState[i] = InvalidState; - } #endif } @@ -137,9 +120,7 @@ public: if (_allSubresourcesSame) { for (int32 i = 0; i < _subresourceState.Count(); i++) - { _subresourceState[i] = _resourceState; - } _allSubresourcesSame = 0; #if BUILD_DEBUG _resourceState = InvalidState; diff --git a/Source/Engine/Graphics/Materials/MaterialInfo.h b/Source/Engine/Graphics/Materials/MaterialInfo.h index 7edf7da54..f3fc4fa67 100644 --- a/Source/Engine/Graphics/Materials/MaterialInfo.h +++ b/Source/Engine/Graphics/Materials/MaterialInfo.h @@ -77,15 +77,6 @@ API_ENUM() enum class MaterialBlendMode : byte Multiply = 3, }; -// Old material blending mode used before introducing MaterialShadingModel -// [Deprecated on 10.09.2018, expires on 10.05.2019] -enum class OldMaterialBlendMode : byte -{ - Opaque = 0, - Transparent = 1, - Unlit = 2, -}; - /// /// Material shading modes. Defines how material inputs and properties are combined to result the final surface color. /// @@ -417,243 +408,6 @@ API_ENUM() enum class MaterialSceneTextures ShadingModel = 10, }; -/// -/// Material info structure - version 1 -/// [Deprecated on 10.09.2018, expires on 10.05.2019] -/// -struct MaterialInfo1 -{ - int32 Version; - MaterialDomain Domain; - OldMaterialBlendMode BlendMode; - MaterialFlags_Deprecated Flags; - - /// - /// Compare structure with other one - /// - /// Other structure to compare - /// True if both structures are equal - bool operator==(const MaterialInfo1& other) const - { - return Domain == other.Domain - && BlendMode == other.BlendMode - && Flags == other.Flags; - } -}; - -/// -/// Material info structure - version 2 -/// [Deprecated on 10.09.2018, expires on 10.05.2019] -/// -struct MaterialInfo2 -{ - int32 Version; - MaterialDomain Domain; - OldMaterialBlendMode BlendMode; - MaterialFlags_Deprecated Flags; - MaterialTransparentLighting_Deprecated TransparentLighting; - - /// - /// Compare structure with other one - /// - /// Other structure to compare - /// True if both structures are equal - bool operator==(const MaterialInfo2& other) const - { - return Domain == other.Domain - && BlendMode == other.BlendMode - && TransparentLighting == other.TransparentLighting - && Flags == other.Flags; - } -}; - -/// -/// Material info structure - version 3 -/// [Deprecated on 10.09.2018, expires on 10.05.2019] -/// -struct MaterialInfo3 -{ - MaterialDomain Domain; - OldMaterialBlendMode BlendMode; - MaterialFlags_Deprecated Flags; - MaterialTransparentLighting_Deprecated TransparentLighting; - - /// - /// Compare structure with other one - /// - /// Other structure to compare - /// True if both structures are equal - bool operator==(const MaterialInfo3& other) const - { - return Domain == other.Domain - && BlendMode == other.BlendMode - && TransparentLighting == other.TransparentLighting - && Flags == other.Flags; - } -}; - -/// -/// Material info structure - version 4 -/// [Deprecated on 10.09.2018, expires on 10.05.2019] -/// -struct MaterialInfo4 -{ - MaterialDomain Domain; - OldMaterialBlendMode BlendMode; - MaterialFlags_Deprecated Flags; - MaterialTransparentLighting_Deprecated TransparentLighting; - MaterialPostFxLocation PostFxLocation; - - /// - /// Compare structure with other one - /// - /// Other structure to compare - /// True if both structures are equal - bool operator==(const MaterialInfo4& other) const - { - return Domain == other.Domain - && BlendMode == other.BlendMode - && TransparentLighting == other.TransparentLighting - && PostFxLocation == other.PostFxLocation - && Flags == other.Flags; - } -}; - -/// -/// Material info structure - version 5 -/// [Deprecated on 10.09.2018, expires on 10.05.2019] -/// -struct MaterialInfo5 -{ - MaterialDomain Domain; - OldMaterialBlendMode BlendMode; - MaterialFlags_Deprecated Flags; - MaterialTransparentLighting_Deprecated TransparentLighting; - MaterialPostFxLocation PostFxLocation; - float MaskThreshold; - float OpacityThreshold; - - /// - /// Compare structure with other one - /// - /// Other structure to compare - /// True if both structures are equal - bool operator==(const MaterialInfo5& other) const - { - return Domain == other.Domain - && BlendMode == other.BlendMode - && TransparentLighting == other.TransparentLighting - && PostFxLocation == other.PostFxLocation - && Math::NearEqual(MaskThreshold, other.MaskThreshold) - && Math::NearEqual(OpacityThreshold, other.OpacityThreshold) - && Flags == other.Flags; - } -}; - -/// -/// Material info structure - version 6 -/// [Deprecated on 10.09.2018, expires on 10.05.2019] -/// -struct MaterialInfo6 -{ - MaterialDomain Domain; - OldMaterialBlendMode BlendMode; - MaterialFlags_Deprecated Flags; - MaterialTransparentLighting_Deprecated TransparentLighting; - MaterialDecalBlendingMode DecalBlendingMode; - MaterialPostFxLocation PostFxLocation; - float MaskThreshold; - float OpacityThreshold; - - MaterialInfo6() - { - } - - MaterialInfo6(const MaterialInfo5& other) - { - Domain = other.Domain; - BlendMode = other.BlendMode; - Flags = other.Flags; - TransparentLighting = other.TransparentLighting; - DecalBlendingMode = MaterialDecalBlendingMode::Translucent; - PostFxLocation = other.PostFxLocation; - MaskThreshold = other.MaskThreshold; - OpacityThreshold = other.OpacityThreshold; - } - - /// - /// Compare structure with other one - /// - /// Other structure to compare - /// True if both structures are equal - bool operator==(const MaterialInfo6& other) const - { - return Domain == other.Domain - && BlendMode == other.BlendMode - && TransparentLighting == other.TransparentLighting - && DecalBlendingMode == other.DecalBlendingMode - && PostFxLocation == other.PostFxLocation - && Math::NearEqual(MaskThreshold, other.MaskThreshold) - && Math::NearEqual(OpacityThreshold, other.OpacityThreshold) - && Flags == other.Flags; - } -}; - -/// -/// Material info structure - version 7 -/// [Deprecated on 13.09.2018, expires on 13.12.2018] -/// -struct MaterialInfo7 -{ - MaterialDomain Domain; - OldMaterialBlendMode BlendMode; - MaterialFlags_Deprecated Flags; - MaterialTransparentLighting_Deprecated TransparentLighting; - MaterialDecalBlendingMode DecalBlendingMode; - MaterialPostFxLocation PostFxLocation; - float MaskThreshold; - float OpacityThreshold; - TessellationMethod TessellationMode; - int32 MaxTessellationFactor; - - MaterialInfo7() - { - } - - MaterialInfo7(const MaterialInfo6& other) - { - Domain = other.Domain; - BlendMode = other.BlendMode; - Flags = other.Flags; - TransparentLighting = other.TransparentLighting; - DecalBlendingMode = other.DecalBlendingMode; - PostFxLocation = other.PostFxLocation; - MaskThreshold = other.MaskThreshold; - OpacityThreshold = other.OpacityThreshold; - TessellationMode = TessellationMethod::None; - MaxTessellationFactor = 15; - } - - /// - /// Compare structure with other one - /// - /// Other structure to compare - /// True if both structures are equal - bool operator==(const MaterialInfo7& other) const - { - return Domain == other.Domain - && BlendMode == other.BlendMode - && TransparentLighting == other.TransparentLighting - && DecalBlendingMode == other.DecalBlendingMode - && PostFxLocation == other.PostFxLocation - && Math::NearEqual(MaskThreshold, other.MaskThreshold) - && Math::NearEqual(OpacityThreshold, other.OpacityThreshold) - && Flags == other.Flags - && TessellationMode == other.TessellationMode - && MaxTessellationFactor == other.MaxTessellationFactor; - } -}; - /// /// Material info structure - version 8 /// [Deprecated on 24.07.2019, expires on 10.05.2021] @@ -676,53 +430,7 @@ struct MaterialInfo8 { } - MaterialInfo8(const MaterialInfo7& other) - { - Domain = other.Domain; - switch (other.BlendMode) - { - case OldMaterialBlendMode::Opaque: - BlendMode = MaterialBlendMode::Opaque; - ShadingModel = MaterialShadingModel::Lit; - break; - case OldMaterialBlendMode::Transparent: - BlendMode = MaterialBlendMode::Transparent; - ShadingModel = MaterialShadingModel::Lit; - break; - case OldMaterialBlendMode::Unlit: - BlendMode = MaterialBlendMode::Opaque; - ShadingModel = MaterialShadingModel::Unlit; - break; - } - Flags = other.Flags; - TransparentLighting = other.TransparentLighting; - DecalBlendingMode = other.DecalBlendingMode; - PostFxLocation = other.PostFxLocation; - MaskThreshold = other.MaskThreshold; - OpacityThreshold = other.OpacityThreshold; - TessellationMode = other.TessellationMode; - MaxTessellationFactor = other.MaxTessellationFactor; - } - - /// - /// Compare structure with other one - /// - /// Other structure to compare - /// True if both structures are equal - bool operator==(const MaterialInfo8& other) const - { - return Domain == other.Domain - && BlendMode == other.BlendMode - && ShadingModel == other.ShadingModel - && TransparentLighting == other.TransparentLighting - && DecalBlendingMode == other.DecalBlendingMode - && PostFxLocation == other.PostFxLocation - && Math::NearEqual(MaskThreshold, other.MaskThreshold) - && Math::NearEqual(OpacityThreshold, other.OpacityThreshold) - && Flags == other.Flags - && TessellationMode == other.TessellationMode - && MaxTessellationFactor == other.MaxTessellationFactor; - } + bool operator==(const MaterialInfo8& other) const; }; /// @@ -796,69 +504,8 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(MaterialInfo); { } - MaterialInfo(const MaterialInfo8& other) - { - Domain = other.Domain; - BlendMode = other.BlendMode; - ShadingModel = other.ShadingModel; - UsageFlags = MaterialUsageFlags::None; - if (other.Flags & MaterialFlags_Deprecated::UseMask) - UsageFlags |= MaterialUsageFlags::UseMask; - if (other.Flags & MaterialFlags_Deprecated::UseEmissive) - UsageFlags |= MaterialUsageFlags::UseEmissive; - if (other.Flags & MaterialFlags_Deprecated::UsePositionOffset) - UsageFlags |= MaterialUsageFlags::UsePositionOffset; - if (other.Flags & MaterialFlags_Deprecated::UseVertexColor) - UsageFlags |= MaterialUsageFlags::UseVertexColor; - if (other.Flags & MaterialFlags_Deprecated::UseNormal) - UsageFlags |= MaterialUsageFlags::UseNormal; - if (other.Flags & MaterialFlags_Deprecated::UseDisplacement) - UsageFlags |= MaterialUsageFlags::UseDisplacement; - if (other.Flags & MaterialFlags_Deprecated::UseRefraction) - UsageFlags |= MaterialUsageFlags::UseRefraction; - FeaturesFlags = MaterialFeaturesFlags::None; - if (other.Flags & MaterialFlags_Deprecated::Wireframe) - FeaturesFlags |= MaterialFeaturesFlags::Wireframe; - if (other.Flags & MaterialFlags_Deprecated::TransparentDisableDepthTest && BlendMode != MaterialBlendMode::Opaque) - FeaturesFlags |= MaterialFeaturesFlags::DisableDepthTest; - if (other.Flags & MaterialFlags_Deprecated::TransparentDisableFog && BlendMode != MaterialBlendMode::Opaque) - FeaturesFlags |= MaterialFeaturesFlags::DisableFog; - if (other.Flags & MaterialFlags_Deprecated::TransparentDisableReflections && BlendMode != MaterialBlendMode::Opaque) - FeaturesFlags |= MaterialFeaturesFlags::DisableReflections; - if (other.Flags & MaterialFlags_Deprecated::DisableDepthWrite) - FeaturesFlags |= MaterialFeaturesFlags::DisableDepthWrite; - if (other.Flags & MaterialFlags_Deprecated::TransparentDisableDistortion && BlendMode != MaterialBlendMode::Opaque) - FeaturesFlags |= MaterialFeaturesFlags::DisableDistortion; - if (other.Flags & MaterialFlags_Deprecated::InputWorldSpaceNormal) - FeaturesFlags |= MaterialFeaturesFlags::InputWorldSpaceNormal; - if (other.Flags & MaterialFlags_Deprecated::UseDitheredLODTransition) - FeaturesFlags |= MaterialFeaturesFlags::DitheredLODTransition; - if (other.BlendMode != MaterialBlendMode::Opaque && other.TransparentLighting == MaterialTransparentLighting_Deprecated::None) - ShadingModel = MaterialShadingModel::Unlit; - DecalBlendingMode = other.DecalBlendingMode; - PostFxLocation = other.PostFxLocation; - CullMode = other.Flags & MaterialFlags_Deprecated::TwoSided ? ::CullMode::TwoSided : ::CullMode::Normal; - MaskThreshold = other.MaskThreshold; - OpacityThreshold = other.OpacityThreshold; - TessellationMode = other.TessellationMode; - MaxTessellationFactor = other.MaxTessellationFactor; - } - - bool operator==(const MaterialInfo& other) const - { - return Domain == other.Domain - && BlendMode == other.BlendMode - && ShadingModel == other.ShadingModel - && UsageFlags == other.UsageFlags - && FeaturesFlags == other.FeaturesFlags - && DecalBlendingMode == other.DecalBlendingMode - && PostFxLocation == other.PostFxLocation - && CullMode == other.CullMode - && Math::NearEqual(MaskThreshold, other.MaskThreshold) - && Math::NearEqual(OpacityThreshold, other.OpacityThreshold) - && TessellationMode == other.TessellationMode - && MaxTessellationFactor == other.MaxTessellationFactor; - } + MaterialInfo(const MaterialInfo8& other); + bool operator==(const MaterialInfo& other) const; }; // The current material info descriptor version used by the material pipeline diff --git a/Source/Engine/Graphics/Materials/MaterialParams.cpp b/Source/Engine/Graphics/Materials/MaterialParams.cpp index b5ff66c3b..01483558d 100644 --- a/Source/Engine/Graphics/Materials/MaterialParams.cpp +++ b/Source/Engine/Graphics/Materials/MaterialParams.cpp @@ -12,6 +12,85 @@ #include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPULimits.h" +bool MaterialInfo8::operator==(const MaterialInfo8& other) const +{ + return Domain == other.Domain + && BlendMode == other.BlendMode + && ShadingModel == other.ShadingModel + && TransparentLighting == other.TransparentLighting + && DecalBlendingMode == other.DecalBlendingMode + && PostFxLocation == other.PostFxLocation + && Math::NearEqual(MaskThreshold, other.MaskThreshold) + && Math::NearEqual(OpacityThreshold, other.OpacityThreshold) + && Flags == other.Flags + && TessellationMode == other.TessellationMode + && MaxTessellationFactor == other.MaxTessellationFactor; +} + +MaterialInfo::MaterialInfo(const MaterialInfo8& other) +{ + Domain = other.Domain; + BlendMode = other.BlendMode; + ShadingModel = other.ShadingModel; + UsageFlags = MaterialUsageFlags::None; + if (other.Flags & MaterialFlags_Deprecated::UseMask) + UsageFlags |= MaterialUsageFlags::UseMask; + if (other.Flags & MaterialFlags_Deprecated::UseEmissive) + UsageFlags |= MaterialUsageFlags::UseEmissive; + if (other.Flags & MaterialFlags_Deprecated::UsePositionOffset) + UsageFlags |= MaterialUsageFlags::UsePositionOffset; + if (other.Flags & MaterialFlags_Deprecated::UseVertexColor) + UsageFlags |= MaterialUsageFlags::UseVertexColor; + if (other.Flags & MaterialFlags_Deprecated::UseNormal) + UsageFlags |= MaterialUsageFlags::UseNormal; + if (other.Flags & MaterialFlags_Deprecated::UseDisplacement) + UsageFlags |= MaterialUsageFlags::UseDisplacement; + if (other.Flags & MaterialFlags_Deprecated::UseRefraction) + UsageFlags |= MaterialUsageFlags::UseRefraction; + FeaturesFlags = MaterialFeaturesFlags::None; + if (other.Flags & MaterialFlags_Deprecated::Wireframe) + FeaturesFlags |= MaterialFeaturesFlags::Wireframe; + if (other.Flags & MaterialFlags_Deprecated::TransparentDisableDepthTest && BlendMode != MaterialBlendMode::Opaque) + FeaturesFlags |= MaterialFeaturesFlags::DisableDepthTest; + if (other.Flags & MaterialFlags_Deprecated::TransparentDisableFog && BlendMode != MaterialBlendMode::Opaque) + FeaturesFlags |= MaterialFeaturesFlags::DisableFog; + if (other.Flags & MaterialFlags_Deprecated::TransparentDisableReflections && BlendMode != MaterialBlendMode::Opaque) + FeaturesFlags |= MaterialFeaturesFlags::DisableReflections; + if (other.Flags & MaterialFlags_Deprecated::DisableDepthWrite) + FeaturesFlags |= MaterialFeaturesFlags::DisableDepthWrite; + if (other.Flags & MaterialFlags_Deprecated::TransparentDisableDistortion && BlendMode != MaterialBlendMode::Opaque) + FeaturesFlags |= MaterialFeaturesFlags::DisableDistortion; + if (other.Flags & MaterialFlags_Deprecated::InputWorldSpaceNormal) + FeaturesFlags |= MaterialFeaturesFlags::InputWorldSpaceNormal; + if (other.Flags & MaterialFlags_Deprecated::UseDitheredLODTransition) + FeaturesFlags |= MaterialFeaturesFlags::DitheredLODTransition; + if (other.BlendMode != MaterialBlendMode::Opaque && other.TransparentLighting == MaterialTransparentLighting_Deprecated::None) + ShadingModel = MaterialShadingModel::Unlit; + DecalBlendingMode = other.DecalBlendingMode; + PostFxLocation = other.PostFxLocation; + CullMode = other.Flags & MaterialFlags_Deprecated::TwoSided ? ::CullMode::TwoSided : ::CullMode::Normal; + MaskThreshold = other.MaskThreshold; + OpacityThreshold = other.OpacityThreshold; + TessellationMode = other.TessellationMode; + MaxTessellationFactor = other.MaxTessellationFactor; +} + +bool MaterialInfo::operator==(const MaterialInfo& other) const +{ + return Domain == other.Domain + && BlendMode == other.BlendMode + && ShadingModel == other.ShadingModel + && UsageFlags == other.UsageFlags + && FeaturesFlags == other.FeaturesFlags + && DecalBlendingMode == other.DecalBlendingMode + && PostFxLocation == other.PostFxLocation + && CullMode == other.CullMode + && Math::NearEqual(MaskThreshold, other.MaskThreshold) + && Math::NearEqual(OpacityThreshold, other.OpacityThreshold) + && TessellationMode == other.TessellationMode + && MaxTessellationFactor == other.MaxTessellationFactor; +} + const Char* ToString(MaterialParameterType value) { const Char* result; diff --git a/Source/Engine/Graphics/Materials/MaterialParams.h b/Source/Engine/Graphics/Materials/MaterialParams.h index d4fef1966..63ee16677 100644 --- a/Source/Engine/Graphics/Materials/MaterialParams.h +++ b/Source/Engine/Graphics/Materials/MaterialParams.h @@ -214,7 +214,6 @@ public: /// /// Gets the parameter ID (not the parameter instance Id but the original parameter ID). /// - /// The ID. API_PROPERTY() FORCE_INLINE Guid GetParameterID() const { return _paramId; @@ -223,7 +222,6 @@ public: /// /// Gets the parameter type. /// - /// The type. API_PROPERTY() FORCE_INLINE MaterialParameterType GetParameterType() const { return _type; @@ -232,7 +230,6 @@ public: /// /// Gets the parameter name. /// - /// The name. API_PROPERTY() FORCE_INLINE const String& GetName() const { return _name; @@ -241,7 +238,6 @@ public: /// /// Returns true is parameter is public visible. /// - /// True if parameter has public access, otherwise false. API_PROPERTY() FORCE_INLINE bool IsPublic() const { return _isPublic; @@ -250,7 +246,6 @@ public: /// /// Returns true is parameter is overriding the value. /// - /// True if parameter is overriding the value, otherwise false. API_PROPERTY() FORCE_INLINE bool IsOverride() const { return _override; @@ -259,7 +254,6 @@ public: /// /// Sets the value override mode. /// - /// The value. API_PROPERTY() void SetIsOverride(bool value) { _override = value; @@ -268,7 +262,6 @@ public: /// /// Gets the parameter resource graphics pipeline binding register index. /// - /// The binding register. FORCE_INLINE byte GetRegister() const { return _registerIndex; @@ -277,7 +270,6 @@ public: /// /// Gets the parameter binding offset since the start of the constant buffer. /// - /// The binding data offset (in bytes). FORCE_INLINE uint16 GetBindOffset() const { return _offset; diff --git a/Source/Engine/Streaming/Handlers/ModelsStreamingHandler.cpp b/Source/Engine/Streaming/Handlers/ModelsStreamingHandler.cpp index 32626e071..8c99f5155 100644 --- a/Source/Engine/Streaming/Handlers/ModelsStreamingHandler.cpp +++ b/Source/Engine/Streaming/Handlers/ModelsStreamingHandler.cpp @@ -21,7 +21,7 @@ int32 ModelsStreamingHandler::CalculateResidency(StreamableResource* resource, S if (quality < ZeroTolerance) return 0; - int32 lods = Math::CeilToInt(quality * lodCount); + int32 lods = Math::CeilToInt(quality * (StreamingQuality)lodCount); ASSERT(model.IsValidLODIndex(lods - 1)); return lods; diff --git a/Source/Engine/Streaming/StreamableResource.h b/Source/Engine/Streaming/StreamableResource.h index fab245791..257b02c57 100644 --- a/Source/Engine/Streaming/StreamableResource.h +++ b/Source/Engine/Streaming/StreamableResource.h @@ -108,27 +108,13 @@ public: public: - // Streaming Manager cached variables struct StreamingCache { - /// - /// The minimum usage distance since last update (eg. mesh draw distance from camera). - /// Used to calculate resource quality. - /// - //float MinDstSinceLastUpdate; - - DateTime LastUpdate; - int32 TargetResidency; - DateTime TargetResidencyChange; + //float MinDstSinceLastUpdate = MAX_float; + DateTime LastUpdate = 0; + int32 TargetResidency = 0; + DateTime TargetResidencyChange = 0; SamplesBuffer QualitySamples; - - StreamingCache() - //: MinDstSinceLastUpdate(MAX_float) - : LastUpdate(0) - , TargetResidency(0) - , TargetResidencyChange(0) - { - } }; StreamingCache Streaming; From 3d4309457b6154234ba4b4535f2ebd2a0fcd737c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 10 Jun 2021 17:01:07 +0200 Subject: [PATCH 129/198] Improve stability of Vulkan and D3D12 backends on large worlds --- .../DirectX/DX12/GPUContextDX12.cpp | 44 ++++-- .../DirectX/DX12/GPUContextDX12.h | 2 +- .../Vulkan/GPUContextVulkan.cpp | 138 ++++++++++++------ .../GraphicsDevice/Vulkan/GPUContextVulkan.h | 7 +- .../Vulkan/GPUPipelineStateVulkan.cpp | 2 +- .../Vulkan/GPUPipelineStateVulkan.h | 5 + .../GraphicsDevice/Vulkan/GPUShaderVulkan.cpp | 19 +-- .../GraphicsDevice/Vulkan/GPUShaderVulkan.h | 2 +- .../GraphicsDevice/Vulkan/RenderToolsVulkan.h | 31 ---- Source/Engine/Renderer/LightPass.cpp | 14 +- Source/Engine/Renderer/ShadowsPass.cpp | 12 +- 11 files changed, 161 insertions(+), 115 deletions(-) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index 8b085d374..0d3309219 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -153,7 +153,7 @@ void GPUContextDX12::SetResourceState(ResourceOwnerDX12* resource, D3D12_RESOURC if (state.AreAllSubresourcesSame()) { // Transition entire resource at once - const D3D12_RESOURCE_STATES before = state.GetSubresourceState(subresourceIndex); + const D3D12_RESOURCE_STATES before = state.GetSubresourceState(-1); if (ResourceStateDX12::IsTransitionNeeded(before, after)) { AddTransitionBarrier(resource, before, after, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES); @@ -163,6 +163,7 @@ void GPUContextDX12::SetResourceState(ResourceOwnerDX12* resource, D3D12_RESOURC else { // Slow path to transition each subresource + bool hadAnyTransition = false; for (int32 i = 0; i < state.GetSubresourcesCount(); i++) { const D3D12_RESOURCE_STATES before = state.GetSubresourceState(i); @@ -170,10 +171,20 @@ void GPUContextDX12::SetResourceState(ResourceOwnerDX12* resource, D3D12_RESOURC { AddTransitionBarrier(resource, before, after, i); state.SetSubresourceState(i, after); + hadAnyTransition = true; } } - ASSERT(state.CheckResourceState(after)); - state.SetResourceState(after); + if (hadAnyTransition) + { + // Try merge all subresources states into a single state + after = state.GetSubresourceState(0); + for (int32 i = 1; i < state.GetSubresourcesCount(); i++) + { + if (state.GetSubresourceState(i) != after) + return; + } + state.SetResourceState(after); + } } } else @@ -357,6 +368,21 @@ void GPUContextDX12::flushRTVs() { depthBuffer = _rtDepth->DSV(); auto states = _rtDepth->ReadOnlyDepthView ? D3D12_RESOURCE_STATE_DEPTH_READ : D3D12_RESOURCE_STATE_DEPTH_WRITE; + if (_currentState && _rtDepth->ReadOnlyDepthView) + { + // If read-only depth buffer is also binded as shader input then ensure it has proper state bit + const uint32 srMask = _currentState->GetUsedSRsMask(); + const uint32 srCount = Math::FloorLog2(srMask) + 1; + for (uint32 i = 0; i < srCount; i++) + { + const auto handle = _srHandles[i]; + if (srMask & (1 << i) && handle == _rtDepth) + { + states |= D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + break; + } + } + } SetResourceState(_rtDepth->GetResourceOwner(), states, _rtDepth->SubresourceIndex); } else @@ -483,7 +509,7 @@ void GPUContextDX12::flushPS() } } -void GPUContextDX12::onDrawCall() +void GPUContextDX12::OnDrawCall() { // Ensure state of the vertex and index buffers for (int32 i = 0; i < _vbCount; i++) @@ -510,7 +536,7 @@ void GPUContextDX12::onDrawCall() if (srMask & (1 << i) && handle != nullptr && handle->GetResourceOwner()) { const auto resourceOwner = handle->GetResourceOwner(); - bool isRtv = false; + bool isRtv = _rtDepth == handle; for (int32 j = 0; j < _rtCount; j++) { if (_rtHandles[j] && _rtHandles[j]->GetResourceOwner() == resourceOwner) @@ -970,14 +996,14 @@ void GPUContextDX12::ResolveMultisample(GPUTexture* sourceMultisampleTexture, GP void GPUContextDX12::DrawInstanced(uint32 verticesCount, uint32 instanceCount, int32 startInstance, int32 startVertex) { - onDrawCall(); + OnDrawCall(); _commandList->DrawInstanced(verticesCount, instanceCount, startVertex, startInstance); RENDER_STAT_DRAW_CALL(verticesCount * instanceCount, verticesCount * instanceCount / 3); } void GPUContextDX12::DrawIndexedInstanced(uint32 indicesCount, uint32 instanceCount, int32 startInstance, int32 startVertex, int32 startIndex) { - onDrawCall(); + OnDrawCall(); _commandList->DrawIndexedInstanced(indicesCount, instanceCount, startIndex, startVertex, startInstance); RENDER_STAT_DRAW_CALL(0, indicesCount / 3 * instanceCount); } @@ -990,7 +1016,7 @@ void GPUContextDX12::DrawInstancedIndirect(GPUBuffer* bufferForArgs, uint32 offs auto signature = _device->DrawIndirectCommandSignature->GetSignature(); SetResourceState(bufferForArgsDX12, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); - onDrawCall(); + OnDrawCall(); _commandList->ExecuteIndirect(signature, 1, bufferForArgsDX12->GetResource(), (UINT64)offsetForArgs, nullptr, 0); RENDER_STAT_DRAW_CALL(0, 0); } @@ -1003,7 +1029,7 @@ void GPUContextDX12::DrawIndexedInstancedIndirect(GPUBuffer* bufferForArgs, uint auto signature = _device->DrawIndexedIndirectCommandSignature->GetSignature(); SetResourceState(bufferForArgsDX12, D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT); - onDrawCall(); + OnDrawCall(); _commandList->ExecuteIndirect(signature, 1, bufferForArgsDX12->GetResource(), (UINT64)offsetForArgs, nullptr, 0); RENDER_STAT_DRAW_CALL(0, 0); } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h index 1551edc76..b102cfa6b 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.h @@ -138,7 +138,7 @@ private: void flushCBs(); void flushRBs(); void flushPS(); - void onDrawCall(); + void OnDrawCall(); public: diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index 3a3e41b27..63b5503a8 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -33,10 +33,46 @@ static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, StartIndex) == OFFSET_OF(VkD static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, StartVertex) == OFFSET_OF(VkDrawIndexedIndirectCommand, vertexOffset), "Wrong offset for GPUDrawIndexedIndirectArgs::StartVertex"); static_assert(OFFSET_OF(GPUDrawIndexedIndirectArgs, StartInstance) == OFFSET_OF(VkDrawIndexedIndirectCommand, firstInstance), "Wrong offset for GPUDrawIndexedIndirectArgs::StartInstance"); +#if VK_ENABLE_BARRIERS_DEBUG + +const Char* ToString(VkImageLayout layout) +{ + switch (layout) + { +#define TO_STR(type) case type: return TEXT(#type) + TO_STR(VK_IMAGE_LAYOUT_UNDEFINED); + TO_STR(VK_IMAGE_LAYOUT_GENERAL); + TO_STR(VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + TO_STR(VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + TO_STR(VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL); + TO_STR(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + TO_STR(VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + TO_STR(VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + TO_STR(VK_IMAGE_LAYOUT_PREINITIALIZED); + TO_STR(VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL); + TO_STR(VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL); + TO_STR(VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL); + TO_STR(VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL); + TO_STR(VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL); + TO_STR(VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL); + TO_STR(VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + TO_STR(VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR); + TO_STR(VK_IMAGE_LAYOUT_SHADING_RATE_OPTIMAL_NV); + TO_STR(VK_IMAGE_LAYOUT_FRAGMENT_DENSITY_MAP_OPTIMAL_EXT); + TO_STR(VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL_KHR); + TO_STR(VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR); +#undef TO_STR + default: + return TEXT("?"); + } +} + +#endif + void PipelineBarrierVulkan::AddImageBarrier(VkImage image, const VkImageSubresourceRange& range, VkImageLayout srcLayout, VkImageLayout dstLayout, GPUTextureViewVulkan* handle) { #if VK_ENABLE_BARRIERS_DEBUG - ImageBarriersDebug.Add(handle); + ImageBarriersDebug.Add(handle); #endif VkImageMemoryBarrier& imageBarrier = ImageBarriers.AddOne(); RenderToolsVulkan::ZeroStruct(imageBarrier, VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER); @@ -44,21 +80,21 @@ void PipelineBarrierVulkan::AddImageBarrier(VkImage image, const VkImageSubresou imageBarrier.subresourceRange = range; imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - - RenderToolsVulkan::SetImageBarrierInfo(srcLayout, dstLayout, imageBarrier, SourceStage, DestStage); - + imageBarrier.oldLayout = srcLayout; + imageBarrier.newLayout = dstLayout; + SourceStage |= RenderToolsVulkan::GetImageBarrierFlags(srcLayout, imageBarrier.srcAccessMask); + DestStage |= RenderToolsVulkan::GetImageBarrierFlags(dstLayout, imageBarrier.dstAccessMask); #if VK_ENABLE_BARRIERS_DEBUG - LOG(Warning, "Image Barrier: 0x{0:x}, {1} -> {2} for baseMipLevel: {3}, baseArrayLayer: {4}, levelCount: {5}, layerCount: {6} ({7})", - (int)image, - srcLayout, - dstLayout, - range.baseMipLevel, - range.baseArrayLayer, - range.levelCount, - range.layerCount, - - handle && handle->Owner->AsGPUResource() ? handle->Owner->AsGPUResource()->ToString() : String::Empty - ); + LOG(Warning, "Image Barrier: 0x{0:x}, {1} -> {2} for baseMipLevel: {3}, baseArrayLayer: {4}, levelCount: {5}, layerCount: {6} ({7})", + (uintptr)image, + ToString(srcLayout), + ToString(dstLayout), + range.baseMipLevel, + range.baseArrayLayer, + range.levelCount, + range.layerCount, + handle && handle->Owner->AsGPUResource() ? handle->Owner->AsGPUResource()->ToString() : String::Empty + ); #endif } @@ -73,16 +109,14 @@ void PipelineBarrierVulkan::AddBufferBarrier(VkBuffer buffer, VkDeviceSize offse bufferBarrier.dstAccessMask = dstAccess; bufferBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; bufferBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - - RenderToolsVulkan::SetBufferBarrierInfo(srcAccess, dstAccess, SourceStage, DestStage); + SourceStage |= RenderToolsVulkan::GetBufferBarrierFlags(srcAccess); + DestStage |= RenderToolsVulkan::GetBufferBarrierFlags(dstAccess); } void PipelineBarrierVulkan::Execute(CmdBufferVulkan* cmdBuffer) { ASSERT(cmdBuffer->IsOutsideRenderPass()); - vkCmdPipelineBarrier(cmdBuffer->GetHandle(), SourceStage, DestStage, 0, 0, nullptr, BufferBarriers.Count(), BufferBarriers.Get(), ImageBarriers.Count(), ImageBarriers.Get()); - Reset(); } @@ -127,11 +161,11 @@ void GPUContextVulkan::AddImageBarrier(VkImage image, VkImageLayout srcLayout, V _barriers.AddImageBarrier(image, subresourceRange, srcLayout, dstLayout, handle); #if !VK_ENABLE_BARRIERS_BATCHING - // Auto-flush without batching - const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); - if (cmdBuffer->IsInsideRenderPass()) - EndRenderPass(); - _barriers.Execute(cmdBuffer); + // Auto-flush without batching + const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); + if (cmdBuffer->IsInsideRenderPass()) + EndRenderPass(); + _barriers.Execute(cmdBuffer); #endif } @@ -144,15 +178,19 @@ void GPUContextVulkan::AddImageBarrier(GPUTextureViewVulkan* handle, VkImageLayo const int32 mipLevels = state.GetSubresourcesCount() / handle->Owner->ArraySlices; if (state.AreAllSubresourcesSame()) { - // Transition entire resource at once - const VkImageLayout srcLayout = state.GetSubresourceState(0); - VkImageSubresourceRange range; - range.aspectMask = handle->Info.subresourceRange.aspectMask; - range.baseMipLevel = 0; - range.levelCount = mipLevels; - range.baseArrayLayer = 0; - range.layerCount = handle->Owner->ArraySlices; - AddImageBarrier(handle->Image, srcLayout, dstLayout, range, handle); + const VkImageLayout srcLayout = state.GetSubresourceState(-1); + if (srcLayout != dstLayout) + { + // Transition entire resource at once + VkImageSubresourceRange range; + range.aspectMask = handle->Info.subresourceRange.aspectMask; + range.baseMipLevel = 0; + range.levelCount = mipLevels; + range.baseArrayLayer = 0; + range.layerCount = handle->Owner->ArraySlices; + AddImageBarrier(handle->Image, srcLayout, dstLayout, range, handle); + state.SetResourceState(dstLayout); + } } else { @@ -172,8 +210,8 @@ void GPUContextVulkan::AddImageBarrier(GPUTextureViewVulkan* handle, VkImageLayo state.SetSubresourceState(i, dstLayout); } } - ASSERT(state.CheckResourceState(dstLayout)); } + ASSERT(state.CheckResourceState(dstLayout)); state.SetResourceState(dstLayout); } else @@ -262,11 +300,11 @@ void GPUContextVulkan::AddBufferBarrier(GPUBufferVulkan* buffer, VkAccessFlags d buffer->Access = dstAccess; #if !VK_ENABLE_BARRIERS_BATCHING - // Auto-flush without batching - const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); - if (cmdBuffer->IsInsideRenderPass()) - EndRenderPass(); - _barriers.Execute(cmdBuffer); + // Auto-flush without batching + const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); + if (cmdBuffer->IsInsideRenderPass()) + EndRenderPass(); + _barriers.Execute(cmdBuffer); #endif } @@ -628,6 +666,18 @@ void GPUContextVulkan::OnDrawCall() UpdateDescriptorSets(pipelineState); } + // Bind any missing vertex buffers to null if required by the current state + const auto vertexInputState = pipelineState->GetVertexInputState(); + const int32 missingVBs = vertexInputState->vertexBindingDescriptionCount - _vbCount; + if (missingVBs > 0) + { + VkBuffer buffers[GPU_MAX_VB_BINDED]; + VkDeviceSize offsets[GPU_MAX_VB_BINDED] = {}; + for (int32 i = 0; i < missingVBs; i++) + buffers[i] = _device->HelperResources.GetDummyVertexBuffer()->GetHandle(); + vkCmdBindVertexBuffers(cmdBuffer->GetHandle(), _vbCount, missingVBs, buffers, offsets); + } + // Start render pass if not during one if (cmdBuffer->IsOutsideRenderPass()) BeginRenderPass(); @@ -652,7 +702,7 @@ void GPUContextVulkan::OnDrawCall() _rtDirtyFlag = false; #if VK_ENABLE_BARRIERS_DEBUG - LOG(Warning, "Draw"); + LOG(Warning, "Draw"); #endif } @@ -668,6 +718,7 @@ void GPUContextVulkan::FrameBegin() _srDirtyFlag = 0; _uaDirtyFlag = 0; _rtCount = 0; + _vbCount = 0; _renderPass = nullptr; _currentState = nullptr; _rtDepth = nullptr; @@ -922,6 +973,7 @@ void GPUContextVulkan::BindUA(int32 slot, GPUResourceView* view) void GPUContextVulkan::BindVB(const Span& vertexBuffers, const uint32* vertexBuffersOffsets) { + _vbCount = vertexBuffers.Length(); if (vertexBuffers.Length() == 0) return; const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); @@ -955,7 +1007,7 @@ void GPUContextVulkan::UpdateCB(GPUConstantBuffer* cb, const void* data) const auto cmdBuffer = _cmdBufferManager->GetCmdBuffer(); // Allocate bytes for the buffer - const auto allocation = _device->UniformBufferUploader->Allocate(size, 0, cmdBuffer); + const auto allocation = _device->UniformBufferUploader->Allocate(size, 0, this); // Copy data Platform::MemoryCopy(allocation.CPUAddress, data, allocation.Size); @@ -1002,7 +1054,7 @@ void GPUContextVulkan::Dispatch(GPUShaderProgramCS* shader, uint32 threadGroupCo RENDER_STAT_DISPATCH_CALL(); #if VK_ENABLE_BARRIERS_DEBUG - LOG(Warning, "Dispatch"); + LOG(Warning, "Dispatch"); #endif } @@ -1036,7 +1088,7 @@ void GPUContextVulkan::DispatchIndirect(GPUShaderProgramCS* shader, GPUBuffer* b RENDER_STAT_DISPATCH_CALL(); #if VK_ENABLE_BARRIERS_DEBUG - LOG(Warning, "DispatchIndirect"); + LOG(Warning, "DispatchIndirect"); #endif } diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h index a3d26f017..da2054ab3 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.h @@ -35,8 +35,6 @@ class DescriptorSetLayoutVulkan; /// #define VK_BARRIER_BUFFER_SIZE 16 -#if VK_ENABLE_BARRIERS_BATCHING - /// /// The Vulkan pipeline resources layout barrier batching structure. /// @@ -81,8 +79,6 @@ public: void Execute(CmdBufferVulkan* cmdBuffer); }; -#endif - /// /// GPU Context for Vulkan backend. /// @@ -93,9 +89,7 @@ private: GPUDeviceVulkan* _device; QueueVulkan* _queue; CmdBufferManagerVulkan* _cmdBufferManager; -#if VK_ENABLE_BARRIERS_BATCHING PipelineBarrierVulkan _barriers; -#endif int32 _psDirtyFlag : 1; int32 _rtDirtyFlag : 1; @@ -104,6 +98,7 @@ private: int32 _uaDirtyFlag : 1; int32 _rtCount; + int32 _vbCount; RenderPassVulkan* _renderPass; GPUPipelineStateVulkan* _currentState; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp index cdb76e1cf..991bd5766 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.cpp @@ -270,7 +270,7 @@ bool GPUPipelineStateVulkan::Init(const Description& desc) _descDynamic.pDynamicStates = _dynamicStates; _dynamicStates[_descDynamic.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT; _dynamicStates[_descDynamic.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR; - _dynamicStates[_descDynamic.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE; + //_dynamicStates[_descDynamic.dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE; static_assert(ARRAY_COUNT(_dynamicStates) <= 3, "Invalid dynamic states array."); _desc.pDynamicState = &_descDynamic; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h index 29b71a3bc..142dd9f91 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUPipelineStateVulkan.h @@ -152,6 +152,11 @@ public: /// const SpirvShaderDescriptorInfo* DescriptorInfoPerStage[DescriptorSet::GraphicsStagesCount]; + const VkPipelineVertexInputStateCreateInfo* GetVertexInputState() const + { + return _desc.pVertexInputState; + } + DescriptorSetWriteContainerVulkan DSWriteContainer; DescriptorSetWriterVulkan DSWriter[DescriptorSet::GraphicsStagesCount]; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.cpp index 5b38b926a..863ebdc46 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.cpp @@ -3,6 +3,7 @@ #if GRAPHICS_API_VULKAN #include "GPUShaderVulkan.h" +#include "GPUContextVulkan.h" #include "GPUShaderProgramVulkan.h" #include "RenderToolsVulkan.h" #include "CmdBufferVulkan.h" @@ -12,7 +13,7 @@ #include "Engine/Graphics/PixelFormatExtensions.h" #if PLATFORM_DESKTOP -#define VULKAN_UNIFORM_RING_BUFFER_SIZE 16 * 1024 * 1024 +#define VULKAN_UNIFORM_RING_BUFFER_SIZE 24 * 1024 * 1024 #else #define VULKAN_UNIFORM_RING_BUFFER_SIZE 8 * 1024 * 1024 #endif @@ -45,7 +46,7 @@ UniformBufferUploaderVulkan::UniformBufferUploaderVulkan(GPUDeviceVulkan* device LOG_VULKAN_RESULT(result); } -UniformBufferUploaderVulkan::Allocation UniformBufferUploaderVulkan::Allocate(uint64 size, uint32 alignment, CmdBufferVulkan* cmdBuffer) +UniformBufferUploaderVulkan::Allocation UniformBufferUploaderVulkan::Allocate(uint64 size, uint32 alignment, GPUContextVulkan* context) { alignment = Math::Max(_minAlignment, alignment); uint64 offset = Math::AlignUp(_offset, alignment); @@ -53,16 +54,12 @@ UniformBufferUploaderVulkan::Allocation UniformBufferUploaderVulkan::Allocate(ui // Check if wrap around ring buffer if (offset + size >= _size) { - if (_fenceCmdBuffer) + auto cmdBuffer = context->GetCmdBufferManager()->GetActiveCmdBuffer(); + if (_fenceCmdBuffer && _fenceCounter == cmdBuffer->GetFenceSignaledCounter()) { - if (_fenceCmdBuffer == cmdBuffer && _fenceCounter == cmdBuffer->GetFenceSignaledCounter()) - { - LOG(Error, "Wrapped around the ring buffer. Requested more bytes than possible in the same cmd buffer!"); - } - else if (_fenceCounter == _fenceCmdBuffer->GetFenceSignaledCounter()) - { - LOG(Error, "Wrapped around the ring buffer! Need to wait on the GPU!!!"); - } + LOG(Error, "Wrapped around the ring buffer! Need to wait on the GPU!"); + context->Flush(); + cmdBuffer = context->GetCmdBufferManager()->GetActiveCmdBuffer(); } offset = 0; diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.h index 842dfc145..5158baefa 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUShaderVulkan.h @@ -62,7 +62,7 @@ public: public: - Allocation Allocate(uint64 size, uint32 alignment, CmdBufferVulkan* cmdBuffer); + Allocation Allocate(uint64 size, uint32 alignment, GPUContextVulkan* context); public: diff --git a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h index 1380d221d..c7c11fb0c 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/RenderToolsVulkan.h @@ -164,37 +164,6 @@ public: return stageFlags; } - static inline void SetBufferBarrierInfo(VkAccessFlags source, VkAccessFlags dest, VkPipelineStageFlags& sourceStage, VkPipelineStageFlags& destStage) - { - sourceStage |= GetBufferBarrierFlags(source); - destStage |= GetBufferBarrierFlags(dest); - } - - static inline void SetImageBarrierInfo(VkImageLayout source, VkImageLayout dest, VkImageMemoryBarrier& barrier, VkPipelineStageFlags& sourceStage, VkPipelineStageFlags& destStage) - { - barrier.oldLayout = source; - barrier.newLayout = dest; - - sourceStage |= GetImageBarrierFlags(source, barrier.srcAccessMask); - destStage |= GetImageBarrierFlags(dest, barrier.dstAccessMask); - } - - static void ImagePipelineBarrier(VkCommandBuffer cmdBuffer, VkImage img, VkImageLayout src, VkImageLayout dest, const VkImageSubresourceRange& subresourceRange) - { - VkImageMemoryBarrier imageBarrier; - ZeroStruct(imageBarrier, VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER); - imageBarrier.image = img; - imageBarrier.subresourceRange = subresourceRange; - imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; - - VkPipelineStageFlags srcStages = (VkPipelineStageFlags)0; - VkPipelineStageFlags destStages = (VkPipelineStageFlags)0; - SetImageBarrierInfo(src, dest, imageBarrier, srcStages, destStages); - - vkCmdPipelineBarrier(cmdBuffer, srcStages, destStages, 0, 0, nullptr, 0, nullptr, 1, &imageBarrier); - } - template static FORCE_INLINE void ZeroStruct(T& data, VkStructureType type) { diff --git a/Source/Engine/Renderer/LightPass.cpp b/Source/Engine/Renderer/LightPass.cpp index 648f934d3..b44f3f452 100644 --- a/Source/Engine/Renderer/LightPass.cpp +++ b/Source/Engine/Renderer/LightPass.cpp @@ -206,8 +206,10 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB // Bind output GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer; - GPUTextureView* depthBufferHandle = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : nullptr; - context->SetRenderTarget(depthBufferHandle, lightBuffer); + const bool depthBufferReadOnly = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView; + GPUTextureView* depthBufferRTV = depthBufferReadOnly ? depthBuffer->ViewReadOnlyDepth() : nullptr; + GPUTextureView* depthBufferSRV = depthBufferReadOnly ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View(); + context->SetRenderTarget(depthBufferRTV, lightBuffer); // Set per frame data GBufferPass::SetInputs(renderContext.View, perFrame.GBuffer); @@ -225,7 +227,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB context->BindSR(0, renderContext.Buffers->GBuffer0); context->BindSR(1, renderContext.Buffers->GBuffer1); context->BindSR(2, renderContext.Buffers->GBuffer2); - context->BindSR(3, depthBuffer); + context->BindSR(3, depthBufferSRV); context->BindSR(4, renderContext.Buffers->GBuffer3); // Check if debug lights @@ -274,7 +276,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB ShadowsPass::Instance()->RenderShadow(renderContext, light, shadowMaskView); // Bind output - context->SetRenderTarget(depthBufferHandle, lightBuffer); + context->SetRenderTarget(depthBufferRTV, lightBuffer); // Set shadow mask context->BindSR(5, shadowMaskView); @@ -329,7 +331,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB ShadowsPass::Instance()->RenderShadow(renderContext, light, shadowMaskView); // Bind output - context->SetRenderTarget(depthBufferHandle, lightBuffer); + context->SetRenderTarget(depthBufferRTV, lightBuffer); // Set shadow mask context->BindSR(5, shadowMaskView); @@ -370,7 +372,7 @@ void LightPass::RenderLight(RenderContext& renderContext, GPUTextureView* lightB ShadowsPass::Instance()->RenderShadow(renderContext, light, lightIndex, shadowMaskView); // Bind output - context->SetRenderTarget(depthBufferHandle, lightBuffer); + context->SetRenderTarget(depthBufferRTV, lightBuffer); // Set shadow mask context->BindSR(5, shadowMaskView); diff --git a/Source/Engine/Renderer/ShadowsPass.cpp b/Source/Engine/Renderer/ShadowsPass.cpp index 707aa461d..319e9f09a 100644 --- a/Source/Engine/Renderer/ShadowsPass.cpp +++ b/Source/Engine/Renderer/ShadowsPass.cpp @@ -315,12 +315,12 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererPointLightD context->ResetRenderTarget(); const Viewport viewport = renderContext.Task->GetViewport(); GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer; - GPUTextureView* depthBufferHandle = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View(); + GPUTextureView* depthBufferSRV = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View(); context->SetViewportAndScissors(viewport); context->BindSR(0, renderContext.Buffers->GBuffer0); context->BindSR(1, renderContext.Buffers->GBuffer1); context->BindSR(2, renderContext.Buffers->GBuffer2); - context->BindSR(3, depthBufferHandle); + context->BindSR(3, depthBufferSRV); context->BindSR(4, renderContext.Buffers->GBuffer3); // Setup shader data @@ -417,12 +417,12 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererSpotLightDa context->ResetRenderTarget(); const Viewport viewport = renderContext.Task->GetViewport(); GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer; - GPUTextureView* depthBufferHandle = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View(); + GPUTextureView* depthBufferSRV = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View(); context->SetViewportAndScissors(viewport); context->BindSR(0, renderContext.Buffers->GBuffer0); context->BindSR(1, renderContext.Buffers->GBuffer1); context->BindSR(2, renderContext.Buffers->GBuffer2); - context->BindSR(3, depthBufferHandle); + context->BindSR(3, depthBufferSRV); context->BindSR(4, renderContext.Buffers->GBuffer3); // Setup shader data @@ -693,12 +693,12 @@ void ShadowsPass::RenderShadow(RenderContext& renderContext, RendererDirectional context->ResetRenderTarget(); const Viewport viewport = renderContext.Task->GetViewport(); GPUTexture* depthBuffer = renderContext.Buffers->DepthBuffer; - GPUTextureView* depthBufferHandle = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View(); + GPUTextureView* depthBufferSRV = depthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? depthBuffer->ViewReadOnlyDepth() : depthBuffer->View(); context->SetViewportAndScissors(viewport); context->BindSR(0, renderContext.Buffers->GBuffer0); context->BindSR(1, renderContext.Buffers->GBuffer1); context->BindSR(2, renderContext.Buffers->GBuffer2); - context->BindSR(3, depthBufferHandle); + context->BindSR(3, depthBufferSRV); context->BindSR(4, renderContext.Buffers->GBuffer3); // Setup shader data From dfa6f5c75f700aa38da1cdda95228cc980ef9fd2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 10 Jun 2021 17:01:24 +0200 Subject: [PATCH 130/198] Fix profiler shutdown issue on end --- Source/Engine/Profiler/ProfilingTools.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Profiler/ProfilingTools.cpp b/Source/Engine/Profiler/ProfilingTools.cpp index 4afab6c04..daedf2a61 100644 --- a/Source/Engine/Profiler/ProfilingTools.cpp +++ b/Source/Engine/Profiler/ProfilingTools.cpp @@ -23,7 +23,7 @@ public: } void Update() override; - void BeforeExit() override; + void Dispose() override; }; ProfilingToolsService ProfilingToolsServiceInstance; @@ -166,7 +166,7 @@ void ProfilingToolsService::Update() #endif } -void ProfilingToolsService::BeforeExit() +void ProfilingToolsService::Dispose() { ProfilingTools::EventsCPU.Clear(); ProfilingTools::EventsCPU.SetCapacity(0); From 3a51320ea671da103e613a861c01117291b87197 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 11 Jun 2021 09:53:43 +0200 Subject: [PATCH 131/198] Increase profiler samples length to 10s at 60hz --- Source/Editor/Windows/Profiler/ProfilerMode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Windows/Profiler/ProfilerMode.cs b/Source/Editor/Windows/Profiler/ProfilerMode.cs index 578cfaebf..fe13b65ef 100644 --- a/Source/Editor/Windows/Profiler/ProfilerMode.cs +++ b/Source/Editor/Windows/Profiler/ProfilerMode.cs @@ -65,7 +65,7 @@ namespace FlaxEditor.Windows.Profiler /// /// The maximum amount of samples to collect. /// - public const int MaxSamples = 60 * 5; + public const int MaxSamples = 60 * 10; /// /// The minimum event time in ms. From 9c7378cf5b4cc3d2eed355bf490ff10c20175479 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 11 Jun 2021 12:03:01 +0200 Subject: [PATCH 132/198] Optimize Profiler window update to refresh only visible tab UI --- Source/Editor/Windows/Profiler/CPU.cs | 8 -------- Source/Editor/Windows/Profiler/GPU.cs | 7 ------- Source/Editor/Windows/Profiler/ProfilerWindow.cs | 8 ++++++-- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/Source/Editor/Windows/Profiler/CPU.cs b/Source/Editor/Windows/Profiler/CPU.cs index a93613d13..84ab4ec16 100644 --- a/Source/Editor/Windows/Profiler/CPU.cs +++ b/Source/Editor/Windows/Profiler/CPU.cs @@ -174,14 +174,6 @@ namespace FlaxEditor.Windows.Profiler // Gather CPU events var events = sharedData.GetEventsCPU(); _events.Add(events); - - // Update timeline if using the last frame - if (_mainChart.SelectedSampleIndex == -1) - { - var viewRange = GetEventsViewRange(); - UpdateTimeline(ref viewRange); - UpdateTable(ref viewRange); - } } /// diff --git a/Source/Editor/Windows/Profiler/GPU.cs b/Source/Editor/Windows/Profiler/GPU.cs index cd1c4cfed..a1af36a70 100644 --- a/Source/Editor/Windows/Profiler/GPU.cs +++ b/Source/Editor/Windows/Profiler/GPU.cs @@ -131,13 +131,6 @@ namespace FlaxEditor.Windows.Profiler // Peek draw time _drawTimeCPU.AddSample(sharedData.Stats.DrawCPUTimeMs); _drawTimeGPU.AddSample(sharedData.Stats.DrawGPUTimeMs); - - // Update timeline if using the last frame - if (_drawTimeCPU.SelectedSampleIndex == -1) - { - UpdateTimeline(); - UpdateTable(); - } } /// diff --git a/Source/Editor/Windows/Profiler/ProfilerWindow.cs b/Source/Editor/Windows/Profiler/ProfilerWindow.cs index 27a718077..d70ba7681 100644 --- a/Source/Editor/Windows/Profiler/ProfilerWindow.cs +++ b/Source/Editor/Windows/Profiler/ProfilerWindow.cs @@ -41,7 +41,7 @@ namespace FlaxEditor.Windows.Profiler } /// - /// Gets or sets the index of the selected frame to view (note: some view modes may not use it). + /// Gets or sets the index of the selected frame to view (note: some view modes may not use it). -1 for the last frame. /// public int ViewFrameIndex { @@ -188,7 +188,7 @@ namespace FlaxEditor.Windows.Profiler for (int i = 0; i < _tabs.ChildrenCount; i++) { if (_tabs.Children[i] is ProfilerMode mode) - mode.UpdateView(ViewFrameIndex, _showOnlyLastUpdateEvents); + mode.UpdateView(_frameIndex, _showOnlyLastUpdateEvents); } UpdateButtons(); @@ -208,6 +208,10 @@ namespace FlaxEditor.Windows.Profiler if (_tabs.Children[i] is ProfilerMode mode) mode.Update(ref sharedData); } + { + if (_tabs.SelectedTab is ProfilerMode mode) + mode.UpdateView(_frameIndex, _showOnlyLastUpdateEvents); + } sharedData.End(); _framesCount = Mathf.Min(_framesCount + 1, ProfilerMode.MaxSamples); From 81fd0263131a19576621e48db13ce602663ab6bc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 11 Jun 2021 16:44:26 +0200 Subject: [PATCH 133/198] Add pooling for timeline event controls in profiler --- Source/Editor/Windows/Profiler/CPU.cs | 93 +++++++++++++++++----- Source/Editor/Windows/Profiler/GPU.cs | 59 +++++++++++--- Source/Editor/Windows/Profiler/Timeline.cs | 25 +++--- 3 files changed, 134 insertions(+), 43 deletions(-) diff --git a/Source/Editor/Windows/Profiler/CPU.cs b/Source/Editor/Windows/Profiler/CPU.cs index 84ab4ec16..e0cfef46f 100644 --- a/Source/Editor/Windows/Profiler/CPU.cs +++ b/Source/Editor/Windows/Profiler/CPU.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; +using System.Collections.Generic; using FlaxEditor.GUI; using FlaxEngine; using FlaxEngine.GUI; @@ -50,7 +51,9 @@ namespace FlaxEditor.Windows.Profiler private readonly SingleChart _mainChart; private readonly Timeline _timeline; private readonly Table _table; - private readonly SamplesBuffer _events = new SamplesBuffer(); + private SamplesBuffer _events; + private List _timelineLabelsCache; + private List _timelineEventsCache; private bool _showOnlyLastUpdateEvents; public CPU() @@ -163,7 +166,7 @@ namespace FlaxEditor.Windows.Profiler public override void Clear() { _mainChart.Clear(); - _events.Clear(); + _events?.Clear(); } /// @@ -173,6 +176,8 @@ namespace FlaxEditor.Windows.Profiler // Gather CPU events var events = sharedData.GetEventsCPU(); + if (_events == null) + _events = new SamplesBuffer(); _events.Add(events); } @@ -182,11 +187,28 @@ namespace FlaxEditor.Windows.Profiler _showOnlyLastUpdateEvents = showOnlyLastUpdateEvents; _mainChart.SelectedSampleIndex = selectedFrame; + if (_events == null) + return; + if (_timelineLabelsCache == null) + _timelineLabelsCache = new List(); + if (_timelineEventsCache == null) + _timelineEventsCache = new List(); + var viewRange = GetEventsViewRange(); UpdateTimeline(ref viewRange); UpdateTable(ref viewRange); } + /// + public override void OnDestroy() + { + Clear(); + _timelineLabelsCache?.Clear(); + _timelineEventsCache?.Clear(); + + base.OnDestroy(); + } + private struct ViewRange { public double Start; @@ -215,7 +237,7 @@ namespace FlaxEditor.Windows.Profiler if (_showOnlyLastUpdateEvents) { // Find root event named 'Update' and use it as a view range - if (_events.Count != 0) + if (_events != null && _events.Count != 0) { var data = _events.Get(_mainChart.SelectedSampleIndex); if (data != null) @@ -251,14 +273,22 @@ namespace FlaxEditor.Windows.Profiler double scale = 100.0; float x = (float)((e.Start - startTime) * scale); float width = (float)(length * scale); - string name = e.Name.Replace("::", "."); - var control = new Timeline.Event(x + xOffset, e.Depth + depthOffset, width) + Timeline.Event control; + if (_timelineEventsCache.Count != 0) { - Name = name, - TooltipText = string.Format("{0}, {1} ms", name, ((int)(length * 1000.0) / 1000.0f)), - Parent = parent, - }; + var last = _timelineEventsCache.Count - 1; + control = _timelineEventsCache[last]; + _timelineEventsCache.RemoveAt(last); + } + else + { + control = new Timeline.Event(); + } + control.Bounds = new Rectangle(x + xOffset, (e.Depth + depthOffset) * Timeline.Event.DefaultHeight, width, Timeline.Event.DefaultHeight - 1); + control.Name = e.Name.Replace("::", "."); + control.TooltipText = string.Format("{0}, {1} ms", control.Name, ((int)(length * 1000.0) / 1000.0f)); + control.Parent = parent; // Spawn sub events int childrenDepth = e.Depth + 1; @@ -282,10 +312,26 @@ namespace FlaxEditor.Windows.Profiler { var container = _timeline.EventsContainer; - // Clear previous events - container.DisposeChildren(); - - container.LockChildrenRecursive(); + container.IsLayoutLocked = true; + int idx = 0; + while (container.Children.Count > idx) + { + var child = container.Children[idx]; + if (child is Timeline.Event e) + { + _timelineEventsCache.Add(e); + child.Parent = null; + } + else if (child is Timeline.TrackLabel l) + { + _timelineLabelsCache.Add(l); + child.Parent = null; + } + else + { + idx++; + } + } _timeline.Height = UpdateTimelineInner(ref viewRange); @@ -340,13 +386,21 @@ namespace FlaxEditor.Windows.Profiler // Add thread label float xOffset = 90; - var label = new Timeline.TrackLabel + Timeline.TrackLabel trackLabel; + if (_timelineLabelsCache.Count != 0) { - Bounds = new Rectangle(0, depthOffset * Timeline.Event.DefaultHeight, xOffset, (maxDepth + 2) * Timeline.Event.DefaultHeight), - Name = data[i].Name, - BackgroundColor = Style.Current.Background * 1.1f, - Parent = container, - }; + var last = _timelineLabelsCache.Count - 1; + trackLabel = _timelineLabelsCache[last]; + _timelineLabelsCache.RemoveAt(last); + } + else + { + trackLabel = new Timeline.TrackLabel(); + } + trackLabel.Bounds = new Rectangle(0, depthOffset * Timeline.Event.DefaultHeight, xOffset, (maxDepth + 2) * Timeline.Event.DefaultHeight); + trackLabel.Name = data[i].Name; + trackLabel.BackgroundColor = Style.Current.Background * 1.1f; + trackLabel.Parent = container; // Add events for (int j = 0; j < events.Length; j++) @@ -371,7 +425,6 @@ namespace FlaxEditor.Windows.Profiler private void UpdateTable(ref ViewRange viewRange) { _table.DisposeChildren(); - _table.LockChildrenRecursive(); UpdateTableInner(ref viewRange); diff --git a/Source/Editor/Windows/Profiler/GPU.cs b/Source/Editor/Windows/Profiler/GPU.cs index a1af36a70..da7d04249 100644 --- a/Source/Editor/Windows/Profiler/GPU.cs +++ b/Source/Editor/Windows/Profiler/GPU.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System.Collections.Generic; using FlaxEditor.GUI; using FlaxEngine; using FlaxEngine.GUI; @@ -16,7 +17,8 @@ namespace FlaxEditor.Windows.Profiler private readonly SingleChart _drawTimeGPU; private readonly Timeline _timeline; private readonly Table _table; - private readonly SamplesBuffer _events = new SamplesBuffer(); + private SamplesBuffer _events; + private List _timelineEventsCache; public GPU() : base("GPU") @@ -118,7 +120,7 @@ namespace FlaxEditor.Windows.Profiler { _drawTimeCPU.Clear(); _drawTimeGPU.Clear(); - _events.Clear(); + _events?.Clear(); } /// @@ -126,6 +128,8 @@ namespace FlaxEditor.Windows.Profiler { // Gather GPU events var data = sharedData.GetEventsGPU(); + if (_events == null) + _events = new SamplesBuffer(); _events.Add(data); // Peek draw time @@ -138,10 +142,25 @@ namespace FlaxEditor.Windows.Profiler { _drawTimeCPU.SelectedSampleIndex = selectedFrame; _drawTimeGPU.SelectedSampleIndex = selectedFrame; + + if (_events == null) + return; + if (_timelineEventsCache == null) + _timelineEventsCache = new List(); + UpdateTimeline(); UpdateTable(); } + /// + public override void OnDestroy() + { + Clear(); + _timelineEventsCache?.Clear(); + + base.OnDestroy(); + } + private float AddEvent(float x, int maxDepth, int index, ProfilerGPU.Event[] events, ContainerControl parent) { ref ProfilerGPU.Event e = ref events[index]; @@ -150,12 +169,21 @@ namespace FlaxEditor.Windows.Profiler float width = (float)(e.Time * scale); string name = new string(e.Name); - new Timeline.Event(x, e.Depth, width) + Timeline.Event control; + if (_timelineEventsCache.Count != 0) { - Name = name, - TooltipText = string.Format("{0}, {1} ms", name, ((int)(e.Time * 10000.0) / 10000.0f)), - Parent = parent, - }; + var last = _timelineEventsCache.Count - 1; + control = _timelineEventsCache[last]; + _timelineEventsCache.RemoveAt(last); + } + else + { + control = new Timeline.Event(); + } + control.Bounds = new Rectangle(x, e.Depth * Timeline.Event.DefaultHeight, width, Timeline.Event.DefaultHeight - 1); + control.Name = name; + control.TooltipText = string.Format("{0}, {1} ms", name, ((int)(e.Time * 10000.0) / 10000.0f)); + control.Parent = parent; // Spawn sub events int childrenDepth = e.Depth + 1; @@ -201,8 +229,21 @@ namespace FlaxEditor.Windows.Profiler { var container = _timeline.EventsContainer; - // Clear previous events - container.DisposeChildren(); + container.IsLayoutLocked = true; + int idx = 0; + while (container.Children.Count > idx) + { + var child = container.Children[idx]; + if (child is Timeline.Event e) + { + _timelineEventsCache.Add(e); + child.Parent = null; + } + else + { + idx++; + } + } container.LockChildrenRecursive(); diff --git a/Source/Editor/Windows/Profiler/Timeline.cs b/Source/Editor/Windows/Profiler/Timeline.cs index 53f4c9987..8e7ab145d 100644 --- a/Source/Editor/Windows/Profiler/Timeline.cs +++ b/Source/Editor/Windows/Profiler/Timeline.cs @@ -40,7 +40,8 @@ namespace FlaxEditor.Windows.Profiler }; private Color _color; - private float _nameLength; + private string _name; + private float _nameLength = -1; /// /// The default height of the event. @@ -50,18 +51,14 @@ namespace FlaxEditor.Windows.Profiler /// /// Gets or sets the event name. /// - public string Name { get; set; } - - /// - /// Initializes a new instance of the class. - /// - /// The x position. - /// The timeline row index (event depth). - /// The width. - public Event(float x, int depth, float width) - : base(x, depth * DefaultHeight, width, DefaultHeight - 1) + public string Name { - _nameLength = -1; + get => _name; + set + { + _name = value; + _nameLength = -1; + } } /// @@ -88,12 +85,12 @@ namespace FlaxEditor.Windows.Profiler Render2D.DrawRectangle(bounds, color * 0.5f); if (_nameLength < 0 && style.FontMedium) - _nameLength = style.FontMedium.MeasureText(Name).X; + _nameLength = style.FontMedium.MeasureText(_name).X; if (_nameLength < bounds.Width + 4) { Render2D.PushClip(bounds); - Render2D.DrawText(style.FontMedium, Name, bounds, Color.White, TextAlignment.Center, TextAlignment.Center); + Render2D.DrawText(style.FontMedium, _name, bounds, Color.White, TextAlignment.Center, TextAlignment.Center); Render2D.PopClip(); } } From 8f78ccf414286da6c0992a9d63842fba6612fa3c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 11 Jun 2021 16:49:12 +0200 Subject: [PATCH 134/198] Add pooling for timeline table row controls in profiler --- Source/Editor/Windows/Profiler/CPU.cs | 73 ++++++++++++++++++--------- Source/Editor/Windows/Profiler/GPU.cs | 73 ++++++++++++++++++--------- 2 files changed, 98 insertions(+), 48 deletions(-) diff --git a/Source/Editor/Windows/Profiler/CPU.cs b/Source/Editor/Windows/Profiler/CPU.cs index e0cfef46f..5d752c0fb 100644 --- a/Source/Editor/Windows/Profiler/CPU.cs +++ b/Source/Editor/Windows/Profiler/CPU.cs @@ -54,6 +54,7 @@ namespace FlaxEditor.Windows.Profiler private SamplesBuffer _events; private List _timelineLabelsCache; private List _timelineEventsCache; + private List _tableRowsCache; private bool _showOnlyLastUpdateEvents; public CPU() @@ -193,6 +194,8 @@ namespace FlaxEditor.Windows.Profiler _timelineLabelsCache = new List(); if (_timelineEventsCache == null) _timelineEventsCache = new List(); + if (_tableRowsCache == null) + _tableRowsCache = new List(); var viewRange = GetEventsViewRange(); UpdateTimeline(ref viewRange); @@ -205,6 +208,7 @@ namespace FlaxEditor.Windows.Profiler Clear(); _timelineLabelsCache?.Clear(); _timelineEventsCache?.Clear(); + _tableRowsCache?.Clear(); base.OnDestroy(); } @@ -424,8 +428,21 @@ namespace FlaxEditor.Windows.Profiler private void UpdateTable(ref ViewRange viewRange) { - _table.DisposeChildren(); - _table.LockChildrenRecursive(); + _table.IsLayoutLocked = true; + int idx = 0; + while (_table.Children.Count > idx) + { + var child = _table.Children[idx]; + if (child is Row row) + { + _tableRowsCache.Add(row); + child.Parent = null; + } + else + { + idx++; + } + } UpdateTableInner(ref viewRange); @@ -480,36 +497,44 @@ namespace FlaxEditor.Windows.Profiler string name = e.Name.Replace("::", "."); - var row = new Row + Row row; + if (_tableRowsCache.Count != 0) { - Values = new object[] + var last = _tableRowsCache.Count - 1; + row = _tableRowsCache[last]; + _tableRowsCache.RemoveAt(last); + } + else + { + row = new Row { - // Event - name, + Values = new object[6], + }; + } + { + // Event + row.Values[0] = name; - // Total (%) - (int)(time / totalTimeMs * 1000.0f) / 10.0f, + // Total (%) + row.Values[1] = (int)(time / totalTimeMs * 1000.0f) / 10.0f; - // Self (%) - (int)((time - subEventsTimeTotal) / time * 1000.0f) / 10.0f, + // Self (%) + row.Values[2] = (int)((time - subEventsTimeTotal) / time * 1000.0f) / 10.0f; - // Time ms - (float)((time * 10000.0f) / 10000.0f), + // Time ms + row.Values[3] = (float)((time * 10000.0f) / 10000.0f); - // Self ms - (float)(((time - subEventsTimeTotal) * 10000.0f) / 10000.0f), + // Self ms + row.Values[4] = (float)(((time - subEventsTimeTotal) * 10000.0f) / 10000.0f); - // Memory Alloc - subEventsMemoryTotal, - }, - Depth = e.Depth, - Width = _table.Width, - Parent = _table, - }; - - if (i % 2 == 0) - row.BackgroundColor = rowColor2; + // Memory Alloc + row.Values[5] = subEventsMemoryTotal; + } + row.Depth = e.Depth; + row.Width = _table.Width; row.Visible = e.Depth < 3; + row.BackgroundColor = i % 2 == 0 ? rowColor2 : Color.Transparent; + row.Parent = _table; } } } diff --git a/Source/Editor/Windows/Profiler/GPU.cs b/Source/Editor/Windows/Profiler/GPU.cs index da7d04249..cc28bb545 100644 --- a/Source/Editor/Windows/Profiler/GPU.cs +++ b/Source/Editor/Windows/Profiler/GPU.cs @@ -19,6 +19,7 @@ namespace FlaxEditor.Windows.Profiler private readonly Table _table; private SamplesBuffer _events; private List _timelineEventsCache; + private List _tableRowsCache; public GPU() : base("GPU") @@ -147,6 +148,8 @@ namespace FlaxEditor.Windows.Profiler return; if (_timelineEventsCache == null) _timelineEventsCache = new List(); + if (_tableRowsCache == null) + _tableRowsCache = new List(); UpdateTimeline(); UpdateTable(); @@ -157,6 +160,7 @@ namespace FlaxEditor.Windows.Profiler { Clear(); _timelineEventsCache?.Clear(); + _tableRowsCache?.Clear(); base.OnDestroy(); } @@ -286,8 +290,21 @@ namespace FlaxEditor.Windows.Profiler private void UpdateTable() { - _table.DisposeChildren(); - + _table.IsLayoutLocked = true; + int idx = 0; + while (_table.Children.Count > idx) + { + var child = _table.Children[idx]; + if (child is Row row) + { + _tableRowsCache.Add(row); + child.Parent = null; + } + else + { + idx++; + } + } _table.LockChildrenRecursive(); UpdateTableInner(); @@ -313,36 +330,44 @@ namespace FlaxEditor.Windows.Profiler var e = data[i]; string name = new string(e.Name); - var row = new Row + Row row; + if (_tableRowsCache.Count != 0) { - Values = new object[] + var last = _tableRowsCache.Count - 1; + row = _tableRowsCache[last]; + _tableRowsCache.RemoveAt(last); + } + else + { + row = new Row { - // Event - name, + Values = new object[6], + }; + } + { + // Event + row.Values[0] = name; - // Total (%) - (int)(e.Time / totalTimeMs * 1000.0f) / 10.0f, + // Total (%) + row.Values[1] = (int)(e.Time / totalTimeMs * 1000.0f) / 10.0f; - // GPU ms - (e.Time * 10000.0f) / 10000.0f, + // GPU ms + row.Values[2] = (e.Time * 10000.0f) / 10000.0f; - // Draw Calls - e.Stats.DrawCalls, + // Draw Calls + row.Values[3] = e.Stats.DrawCalls; - // Triangles - e.Stats.Triangles, + // Triangles + row.Values[4] = e.Stats.Triangles; - // Vertices - e.Stats.Vertices, - }, - Depth = e.Depth, - Width = _table.Width, - Parent = _table, - }; - - if (i % 2 == 0) - row.BackgroundColor = rowColor2; + // Vertices + row.Values[5] = e.Stats.Vertices; + } + row.Depth = e.Depth; + row.Width = _table.Width; row.Visible = e.Depth < 3; + row.BackgroundColor = i % 2 == 0 ? rowColor2 : Color.Transparent; + row.Parent = _table; } } } From 7c9e170a40f748bc2738d6ab40649fbf13c742f9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 11 Jun 2021 16:49:30 +0200 Subject: [PATCH 135/198] Tweaks --- Source/Engine/Graphics/GPUDevice.cpp | 10 ++++++++++ Source/Engine/Graphics/GPUDevice.h | 12 +++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Graphics/GPUDevice.cpp b/Source/Engine/Graphics/GPUDevice.cpp index cdf1da0d3..5ea4914ba 100644 --- a/Source/Engine/Graphics/GPUDevice.cpp +++ b/Source/Engine/Graphics/GPUDevice.cpp @@ -298,6 +298,11 @@ bool GPUDevice::LoadContent() return false; } +bool GPUDevice::CanDraw() +{ + return true; +} + void GPUDevice::preDispose() { RenderTargetPool::Flush(); @@ -449,6 +454,11 @@ void GPUDevice::Dispose() VideoOutputModes.Resize(0); } +uint64 GPUDevice::GetMemoryUsage() const +{ + return Resources.GetMemoryUsage(); +} + MaterialBase* GPUDevice::GetDefaultMaterial() const { return _res->DefaultMaterial; diff --git a/Source/Engine/Graphics/GPUDevice.h b/Source/Engine/Graphics/GPUDevice.h index e1deb16cc..8290df1aa 100644 --- a/Source/Engine/Graphics/GPUDevice.h +++ b/Source/Engine/Graphics/GPUDevice.h @@ -224,10 +224,7 @@ public: /// /// Gets the amount of memory usage by all the GPU resources (in bytes). /// - API_PROPERTY() FORCE_INLINE uint64 GetMemoryUsage() const - { - return Resources.GetMemoryUsage(); - } + API_PROPERTY() uint64 GetMemoryUsage() const; /// /// Gets the default material. @@ -287,10 +284,7 @@ public: /// Checks if GPU can render frame now (all data is ready), otherwise will skip frame rendering. /// /// True if skip rendering, otherwise false. - virtual bool CanDraw() - { - return true; - } + virtual bool CanDraw(); /// /// Call frame rendering and process data using GPU @@ -389,7 +383,7 @@ public: /// /// Utility structure to safety graphics device locking. /// -struct GPUDeviceLock : NonCopyable +struct FLAXENGINE_API GPUDeviceLock : NonCopyable { const GPUDevice* Device; From 4cd31ce2b4a65ac287a12bf2fcc9f7511aa82f91 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 6 Jun 2021 12:30:01 +0200 Subject: [PATCH 136/198] Remove some unsued lines --- Source/Editor/Editor.cs | 4 ++-- Source/Engine/Platform/Base/ThreadBase.h | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index e7b33852b..d549bb907 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -334,7 +334,7 @@ namespace FlaxEditor } // Load scene - + // scene cmd line argument var scene = ContentDatabase.Find(_startupSceneCmdLine); if (scene is SceneItem) @@ -1334,7 +1334,7 @@ namespace FlaxEditor { Instance.StateMachine.StateChanged += RequestStartPlayOnEditMode; } - + [MethodImpl(MethodImplOptions.InternalCall)] internal static extern int Internal_ReadOutputLogs(string[] outMessages, byte[] outLogTypes, long[] outLogTimes); diff --git a/Source/Engine/Platform/Base/ThreadBase.h b/Source/Engine/Platform/Base/ThreadBase.h index 8cd683d7c..6a56e33ad 100644 --- a/Source/Engine/Platform/Base/ThreadBase.h +++ b/Source/Engine/Platform/Base/ThreadBase.h @@ -45,7 +45,6 @@ public: /// /// Gets priority level of the thread. /// - /// The thread priority level. FORCE_INLINE ThreadPriority GetPriority() const { return _priority; @@ -60,7 +59,6 @@ public: /// /// Gets thread ID /// - /// Thread ID FORCE_INLINE uint64 GetID() const { return _id; @@ -69,7 +67,6 @@ public: /// /// Gets thread running state. /// - /// True if thread is running, otherwise false FORCE_INLINE bool IsRunning() const { return _isRunning; @@ -78,7 +75,6 @@ public: /// /// Gets name of the thread. /// - /// The thread name. FORCE_INLINE const String& GetName() const { return _name; From cdc85a19616a0e1b1aa71dffa486d949db7f43f4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 6 Jun 2021 12:30:27 +0200 Subject: [PATCH 137/198] Add profiler entry for Audio Source update --- Source/Engine/Audio/AudioSource.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index a6370cc8b..192fd845f 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -6,6 +6,7 @@ #include "Engine/Graphics/RenderTask.h" #include "Engine/Engine/Time.h" #include "Engine/Level/Scene/Scene.h" +#include "Engine/Profiler/ProfilerCPU.h" #include "AudioBackend.h" #include "Audio.h" @@ -340,6 +341,8 @@ bool AudioSource::IntersectsItself(const Ray& ray, float& distance, Vector3& nor void AudioSource::Update() { + PROFILE_CPU(); + // Update the velocity const Vector3 pos = GetPosition(); const float dt = Math::Max(Time::Update.UnscaledDeltaTime.GetTotalSeconds(), ZeroTolerance); From e0b587251af3f70ffca06f531a9d3dcad3425ab9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 6 Jun 2021 12:30:53 +0200 Subject: [PATCH 138/198] Add RingBuffer template --- Source/Engine/Core/Collections/RingBuffer.h | 95 +++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Source/Engine/Core/Collections/RingBuffer.h diff --git a/Source/Engine/Core/Collections/RingBuffer.h b/Source/Engine/Core/Collections/RingBuffer.h new file mode 100644 index 000000000..bffc70e40 --- /dev/null +++ b/Source/Engine/Core/Collections/RingBuffer.h @@ -0,0 +1,95 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Platform/Platform.h" +#include "Engine/Core/Memory/Memory.h" +#include "Engine/Core/Memory/Allocation.h" + +/// +/// Template for ring buffer with variable capacity. +/// +template +class RingBuffer +{ +public: + + typedef T ItemType; + typedef typename AllocationType::template Data AllocationData; + +private: + + int32 _front = 0, _back = 0, _count = 0, _capacity = 0; + AllocationData _allocation; + +public: + + ~RingBuffer() + { + Memory::DestructItems(Get() + Math::Min(_front, _back), _count); + } + + FORCE_INLINE T* Get() + { + return _allocation.Get(); + } + + FORCE_INLINE int32 Count() const + { + return _count; + } + + FORCE_INLINE int32 Capacity() const + { + return _capacity; + } + + void PushBack(const T& data) + { + if (_capacity == 0 || _capacity == _count) + { + const int32 capacity = _allocation.CalculateCapacityGrow(_capacity, 0); + AllocationData alloc; + alloc.Allocate(capacity); + const int32 frontCount = Math::Min(_capacity - _front, _count); + Memory::MoveItems(alloc.Get(), _allocation.Get() + _front, frontCount); + Memory::DestructItems(_allocation.Get() + _front, frontCount); + const int32 backCount = _count - frontCount; + Memory::MoveItems(alloc.Get() + frontCount, _allocation.Get(), backCount); + Memory::DestructItems(_allocation.Get(), backCount); + _allocation.Swap(alloc); + _front = 0; + _back = _count; + _capacity = capacity; + } + Memory::ConstructItems(_allocation.Get() + _back, &data, 1); + _back = (_back + 1) % _capacity; + _count++; + } + + FORCE_INLINE T& PeekFront() + { + ASSERT(_front != _back); + return _allocation.Get()[_front]; + } + + FORCE_INLINE const T& PeekFront() const + { + ASSERT(_front != _back); + return _allocation.Get()[_front]; + } + + void PopFront() + { + ASSERT(_front != _back); + Memory::DestructItems(_allocation.Get() + _front, 1); + _front = (_front + 1) % _capacity; + _count--; + } + + void Clear() + { + Memory::DestructItems(Get() + Math::Min(_front, _back), _count); + _front = _back = _count = 0; + } +}; From 982b22b4b19d28972647725cb16f8cc0e31673c6 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 6 Jun 2021 13:29:14 +0200 Subject: [PATCH 139/198] Add Job System --- Source/Engine/Threading/JobSystem.cpp | 200 ++++++++++++++++++++++++++ Source/Engine/Threading/JobSystem.h | 32 +++++ 2 files changed, 232 insertions(+) create mode 100644 Source/Engine/Threading/JobSystem.cpp create mode 100644 Source/Engine/Threading/JobSystem.h diff --git a/Source/Engine/Threading/JobSystem.cpp b/Source/Engine/Threading/JobSystem.cpp new file mode 100644 index 000000000..b878bbdea --- /dev/null +++ b/Source/Engine/Threading/JobSystem.cpp @@ -0,0 +1,200 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "JobSystem.h" +#include "IRunnable.h" +#include "Engine/Core/Collections/RingBuffer.h" +#include "Engine/Platform/CPUInfo.h" +#include "Engine/Platform/Thread.h" +#include "Engine/Platform/ConditionVariable.h" +#include "Engine/Engine/EngineService.h" +#include "Engine/Profiler/ProfilerCPU.h" + +class JobSystemService : public EngineService +{ +public: + + JobSystemService() + : EngineService(TEXT("JobSystem"), -800) + { + } + + bool Init() override; + void BeforeExit() override; + void Dispose() override; +}; + +struct JobData +{ + Function Job; + int32 Index; + int32 Count; +}; + +template<> +struct TIsPODType +{ + enum { Value = true }; +}; + +class JobSystemThread : public IRunnable +{ +public: + int32 Index; + +public: + + // [IRunnable] + String ToString() const override + { + return TEXT("JobSystemThread"); + } + + int32 Run() override; + + void AfterWork(bool wasKilled) override + { + Delete(this); + } +}; + +namespace +{ + JobSystemService JobSystemInstance; + Thread* Threads[32] = {}; + int32 ThreadsCount = 0; + volatile int64 ExitFlag = 0; + volatile int64 DoneLabel = 0; + volatile int64 NextLabel = 0; + CriticalSection JobsLocker; + ConditionVariable JobsSignal; + ConditionVariable WaitSignal; + RingBuffer> Jobs; +} + +bool JobSystemService::Init() +{ + ThreadsCount = Math::Min(Platform::GetCPUInfo().LogicalProcessorCount, ARRAY_COUNT(Threads)); + for (int32 i = 0; i < ThreadsCount; i++) + { + auto runnable = New(); + runnable->Index = i; + auto thread = Thread::Create(runnable, String::Format(TEXT("Job System {0}"), i), ThreadPriority::AboveNormal); + if (thread == nullptr) + return true; + Threads[i] = thread; + } + return false; +} + +void JobSystemService::BeforeExit() +{ + Platform::AtomicStore(&ExitFlag, 1); + JobsSignal.NotifyAll(); +} + +void JobSystemService::Dispose() +{ + Platform::AtomicStore(&ExitFlag, 1); + JobsSignal.NotifyAll(); + Platform::Sleep(1); + + for (int32 i = 0; i < ThreadsCount; i++) + { + if (Threads[i] && Threads[i]->IsRunning()) + Threads[i]->Kill(true); + Threads[i] = nullptr; + } +} + +int32 JobSystemThread::Run() +{ + Platform::SetThreadAffinityMask(1 << Index); + + JobData data; + CriticalSection mutex; + while (Platform::AtomicRead(&ExitFlag) == 0) + { + // Try to get a job + JobsLocker.Lock(); + if (Jobs.Count() != 0) + { + auto& front = Jobs.PeekFront(); + data = front; + front.Index++; + if (front.Index == front.Count) + { + Jobs.PopFront(); + } + } + JobsLocker.Unlock(); + + if (data.Job.IsBinded()) + { + // Run job + data.Job(data.Index); + data.Job.Unbind(); + + if (data.Index + 1 == data.Count) + { + // Move forward with the job queue + Platform::InterlockedIncrement(&DoneLabel); + WaitSignal.NotifyAll(); + } + } + else + { + // Wait for signal + mutex.Lock(); + JobsSignal.Wait(mutex); + mutex.Unlock(); + } + } + return 0; +} + +int64 JobSystem::Dispatch(const Function& job, int32 jobCount) +{ + PROFILE_CPU(); + if (jobCount <= 0) + return 0; + + JobData data; + data.Job = job; + data.Index = 0; + data.Count = jobCount; + + JobsLocker.Lock(); + const auto label = Platform::InterlockedIncrement(&NextLabel); + Jobs.PushBack(data); + JobsLocker.Unlock(); + + if (jobCount == 1) + JobsSignal.NotifyOne(); + else + JobsSignal.NotifyAll(); + + return label; +} + +void JobSystem::Wait() +{ + Wait(Platform::AtomicRead(&NextLabel)); +} + +void JobSystem::Wait(int64 label) +{ + PROFILE_CPU(); + + // Early out + if (label <= Platform::AtomicRead(&DoneLabel)) + return; + + // Wait on signal until input label is not yet done + CriticalSection mutex; + while (label > Platform::AtomicRead(&DoneLabel) && Platform::AtomicRead(&ExitFlag) == 0) + { + mutex.Lock(); + WaitSignal.Wait(mutex); + mutex.Unlock(); + } +} diff --git a/Source/Engine/Threading/JobSystem.h b/Source/Engine/Threading/JobSystem.h new file mode 100644 index 000000000..cf9b79073 --- /dev/null +++ b/Source/Engine/Threading/JobSystem.h @@ -0,0 +1,32 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Core/Delegate.h" + +/// +/// Lightweight multi-threaded jobs execution scheduler. Uses a pool of threads and supports work-stealing concept. +/// +API_CLASS(Static) class FLAXENGINE_API JobSystem +{ +DECLARE_SCRIPTING_TYPE_MINIMAL(JobSystem); + + /// + /// Dispatches the job for the execution. + /// + /// The job. Argument is an index of the job execution. + /// The job executions count. + /// The label identifying this dispatch. Can be used to wait for the execution end. + API_FUNCTION() static int64 Dispatch(const Function& job, int32 jobCount = 1); + + /// + /// Waits for all dispatched jobs to finish. + /// + API_FUNCTION() static void Wait(); + + /// + /// Waits for all dispatched jobs until a given label to finish (i.e. waits for a Dispatch that returned that label). + /// + /// The label. + API_FUNCTION() static void Wait(int64 label); +}; From 07ad94de13612d27255dacdd6299797b7a6292c1 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 6 Jun 2021 23:15:41 +0200 Subject: [PATCH 140/198] Optimize Animated Model bones matrices buffer update --- Source/Engine/Animations/Animations.cpp | 2 -- .../Graphics/Models/SkinnedMeshDrawData.cpp | 35 ++++++++++--------- .../Graphics/Models/SkinnedMeshDrawData.h | 7 +++- Source/Engine/Level/Actors/AnimatedModel.cpp | 14 ++++---- 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/Source/Engine/Animations/Animations.cpp b/Source/Engine/Animations/Animations.cpp index 9ef229d23..d2e095fa6 100644 --- a/Source/Engine/Animations/Animations.cpp +++ b/Source/Engine/Animations/Animations.cpp @@ -7,7 +7,6 @@ #include "Engine/Engine/EngineService.h" Array UpdateList; -Array UpdateBones; class AnimationsService : public EngineService { @@ -81,7 +80,6 @@ void AnimationsService::Update() void AnimationsService::Dispose() { UpdateList.Resize(0); - UpdateBones.Resize(0); } void Animations::AddToUpdate(AnimatedModel* obj) diff --git a/Source/Engine/Graphics/Models/SkinnedMeshDrawData.cpp b/Source/Engine/Graphics/Models/SkinnedMeshDrawData.cpp index a99984a0c..a8fc48889 100644 --- a/Source/Engine/Graphics/Models/SkinnedMeshDrawData.cpp +++ b/Source/Engine/Graphics/Models/SkinnedMeshDrawData.cpp @@ -41,10 +41,27 @@ void SkinnedMeshDrawData::SetData(const Matrix* bones, bool dropHistory) { if (!bones) return; - ASSERT(BonesCount > 0); - ANIM_GRAPH_PROFILE_EVENT("SetSkinnedMeshData"); + // Copy bones to the buffer + const int32 count = BonesCount; + const int32 preFetchStride = 2; + const Matrix* input = bones; + const auto output = (Matrix3x4*)Data.Get(); + ASSERT(Data.Count() == count * sizeof(Matrix3x4)); + for (int32 i = 0; i < count; i++) + { + Matrix3x4* bone = output + i; + Platform::Prefetch(bone + preFetchStride); + Platform::Prefetch((byte*)(bone + preFetchStride) + PLATFORM_CACHE_LINE_SIZE); + bone->SetMatrixTranspose(input[i]); + } + + OnDataChanged(dropHistory); +} + +void SkinnedMeshDrawData::OnDataChanged(bool dropHistory) +{ // Setup previous frame bone matrices if needed if (_hasValidData && !dropHistory) { @@ -64,20 +81,6 @@ void SkinnedMeshDrawData::SetData(const Matrix* bones, bool dropHistory) SAFE_DELETE_GPU_RESOURCE(PrevBoneMatrices); } - // Copy bones to the buffer - const int32 count = BonesCount; - const int32 preFetchStride = 2; - const Matrix* input = bones; - const auto output = (Matrix3x4*)Data.Get(); - ASSERT(Data.Count() == count * sizeof(Matrix3x4)); - for (int32 i = 0; i < count; i++) - { - Matrix3x4* bone = output + i; - Platform::Prefetch(bone + preFetchStride); - Platform::Prefetch((byte*)(bone + preFetchStride) + PLATFORM_CACHE_LINE_SIZE); - bone->SetMatrixTranspose(input[i]); - } - _isDirty = true; _hasValidData = true; } diff --git a/Source/Engine/Graphics/Models/SkinnedMeshDrawData.h b/Source/Engine/Graphics/Models/SkinnedMeshDrawData.h index f9d434fee..a9a882684 100644 --- a/Source/Engine/Graphics/Models/SkinnedMeshDrawData.h +++ b/Source/Engine/Graphics/Models/SkinnedMeshDrawData.h @@ -54,7 +54,6 @@ public: /// /// Determines whether this instance is ready for rendering. /// - /// True if has valid data and can be rendered, otherwise false. FORCE_INLINE bool IsReady() const { return BoneMatrices != nullptr && BoneMatrices->IsAllocated(); @@ -73,6 +72,12 @@ public: /// True if drop previous update bones used for motion blur, otherwise will keep them and do the update. void SetData(const Matrix* bones, bool dropHistory); + /// + /// After bones Data has been modified externally. Updates the bone matrices data for the GPU buffer. Ensure to call Flush before rendering. + /// + /// True if drop previous update bones used for motion blur, otherwise will keep them and do the update. + void OnDataChanged(bool dropHistory); + /// /// Flushes the bones data buffer with the GPU by sending the data fro the CPU. /// diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index ece54b78b..701df3d6e 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -2,6 +2,7 @@ #include "AnimatedModel.h" #include "BoneSocket.h" +#include "Engine/Core/Math/Matrix3x4.h" #include "Engine/Animations/Animations.h" #include "Engine/Engine/Engine.h" #if USE_EDITOR @@ -13,8 +14,6 @@ #include "Engine/Level/SceneObjectsFactory.h" #include "Engine/Serialization/Serialization.h" -extern Array UpdateBones; - AnimatedModel::AnimatedModel(const SpawnParams& params) : ModelInstanceActor(params) , _actualMode(AnimationUpdateMode::Never) @@ -470,14 +469,17 @@ void AnimatedModel::OnAnimationUpdated() // Calculate the final bones transformations and update skinning { ANIM_GRAPH_PROFILE_EVENT("Final Pose"); - UpdateBones.Resize(skeleton.Bones.Count(), false); - for (int32 boneIndex = 0; boneIndex < skeleton.Bones.Count(); boneIndex++) + const int32 bonesCount = skeleton.Bones.Count(); + Matrix3x4* output = (Matrix3x4*)_skinningData.Data.Get(); + ASSERT(_skinningData.Data.Count() == bonesCount * sizeof(Matrix3x4)); + for (int32 boneIndex = 0; boneIndex < bonesCount; boneIndex++) { auto& bone = skeleton.Bones[boneIndex]; - UpdateBones[boneIndex] = bone.OffsetMatrix * GraphInstance.NodesPose[bone.NodeIndex]; + Matrix matrix = bone.OffsetMatrix * GraphInstance.NodesPose[bone.NodeIndex]; + output[boneIndex].SetMatrixTranspose(matrix); } + _skinningData.OnDataChanged(!PerBoneMotionBlur); } - _skinningData.SetData(UpdateBones.Get(), !PerBoneMotionBlur); UpdateBounds(); UpdateSockets(); From 544cb1ff6ddb4db183a979cb205ea92462608a8e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 10 Jun 2021 19:08:48 +0200 Subject: [PATCH 141/198] Finish job system --- Source/Engine/Core/Collections/ChunkedArray.h | 44 ++++--- Source/Engine/Core/Collections/RingBuffer.h | 14 ++- Source/Engine/Threading/JobSystem.cpp | 119 ++++++++++++++---- 3 files changed, 135 insertions(+), 42 deletions(-) diff --git a/Source/Engine/Core/Collections/ChunkedArray.h b/Source/Engine/Core/Collections/ChunkedArray.h index 1ed78ffe2..0d24e980d 100644 --- a/Source/Engine/Core/Collections/ChunkedArray.h +++ b/Source/Engine/Core/Collections/ChunkedArray.h @@ -46,7 +46,6 @@ public: /// /// Gets the amount of the elements in the collection. /// - /// The amount of the elements in the collection. FORCE_INLINE int32 Count() const { return _count; @@ -55,7 +54,6 @@ public: /// /// Gets the amount of the elements that can be hold by collection without resizing. /// - /// The current capacity of the collection. FORCE_INLINE int32 Capacity() const { return _chunks.Count() * ChunkSize; @@ -64,7 +62,6 @@ public: /// /// Returns true if array isn't empty. /// - /// True if array has any elements added, otherwise it is empty. FORCE_INLINE bool HasItems() const { return _count != 0; @@ -73,7 +70,6 @@ public: /// /// Returns true if collection is empty. /// - /// True if array is empty, otherwise it has any elements added. FORCE_INLINE bool IsEmpty() const { return _count == 0; @@ -154,20 +150,12 @@ public: public: - /// - /// Checks if iterator is in the end of the collection. - /// - /// True if is in the end, otherwise false. bool IsEnd() const { ASSERT(_collection); return Index() == _collection->Count(); } - /// - /// Checks if iterator is not in the end of the collection. - /// - /// True if is not in the end, otherwise false. bool IsNotEnd() const { ASSERT(_collection); @@ -331,6 +319,36 @@ public: return &chunk->At(chunk->Count() - 1); } + /// + /// Adds the one item to the collection and returns the reference to it. + /// + /// The reference to the added item. + T& AddOne() + { + // Find first chunk with some space + Chunk* chunk = nullptr; + for (int32 i = 0; i < _chunks.Count(); i++) + { + if (_chunks[i]->Count() < ChunkSize) + { + chunk = _chunks[i]; + break; + } + } + + // Allocate chunk if missing + if (chunk == nullptr) + { + chunk = New(); + chunk->SetCapacity(ChunkSize); + _chunks.Add(chunk); + } + + // Add item + _count++; + return chunk->AddOne(); + } + /// /// Removes the element at specified iterator position. /// @@ -408,7 +426,6 @@ public: /// The new size. void Resize(int32 newSize) { - // Check if shrink if (newSize < Count()) { MISSING_CODE("shrinking ChunkedArray on Resize"); @@ -439,7 +456,6 @@ public: chunkIndex++; } } - ASSERT(newSize == Count()); } diff --git a/Source/Engine/Core/Collections/RingBuffer.h b/Source/Engine/Core/Collections/RingBuffer.h index bffc70e40..8a8deb86f 100644 --- a/Source/Engine/Core/Collections/RingBuffer.h +++ b/Source/Engine/Core/Collections/RingBuffer.h @@ -48,7 +48,7 @@ public: { if (_capacity == 0 || _capacity == _count) { - const int32 capacity = _allocation.CalculateCapacityGrow(_capacity, 0); + const int32 capacity = _allocation.CalculateCapacityGrow(_capacity, _count + 1); AllocationData alloc; alloc.Allocate(capacity); const int32 frontCount = Math::Min(_capacity - _front, _count); @@ -79,6 +79,18 @@ public: return _allocation.Get()[_front]; } + FORCE_INLINE T& operator[](int32 index) + { + ASSERT(index >= 0 && index < _count); + return _allocation.Get()[(_front + index) % _capacity]; + } + + FORCE_INLINE const T& operator[](int32 index) const + { + ASSERT(index >= 0 && index < _count); + return _allocation.Get()[(_front + index) % _capacity]; + } + void PopFront() { ASSERT(_front != _back); diff --git a/Source/Engine/Threading/JobSystem.cpp b/Source/Engine/Threading/JobSystem.cpp index b878bbdea..0a29bdecf 100644 --- a/Source/Engine/Threading/JobSystem.cpp +++ b/Source/Engine/Threading/JobSystem.cpp @@ -2,12 +2,35 @@ #include "JobSystem.h" #include "IRunnable.h" -#include "Engine/Core/Collections/RingBuffer.h" #include "Engine/Platform/CPUInfo.h" #include "Engine/Platform/Thread.h" #include "Engine/Platform/ConditionVariable.h" #include "Engine/Engine/EngineService.h" #include "Engine/Profiler/ProfilerCPU.h" +#include "Engine/Scripting/ManagedCLR/MCore.h" +#if USE_MONO +#include "Engine/Scripting/ManagedCLR/MDomain.h" +#include +#include +#endif + +// Jobs storage perf info: +// (500 jobs, i7 9th gen) +// JOB_SYSTEM_USE_MUTEX=1, enqueue=130-280 cycles, dequeue=2-6 cycles +// JOB_SYSTEM_USE_MUTEX=0, enqueue=300-700 cycles, dequeue=10-16 cycles +// So using RingBuffer+Mutex+Signals is better than moodycamel::ConcurrentQueue + +#define JOB_SYSTEM_USE_MUTEX 1 +#define JOB_SYSTEM_USE_STATS 0 + +#if JOB_SYSTEM_USE_STATS +#include "Engine/Core/Log.h" +#endif +#if JOB_SYSTEM_USE_MUTEX +#include "Engine/Core/Collections/RingBuffer.h" +#else +#include "ConcurrentQueue.h" +#endif class JobSystemService : public EngineService { @@ -27,7 +50,6 @@ struct JobData { Function Job; int32 Index; - int32 Count; }; template<> @@ -39,7 +61,7 @@ struct TIsPODType class JobSystemThread : public IRunnable { public: - int32 Index; + uint64 Index; public: @@ -65,10 +87,18 @@ namespace volatile int64 ExitFlag = 0; volatile int64 DoneLabel = 0; volatile int64 NextLabel = 0; - CriticalSection JobsLocker; ConditionVariable JobsSignal; ConditionVariable WaitSignal; +#if JOB_SYSTEM_USE_MUTEX + CriticalSection JobsLocker; RingBuffer> Jobs; +#else + ConcurrentQueue Jobs; +#endif +#if JOB_SYSTEM_USE_STATS + int64 DequeueCount = 0; + int64 DequeueSum = 0; +#endif } bool JobSystemService::Init() @@ -77,7 +107,7 @@ bool JobSystemService::Init() for (int32 i = 0; i < ThreadsCount; i++) { auto runnable = New(); - runnable->Index = i; + runnable->Index = (uint64)i; auto thread = Thread::Create(runnable, String::Format(TEXT("Job System {0}"), i), ThreadPriority::AboveNormal); if (thread == nullptr) return true; @@ -108,38 +138,57 @@ void JobSystemService::Dispose() int32 JobSystemThread::Run() { - Platform::SetThreadAffinityMask(1 << Index); + Platform::SetThreadAffinityMask(1ull << Index); JobData data; CriticalSection mutex; + bool attachMonoThread = true; +#if !JOB_SYSTEM_USE_MUTEX + moodycamel::ConsumerToken consumerToken(Jobs); +#endif while (Platform::AtomicRead(&ExitFlag) == 0) { // Try to get a job +#if JOB_SYSTEM_USE_STATS + const auto start = Platform::GetTimeCycles(); +#endif +#if JOB_SYSTEM_USE_MUTEX JobsLocker.Lock(); if (Jobs.Count() != 0) { - auto& front = Jobs.PeekFront(); - data = front; - front.Index++; - if (front.Index == front.Count) - { - Jobs.PopFront(); - } + data = Jobs.PeekFront(); + Jobs.PopFront(); } JobsLocker.Unlock(); +#else + if (!Jobs.try_dequeue(consumerToken, data)) + data.Job.Unbind(); +#endif +#if JOB_SYSTEM_USE_STATS + Platform::InterlockedIncrement(&DequeueCount); + Platform::InterlockedAdd(&DequeueSum, Platform::GetTimeCycles() - start); +#endif if (data.Job.IsBinded()) { +#if USE_MONO + // Ensure to have C# thread attached to this thead (late init due to MCore being initialized after Job System) + if (attachMonoThread && !mono_domain_get()) + { + const auto domain = MCore::Instance()->GetActiveDomain(); + mono_thread_attach(domain->GetNative()); + attachMonoThread = false; + } +#endif + // Run job data.Job(data.Index); - data.Job.Unbind(); - if (data.Index + 1 == data.Count) - { - // Move forward with the job queue - Platform::InterlockedIncrement(&DoneLabel); - WaitSignal.NotifyAll(); - } + // Move forward with the job queue + Platform::InterlockedIncrement(&DoneLabel); + WaitSignal.NotifyAll(); + + data.Job.Unbind(); } else { @@ -157,16 +206,27 @@ int64 JobSystem::Dispatch(const Function& job, int32 jobCount) PROFILE_CPU(); if (jobCount <= 0) return 0; +#if JOB_SYSTEM_USE_STATS + const auto start = Platform::GetTimeCycles(); +#endif JobData data; data.Job = job; - data.Index = 0; - data.Count = jobCount; +#if JOB_SYSTEM_USE_MUTEX JobsLocker.Lock(); - const auto label = Platform::InterlockedIncrement(&NextLabel); - Jobs.PushBack(data); + for (data.Index = 0; data.Index < jobCount; data.Index++) + Jobs.PushBack(data); JobsLocker.Unlock(); +#else + for (data.Index = 0; data.Index < jobCount; data.Index++) + Jobs.enqueue(data); +#endif + const auto label = Platform::InterlockedAdd(&NextLabel, (int64)jobCount) + jobCount; + +#if JOB_SYSTEM_USE_STATS + LOG(Info, "Job enqueue time: {0} cycles", (int64)(Platform::GetTimeCycles() - start)); +#endif if (jobCount == 1) JobsSignal.NotifyOne(); @@ -191,10 +251,15 @@ void JobSystem::Wait(int64 label) // Wait on signal until input label is not yet done CriticalSection mutex; - while (label > Platform::AtomicRead(&DoneLabel) && Platform::AtomicRead(&ExitFlag) == 0) + do { mutex.Lock(); - WaitSignal.Wait(mutex); + WaitSignal.Wait(mutex, 1); mutex.Unlock(); - } + } while (label > Platform::AtomicRead(&DoneLabel) && Platform::AtomicRead(&ExitFlag) == 0); + +#if JOB_SYSTEM_USE_STATS + LOG(Info, "Job average dequeue time: {0} cycles", DequeueSum / DequeueCount); + DequeueSum = DequeueCount = 0; +#endif } From b8ad4bdd2a6785c2faeb46537e4f799608aa3ec7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 10 Jun 2021 19:10:39 +0200 Subject: [PATCH 142/198] Refactor AnimGraph debug flows to use scripting API event --- Source/Editor/Editor.cs | 16 --------- Source/Editor/Managed/ManagedEditor.cpp | 36 ------------------- .../Windows/Assets/AnimationGraphWindow.cs | 24 +++++++++---- Source/Engine/Animations/Animations.cpp | 2 ++ Source/Engine/Animations/Animations.h | 15 ++++++-- .../Animations/Graph/AnimGraph.Custom.cpp | 4 --- Source/Engine/Animations/Graph/AnimGraph.cpp | 2 +- Source/Engine/Animations/Graph/AnimGraph.h | 4 --- 8 files changed, 32 insertions(+), 71 deletions(-) diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index d549bb907..70f52268c 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -1306,22 +1306,6 @@ namespace FlaxEditor VisualScriptingDebugFlow?.Invoke(debugFlow); } - [StructLayout(LayoutKind.Sequential)] - internal struct AnimGraphDebugFlowInfo - { - public Asset Asset; - public FlaxEngine.Object Object; - public uint NodeId; - public int BoxId; - } - - internal static event Action AnimGraphDebugFlow; - - internal static void Internal_OnAnimGraphDebugFlow(ref AnimGraphDebugFlowInfo debugFlow) - { - AnimGraphDebugFlow?.Invoke(debugFlow); - } - private static void RequestStartPlayOnEditMode() { if (Instance.StateMachine.IsEditMode) diff --git a/Source/Editor/Managed/ManagedEditor.cpp b/Source/Editor/Managed/ManagedEditor.cpp index 18a905d4b..6a83ceeb5 100644 --- a/Source/Editor/Managed/ManagedEditor.cpp +++ b/Source/Editor/Managed/ManagedEditor.cpp @@ -34,7 +34,6 @@ MMethod* Internal_GetGameWinPtr = nullptr; MMethod* Internal_GetGameWindowSize = nullptr; MMethod* Internal_OnAppExit = nullptr; MMethod* Internal_OnVisualScriptingDebugFlow = nullptr; -MMethod* Internal_OnAnimGraphDebugFlow = nullptr; MMethod* Internal_RequestStartPlayOnEditMode = nullptr; void OnLightmapsBake(ShadowsOfMordor::BuildProgressStep step, float stepProgress, float totalProgress, bool isProgressEvent) @@ -138,38 +137,6 @@ void OnVisualScriptingDebugFlow() } } -struct AnimGraphDebugFlowInfo -{ - MonoObject* Asset; - MonoObject* Object; - uint32 NodeId; - int32 BoxId; -}; - -void OnAnimGraphDebugFlow(Asset* asset, ScriptingObject* object, uint32 nodeId, uint32 boxId) -{ - if (Internal_OnAnimGraphDebugFlow == nullptr) - { - Internal_OnAnimGraphDebugFlow = ManagedEditor::GetStaticClass()->GetMethod("Internal_OnAnimGraphDebugFlow", 1); - ASSERT(Internal_OnAnimGraphDebugFlow); - } - - AnimGraphDebugFlowInfo flowInfo; - flowInfo.Asset = asset ? asset->GetOrCreateManagedInstance() : nullptr; - flowInfo.Object = object ? object->GetOrCreateManagedInstance() : nullptr; - flowInfo.NodeId = nodeId; - flowInfo.BoxId = boxId; - MonoObject* exception = nullptr; - void* params[1]; - params[0] = &flowInfo; - Internal_OnAnimGraphDebugFlow->Invoke(nullptr, params, &exception); - if (exception) - { - MException ex(exception); - ex.Log(LogType::Error, TEXT("OnAnimGraphDebugFlow")); - } -} - void OnLogMessage(LogType type, const StringView& msg); ManagedEditor::ManagedEditor() @@ -187,7 +154,6 @@ ManagedEditor::ManagedEditor() CSG::Builder::OnBrushModified.Bind(); Log::Logger::OnMessage.Bind(); VisualScripting::DebugFlow.Bind(); - AnimGraphExecutor::DebugFlow.Bind(); } ManagedEditor::~ManagedEditor() @@ -204,7 +170,6 @@ ManagedEditor::~ManagedEditor() CSG::Builder::OnBrushModified.Unbind(); Log::Logger::OnMessage.Unbind(); VisualScripting::DebugFlow.Unbind(); - AnimGraphExecutor::DebugFlow.Unbind(); } void ManagedEditor::Init() @@ -530,7 +495,6 @@ void ManagedEditor::DestroyManaged() Internal_GetGameWinPtr = nullptr; Internal_OnAppExit = nullptr; Internal_OnVisualScriptingDebugFlow = nullptr; - Internal_OnAnimGraphDebugFlow = nullptr; // Base PersistentScriptingObject::DestroyManaged(); diff --git a/Source/Editor/Windows/Assets/AnimationGraphWindow.cs b/Source/Editor/Windows/Assets/AnimationGraphWindow.cs index 634202061..603fc6c7c 100644 --- a/Source/Editor/Windows/Assets/AnimationGraphWindow.cs +++ b/Source/Editor/Windows/Assets/AnimationGraphWindow.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices; using FlaxEditor.Content; using FlaxEditor.CustomEditors; using FlaxEditor.CustomEditors.Editors; @@ -13,6 +14,7 @@ using FlaxEditor.Viewport.Cameras; using FlaxEditor.Viewport.Previews; using FlaxEngine; using FlaxEngine.GUI; +using Object = FlaxEngine.Object; // ReSharper disable UnusedMember.Local // ReSharper disable UnusedMember.Global @@ -206,11 +208,18 @@ namespace FlaxEditor.Windows.Assets } } + [StructLayout(LayoutKind.Sequential)] + private struct AnimGraphDebugFlowInfo + { + public uint NodeId; + public int BoxId; + } + private FlaxObjectRefPickerControl _debugPicker; private NavigationBar _navigationBar; private PropertiesProxy _properties; private Tab _previewTab; - private readonly List _debugFlows = new List(); + private readonly List _debugFlows = new List(); /// /// Gets the animated model actor used for the animation preview. @@ -285,7 +294,7 @@ namespace FlaxEditor.Windows.Assets Parent = this }; - Editor.AnimGraphDebugFlow += OnDebugFlow; + Animations.DebugFlow += OnDebugFlow; } private void OnSurfaceContextChanged(VisjectSurfaceContext context) @@ -293,26 +302,27 @@ namespace FlaxEditor.Windows.Assets _surface.UpdateNavigationBar(_navigationBar, _toolstrip); } - private bool OnCheckValid(FlaxEngine.Object obj, ScriptType type) + private bool OnCheckValid(Object obj, ScriptType type) { return obj is AnimatedModel player && player.AnimationGraph == OriginalAsset; } - private void OnDebugFlow(Editor.AnimGraphDebugFlowInfo flowInfo) + private void OnDebugFlow(Asset asset, Object obj, uint nodeId, uint boxId) { // Filter the flow if (_debugPicker.Value != null) { - if (flowInfo.Asset != OriginalAsset || _debugPicker.Value != flowInfo.Object) + if (asset != OriginalAsset || _debugPicker.Value != obj) return; } else { - if (flowInfo.Asset != Asset || _preview.PreviewActor != flowInfo.Object) + if (asset != Asset || _preview.PreviewActor != obj) return; } // Register flow to show it in UI on a surface + var flowInfo = new AnimGraphDebugFlowInfo { NodeId = nodeId, BoxId = (int)boxId }; lock (_debugFlows) { _debugFlows.Add(flowInfo); @@ -457,7 +467,7 @@ namespace FlaxEditor.Windows.Assets /// public override void OnDestroy() { - Editor.AnimGraphDebugFlow -= OnDebugFlow; + Animations.DebugFlow -= OnDebugFlow; _properties = null; _navigationBar = null; diff --git a/Source/Engine/Animations/Animations.cpp b/Source/Engine/Animations/Animations.cpp index d2e095fa6..037c87a5f 100644 --- a/Source/Engine/Animations/Animations.cpp +++ b/Source/Engine/Animations/Animations.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "Animations.h" +#include "Engine/Engine/Engine.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Level/Actors/AnimatedModel.h" #include "Engine/Engine/Time.h" @@ -22,6 +23,7 @@ public: }; AnimationsService AnimationManagerInstance; +Delegate Animations::DebugFlow; void AnimationsService::Update() { diff --git a/Source/Engine/Animations/Animations.h b/Source/Engine/Animations/Animations.h index a1b90e753..8b5b79c67 100644 --- a/Source/Engine/Animations/Animations.h +++ b/Source/Engine/Animations/Animations.h @@ -2,14 +2,23 @@ #pragma once +#include "Engine/Scripting/ScriptingType.h" +#include "Engine/Core/Delegate.h" + class AnimatedModel; +class Asset; /// -/// The animations service. +/// The animations playback service. /// -class FLAXENGINE_API Animations +API_CLASS(Static) class FLAXENGINE_API Animations { -public: +DECLARE_SCRIPTING_TYPE_NO_SPAWN(Content); + +#if USE_EDITOR + // Custom event that is called every time the Anim Graph signal flows over the graph (including the data connections). Can be used to read and visualize the animation blending logic. Args are: anim graph asset, animated object, node id, box id + API_EVENT() static Delegate DebugFlow; +#endif /// /// Adds an animated model to update. diff --git a/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp b/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp index 22fc6af28..60ae46f16 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp @@ -80,10 +80,6 @@ namespace AnimGraphInternal } } -#if USE_EDITOR -Delegate AnimGraphExecutor::DebugFlow; -#endif - void AnimGraphExecutor::initRuntime() { ADD_INTERNAL_CALL("FlaxEngine.AnimationGraph::Internal_HasConnection", &AnimGraphInternal::HasConnection); diff --git a/Source/Engine/Animations/Graph/AnimGraph.cpp b/Source/Engine/Animations/Graph/AnimGraph.cpp index 2932f7c3f..321c5a944 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.cpp @@ -312,7 +312,7 @@ VisjectExecutor::Value AnimGraphExecutor::eatBox(Node* caller, Box* box) _callStack.Add(caller); #if USE_EDITOR - DebugFlow(_graph._owner, _data->Object, box->GetParent()->ID, box->ID); + Animations::DebugFlow(_graph._owner, context.Data->Object, box->GetParent()->ID, box->ID); #endif // Call per group custom processing event diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h index 8ca4b7247..a8f055033 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.h +++ b/Source/Engine/Animations/Graph/AnimGraph.h @@ -828,10 +828,6 @@ private: public: -#if USE_EDITOR - // Custom event that is called every time the Anim Graph signal flows over the graph (including the data connections). Can be used to read and visualize the animation blending logic. - static Delegate DebugFlow; -#endif /// /// Initializes the managed runtime calls. From 41ad835d865fe148c97140021a0376bf8fed1369 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 12 Jun 2021 12:29:47 +0200 Subject: [PATCH 143/198] Refactor AnimGraph to support asynchronous execution --- .../Animations/Graph/AnimGraph.Base.cpp | 19 --- .../Animations/Graph/AnimGraph.Custom.cpp | 33 ++-- Source/Engine/Animations/Graph/AnimGraph.cpp | 124 ++++++++++----- Source/Engine/Animations/Graph/AnimGraph.h | 127 ++++------------ .../Animations/Graph/AnimGroup.Animation.cpp | 141 +++++++++--------- Source/Engine/Visject/VisjectGraph.h | 7 - 6 files changed, 199 insertions(+), 252 deletions(-) diff --git a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp index 22b74e6f8..8748e99bb 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.Base.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.Base.cpp @@ -9,25 +9,6 @@ #include "Engine/Utilities/Delaunay2D.h" #include "Engine/Serialization/MemoryReadStream.h" -void AnimGraphBase::ClearCache() -{ - // Clear sub-graphs - for (int32 i = 0; i < SubGraphs.Count(); i++) - { - SubGraphs[i]->ClearCache(); - } - - // Clear cache - for (int32 i = 0; i < Nodes.Count(); i++) - { - auto& node = Nodes[i]; - for (int32 j = 0; j < node.Boxes.Count(); j++) - { - node.Boxes[j].InvalidateCache(); - } - } -} - AnimSubGraph* AnimGraphBase::LoadSubGraph(const void* data, int32 dataLength, const Char* name) { if (data == nullptr || dataLength == 0) diff --git a/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp b/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp index 60ae46f16..5c082a8b5 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.Custom.cpp @@ -89,13 +89,10 @@ void AnimGraphExecutor::initRuntime() void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value& value) { - auto box = (AnimGraphBox*)boxBase; - if (box->IsCacheValid()) - { - // Return cache - value = box->Cache; + auto& context = Context.Get(); + if (context.ValueCache.TryGet(boxBase, value)) return; - } + auto box = (AnimGraphBox*)boxBase; auto node = (AnimGraphNode*)nodeBase; auto& data = node->Data.Custom; value = Value::Null; @@ -105,16 +102,16 @@ void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value& return; // Prepare node context - InternalContext context; - context.Graph = &_graph; - context.GraphExecutor = this; - context.Node = node; - context.NodeId = node->ID; - context.BoxId = box->ID; - context.DeltaTime = _deltaTime; - context.CurrentFrameIndex = _currentFrameIndex;; - context.BaseModel = _graph.BaseModel->GetOrCreateManagedInstance(); - context.Instance = _data->Object ? _data->Object->GetOrCreateManagedInstance() : nullptr; + InternalContext internalContext; + internalContext.Graph = &_graph; + internalContext.GraphExecutor = this; + internalContext.Node = node; + internalContext.NodeId = node->ID; + internalContext.BoxId = box->ID; + internalContext.DeltaTime = context.DeltaTime; + internalContext.CurrentFrameIndex = context.CurrentFrameIndex; + internalContext.BaseModel = _graph.BaseModel->GetOrCreateManagedInstance(); + internalContext.Instance = context.Data->Object ? context.Data->Object->GetOrCreateManagedInstance() : nullptr; // Peek managed object const auto obj = mono_gchandle_get_target(data.Handle); @@ -126,7 +123,7 @@ void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value& // Evaluate node void* params[1]; - params[0] = &context; + params[0] = &internalContext; MonoObject* exception = nullptr; MonoObject* result = data.Evaluate->Invoke(obj, params, &exception); if (exception) @@ -138,7 +135,7 @@ void AnimGraphExecutor::ProcessGroupCustom(Box* boxBase, Node* nodeBase, Value& // Extract result value = MUtils::UnboxVariant(result); - box->Cache = value; + context.ValueCache.Add(boxBase, value); } bool AnimGraph::IsReady() const diff --git a/Source/Engine/Animations/Graph/AnimGraph.cpp b/Source/Engine/Animations/Graph/AnimGraph.cpp index 321c5a944..6d95cbdfc 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.cpp @@ -1,11 +1,14 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "AnimGraph.h" +#include "Engine/Animations/Animations.h" #include "Engine/Content/Assets/SkinnedModel.h" #include "Engine/Graphics/Models/SkeletonData.h" #include "Engine/Scripting/Scripting.h" #include "Engine/Engine/Time.h" +ThreadLocal AnimGraphExecutor::Context; + RootMotionData RootMotionData::Identity = { Vector3(0.0f), Quaternion(0.0f, 0.0f, 0.0f, 1.0f) }; RootMotionData& RootMotionData::operator+=(const RootMotionData& b) @@ -78,16 +81,44 @@ void AnimGraphImpulse::SetNodeModelTransformation(SkeletonData& skeleton, int32 parentTransform.WorldToLocal(value, Nodes[nodeIndex]); } +void AnimGraphInstanceData::Clear() +{ + Version = 0; + LastUpdateTime = -1; + CurrentFrame = 0; + RootTransform = Transform::Identity; + RootMotion = RootMotionData::Identity; + Parameters.Resize(0); + State.Resize(0); + NodesPose.Resize(0); +} + +void AnimGraphInstanceData::ClearState() +{ + Version = 0; + LastUpdateTime = -1; + CurrentFrame = 0; + RootTransform = Transform::Identity; + RootMotion = RootMotionData::Identity; + State.Resize(0); + NodesPose.Resize(0); +} + +void AnimGraphInstanceData::Invalidate() +{ + LastUpdateTime = -1; + CurrentFrame = 0; +} + AnimGraphImpulse* AnimGraphNode::GetNodes(AnimGraphExecutor* executor) { - // Ensure to have memory + auto& context = AnimGraphExecutor::Context.Get(); const int32 count = executor->_skeletonNodesCount; - if (Nodes.Nodes.Count() != count) - { - Nodes.Nodes.Resize(count, false); - } - - return &Nodes; + if (context.PoseCacheSize == context.PoseCache.Count()) + context.PoseCache.AddOne(); + auto& nodes = context.PoseCache[context.PoseCacheSize++]; + nodes.Nodes.Resize(count, false); + return &nodes; } bool AnimGraph::Load(ReadStream* stream, bool loadMeta) @@ -181,20 +212,24 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt) // Initialize auto& skeleton = _graph.BaseModel->Skeleton; + auto& context = Context.Get(); { ANIM_GRAPH_PROFILE_EVENT("Init"); - // Prepare graph data for the evaluation + // Init data from base model _skeletonNodesCount = skeleton.Nodes.Count(); - _graphStack.Clear(); - _graphStack.Push((Graph*)&_graph); - _data = &data; - _deltaTime = dt; _rootMotionMode = (RootMotionMode)(int32)_graph._rootNode->Values[0]; - _currentFrameIndex = ++data.CurrentFrame; - _callStack.Clear(); - _functions.Clear(); - _graph.ClearCache(); + + // Prepare context data for the evaluation + context.GraphStack.Clear(); + context.GraphStack.Push((Graph*)&_graph); + context.Data = &data; + context.DeltaTime = dt; + context.CurrentFrameIndex = ++data.CurrentFrame; + context.CallStack.Clear(); + context.Functions.Clear(); + context.PoseCacheSize = 0; + context.ValueCache.Clear(); // Prepare instance data if (data.Version != _graph.Version) @@ -208,18 +243,18 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt) data.State.Resize(_graph.BucketsCountTotal, false); // Initialize buckets - ResetBuckets(&_graph); + ResetBuckets(context, &_graph); } // Init empty nodes data - _emptyNodes.RootMotion = RootMotionData::Identity; - _emptyNodes.Position = 0.0f; - _emptyNodes.Length = 0.0f; - _emptyNodes.Nodes.Resize(_skeletonNodesCount, false); + context.EmptyNodes.RootMotion = RootMotionData::Identity; + context.EmptyNodes.Position = 0.0f; + context.EmptyNodes.Length = 0.0f; + context.EmptyNodes.Nodes.Resize(_skeletonNodesCount, false); for (int32 i = 0; i < _skeletonNodesCount; i++) { auto& node = skeleton.Nodes[i]; - _emptyNodes.Nodes[i] = node.LocalTransform; + context.EmptyNodes.Nodes[i] = node.LocalTransform; } } @@ -244,7 +279,7 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt) { ANIM_GRAPH_PROFILE_EVENT("Global Pose"); - _data->NodesPose.Resize(_skeletonNodesCount, false); + data.NodesPose.Resize(_skeletonNodesCount, false); // Note: this assumes that nodes are sorted (parents first) for (int32 nodeIndex = 0; nodeIndex < _skeletonNodesCount; nodeIndex++) @@ -254,18 +289,16 @@ void AnimGraphExecutor::Update(AnimGraphInstanceData& data, float dt) { nodesTransformations[nodeIndex] = nodesTransformations[parentIndex].LocalToWorld(nodesTransformations[nodeIndex]); } - nodesTransformations[nodeIndex].GetWorld(_data->NodesPose[nodeIndex]); + nodesTransformations[nodeIndex].GetWorld(data.NodesPose[nodeIndex]); } - } - // Process the root node transformation and the motion - { - _data->RootTransform = nodesTransformations[0]; - _data->RootMotion = animResult->RootMotion; + // Process the root node transformation and the motion + data.RootTransform = nodesTransformations[0]; + data.RootMotion = animResult->RootMotion; } // Cleanup - _data = nullptr; + context.Data = nullptr; } void AnimGraphExecutor::GetInputValue(Box* box, Value& result) @@ -273,29 +306,39 @@ void AnimGraphExecutor::GetInputValue(Box* box, Value& result) result = eatBox(box->GetParent(), box->FirstConnection()); } -void AnimGraphExecutor::ResetBucket(int32 bucketIndex) +AnimGraphImpulse* AnimGraphExecutor::GetEmptyNodes() { - auto& stateBucket = _data->State[bucketIndex]; - _graph._bucketInitializerList[bucketIndex](stateBucket); + return &Context.Get().EmptyNodes; } -void AnimGraphExecutor::ResetBuckets(AnimGraphBase* graph) +void AnimGraphExecutor::InitNodes(AnimGraphImpulse* nodes) const +{ + const auto& emptyNodes = Context.Get().EmptyNodes; + Platform::MemoryCopy(nodes->Nodes.Get(), emptyNodes.Nodes.Get(), sizeof(Transform) * _skeletonNodesCount); + nodes->RootMotion = emptyNodes.RootMotion; + nodes->Position = emptyNodes.Position; + nodes->Length = emptyNodes.Length; +} + +void AnimGraphExecutor::ResetBuckets(AnimGraphContext& context, AnimGraphBase* graph) { if (graph == nullptr) return; - ASSERT(_data); + auto& state = context.Data->State; for (int32 i = 0; i < graph->BucketsCountTotal; i++) { const int32 bucketIndex = graph->BucketsStart + i; - _graph._bucketInitializerList[bucketIndex](_data->State[bucketIndex]); + _graph._bucketInitializerList[bucketIndex](state[bucketIndex]); } } VisjectExecutor::Value AnimGraphExecutor::eatBox(Node* caller, Box* box) { + auto& context = Context.Get(); + // Check if graph is looped or is too deep - if (_callStack.Count() >= ANIM_GRAPH_MAX_CALL_STACK) + if (context.CallStack.Count() >= ANIM_GRAPH_MAX_CALL_STACK) { OnError(caller, box, TEXT("Graph is looped or too deep!")); return Value::Zero; @@ -309,7 +352,7 @@ VisjectExecutor::Value AnimGraphExecutor::eatBox(Node* caller, Box* box) #endif // Add to the calling stack - _callStack.Add(caller); + context.CallStack.Add(caller); #if USE_EDITOR Animations::DebugFlow(_graph._owner, context.Data->Object, box->GetParent()->ID, box->ID); @@ -322,12 +365,13 @@ VisjectExecutor::Value AnimGraphExecutor::eatBox(Node* caller, Box* box) (this->*func)(box, parentNode, value); // Remove from the calling stack - _callStack.RemoveLast(); + context.CallStack.RemoveLast(); return value; } VisjectExecutor::Graph* AnimGraphExecutor::GetCurrentGraph() const { - return _graphStack.Peek(); + auto& context = Context.Get(); + return context.GraphStack.Peek(); } diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h index a8f055033..1e011aed3 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.h +++ b/Source/Engine/Animations/Graph/AnimGraph.h @@ -4,6 +4,7 @@ #include "Engine/Visject/VisjectGraph.h" #include "Engine/Content/Assets/Animation.h" +#include "Engine/Core/Collections/ChunkedArray.h" #include "Engine/Animations/AlphaBlend.h" #include "Engine/Core/Math/Matrix.h" #include "../Config.h" @@ -362,40 +363,17 @@ public: /// /// Clears this container data. /// - void Clear() - { - Version = 0; - LastUpdateTime = -1; - CurrentFrame = 0; - RootTransform = Transform::Identity; - RootMotion = RootMotionData::Identity; - Parameters.Resize(0); - State.Resize(0); - NodesPose.Resize(0); - } + void Clear(); /// /// Clears this container state data. /// - void ClearState() - { - Version = 0; - LastUpdateTime = -1; - CurrentFrame = 0; - RootTransform = Transform::Identity; - RootMotion = RootMotionData::Identity; - State.Resize(0); - NodesPose.Resize(0); - } + void ClearState(); /// /// Invalidates the update timer. /// - void Invalidate() - { - LastUpdateTime = -1; - CurrentFrame = 0; - } + void Invalidate(); }; /// @@ -424,18 +402,6 @@ public: : VisjectGraphBox(parent, id, type) { } - -public: - - bool IsCacheValid() const - { - return Cache.Type.Type != VariantType::Pointer || Cache.AsPointer != nullptr; - } - - void InvalidateCache() - { - Cache = Variant::Null; - } }; class AnimGraphNode : public VisjectGraphNode @@ -575,13 +541,6 @@ public: /// int32 BucketIndex = -1; - // TODO: use shared allocator per AnimGraph to reduce dynamic memory allocation (also bones data would be closer in memory -> less cache misses) - - /// - /// The node transformations (layout matches the linked to graph skinned model skeleton). - /// - AnimGraphImpulse Nodes; - /// /// The custom data (depends on node type). Used to cache data for faster usage at runtime. /// @@ -661,17 +620,11 @@ public: /// /// Gets the root node of the graph (cache don load). /// - /// The root node. FORCE_INLINE Node* GetRootNode() const { return _rootNode; } - /// - /// Clear all cached values in the graph nodes and the sub-graphs data. - /// - void ClearCache(); - /// /// Loads the sub-graph. /// @@ -751,9 +704,9 @@ public: AnimGraph(Asset* owner, bool isFunction = false) : AnimGraphBase(this) , _isFunction(isFunction) + , _isRegisteredForScriptingEvents(false) , _bucketInitializerList(64) , _owner(owner) - , _isRegisteredForScriptingEvents(false) { } @@ -806,6 +759,24 @@ public: bool onParamCreated(Parameter* p) override; }; +/// +/// The Animation Graph evaluation context. +/// +struct AnimGraphContext +{ + float DeltaTime; + uint64 CurrentFrameIndex; + AnimGraphInstanceData* Data; + AnimGraphImpulse EmptyNodes; + AnimGraphTransitionData TransitionData; + Array> CallStack; + Array> GraphStack; + Dictionary Functions; + ChunkedArray PoseCache; + int32 PoseCacheSize; + Dictionary ValueCache; +}; + /// /// The Animation Graph executor runtime for animation pose evaluation. /// @@ -815,20 +786,14 @@ class AnimGraphExecutor : public VisjectExecutor private: AnimGraph& _graph; - float _deltaTime = 0.0f; - uint64 _currentFrameIndex = 0; - int32 _skeletonNodesCount = 0; RootMotionMode _rootMotionMode = RootMotionMode::NoExtraction; - AnimGraphInstanceData* _data = nullptr; - AnimGraphImpulse _emptyNodes; - AnimGraphTransitionData _transitionData; - Array> _callStack; - Array> _graphStack; - Dictionary _functions; + int32 _skeletonNodesCount = 0; + + // Per-thread context to allow async execution + static ThreadLocal Context; public: - /// /// Initializes the managed runtime calls. /// @@ -854,34 +819,10 @@ public: /// /// Gets the skeleton nodes transformations structure containing identity matrices. /// - FORCE_INLINE const AnimGraphImpulse* GetEmptyNodes() const - { - return &_emptyNodes; - } + AnimGraphImpulse* GetEmptyNodes(); - /// - /// Gets the skeleton nodes transformations structure containing identity matrices. - /// - /// The data. - FORCE_INLINE AnimGraphImpulse* GetEmptyNodes() - { - return &_emptyNodes; - } - - FORCE_INLINE void InitNodes(AnimGraphImpulse* nodes) const - { - // Initialize with cached node transformations - Platform::MemoryCopy(nodes->Nodes.Get(), _emptyNodes.Nodes.Get(), sizeof(Transform) * _skeletonNodesCount); - nodes->RootMotion = _emptyNodes.RootMotion; - nodes->Position = _emptyNodes.Position; - nodes->Length = _emptyNodes.Length; - } - - FORCE_INLINE void InitNode(AnimGraphImpulse* nodes, int32 index) const - { - // Initialize with cached node transformation - nodes->Nodes[index] = GetEmptyNodes()->Nodes[index]; - } + // Initialize impulse with cached node transformations + void InitNodes(AnimGraphImpulse* nodes) const; FORCE_INLINE void CopyNodes(AnimGraphImpulse* dstNodes, AnimGraphImpulse* srcNodes) const { @@ -899,16 +840,10 @@ public: CopyNodes(dstNodes, static_cast(value.AsPointer)); } - /// - /// Resets the state bucket. - /// - /// The zero-based index of the bucket. - void ResetBucket(int32 bucketIndex); - /// /// Resets all the state bucket used by the given graph including sub-graphs (total). Can eb used to reset the animation state of the nested graph (including children). /// - void ResetBuckets(AnimGraphBase* graph); + void ResetBuckets(AnimGraphContext& context, AnimGraphBase* graph); private: diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index d7baadfc0..732390112 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -14,7 +14,7 @@ int32 AnimGraphExecutor::GetRootNodeIndex(Animation* anim) if (anim->Data.RootNodeName.HasChars()) { auto& skeleton = _graph.BaseModel->Skeleton; - for (int32 i = 0; i < _skeletonNodesCount; i++) + for (int32 i = 0; i < skeleton.Nodes.Count(); i++) { if (skeleton.Nodes[i].Name == anim->Data.RootNodeName) { @@ -119,7 +119,7 @@ float GetAnimSamplePos(float length, Animation* anim, float pos, float speed) // Also, scale the animation to fit the total animation node length without cut in a middle const auto animLength = anim->GetLength(); const int32 cyclesCount = Math::FloorToInt(length / animLength); - const float cycleLength = animLength * cyclesCount; + const float cycleLength = animLength * (float)cyclesCount; const float adjustRateScale = length / cycleLength; auto animPos = pos * speed * adjustRateScale; while (animPos > animLength) @@ -152,10 +152,11 @@ Variant AnimGraphExecutor::SampleAnimation(AnimGraphNode* node, bool loop, float nodes->Position = pos; nodes->Length = length; const auto mapping = anim->GetMapping(_graph.BaseModel); - for (int32 i = 0; i < _skeletonNodesCount; i++) + const auto emptyNodes = GetEmptyNodes(); + for (int32 i = 0; i < nodes->Nodes.Count(); i++) { const int32 nodeToChannel = mapping->At(i); - InitNode(nodes, i); + nodes->Nodes[i] = emptyNodes->Nodes[i]; if (nodeToChannel != -1) { // Calculate the animated node transformation @@ -197,7 +198,7 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l nodes->Length = length; const auto mappingA = animA->GetMapping(_graph.BaseModel); const auto mappingB = animB->GetMapping(_graph.BaseModel); - for (int32 i = 0; i < _skeletonNodesCount; i++) + for (int32 i = 0; i < nodes->Nodes.Count(); i++) { const int32 nodeToChannelA = mappingA->At(i); const int32 nodeToChannelB = mappingB->At(i); @@ -286,12 +287,13 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l const auto mappingB = animB->GetMapping(_graph.BaseModel); const auto mappingC = animC->GetMapping(_graph.BaseModel); Transform tmp, t; - for (int32 i = 0; i < _skeletonNodesCount; i++) + const auto emptyNodes = GetEmptyNodes(); + for (int32 i = 0; i < nodes->Nodes.Count(); i++) { const int32 nodeToChannelA = mappingA->At(i); const int32 nodeToChannelB = mappingB->At(i); const int32 nodeToChannelC = mappingC->At(i); - tmp = t = GetEmptyNodes()->Nodes[i]; + tmp = t = emptyNodes->Nodes[i]; // Calculate the animated node transformations if (nodeToChannelA != -1) @@ -384,7 +386,7 @@ Variant AnimGraphExecutor::Blend(AnimGraphNode* node, const Value& poseA, const if (!ANIM_GRAPH_IS_VALID_PTR(poseB)) nodesB = GetEmptyNodes(); - for (int32 i = 0; i < _skeletonNodesCount; i++) + for (int32 i = 0; i < nodes->Nodes.Count(); i++) { Transform::Lerp(nodesA->Nodes[i], nodesB->Nodes[i], alpha, nodes->Nodes[i]); } @@ -443,6 +445,7 @@ void ComputeMultiBlendLength(float& length, AnimGraphNode* node) void AnimGraphExecutor::ProcessGroupParameters(Box* box, Node* node, Value& value) { + auto& context = Context.Get(); switch (node->TypeID) { // Get @@ -453,7 +456,7 @@ void AnimGraphExecutor::ProcessGroupParameters(Box* box, Node* node, Value& valu const auto param = _graph.GetParameter((Guid)node->Values[0], paramIndex); if (param) { - value = _data->Parameters[paramIndex].Value; + value = context.Data->Parameters[paramIndex].Value; switch (param->Type.Type) { case VariantType::Vector2: @@ -523,19 +526,20 @@ void AnimGraphExecutor::ProcessGroupParameters(Box* box, Node* node, Value& valu void AnimGraphExecutor::ProcessGroupTools(Box* box, Node* nodeBase, Value& value) { + auto& context = Context.Get(); auto node = (AnimGraphNode*)nodeBase; switch (node->TypeID) { // Time case 5: { - auto& bucket = _data->State[node->BucketIndex].Animation; - if (bucket.LastUpdateFrame != _currentFrameIndex) + auto& bucket = context.Data->State[node->BucketIndex].Animation; + if (bucket.LastUpdateFrame != context.CurrentFrameIndex) { - bucket.TimePosition += _deltaTime; - bucket.LastUpdateFrame = _currentFrameIndex; + bucket.TimePosition += context.DeltaTime; + bucket.LastUpdateFrame = context.CurrentFrameIndex; } - value = box->ID == 0 ? bucket.TimePosition : _deltaTime; + value = box->ID == 0 ? bucket.TimePosition : context.DeltaTime; break; } default: @@ -546,13 +550,10 @@ void AnimGraphExecutor::ProcessGroupTools(Box* box, Node* nodeBase, Value& value void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Value& value) { - auto box = (AnimGraphBox*)boxBase; - if (box->IsCacheValid()) - { - // Return cache - value = box->Cache; + auto& context = Context.Get(); + if (context.ValueCache.TryGet(boxBase, value)) return; - } + auto box = (AnimGraphBox*)boxBase; auto node = (AnimGraphNode*)nodeBase; switch (node->TypeID) { @@ -569,7 +570,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu case 2: { const auto anim = node->Assets[0].As(); - auto& bucket = _data->State[node->BucketIndex].Animation; + auto& bucket = context.Data->State[node->BucketIndex].Animation; const float speed = (float)tryGetValue(node->GetBox(5), node->Values[1]); const bool loop = (bool)tryGetValue(node->GetBox(6), node->Values[2]); const float startTimePos = (float)tryGetValue(node->GetBox(7), node->Values[3]); @@ -584,17 +585,17 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu const float length = anim ? anim->GetLength() : 0.0f; // Calculate new time position - if (speed < 0.0f && bucket.LastUpdateFrame < _currentFrameIndex - 1) + if (speed < 0.0f && bucket.LastUpdateFrame < context.CurrentFrameIndex - 1) { // If speed is negative and it's the first node update then start playing from end bucket.TimePosition = length; } - float newTimePos = bucket.TimePosition + _deltaTime * speed; + float newTimePos = bucket.TimePosition + context.DeltaTime * speed; value = SampleAnimation(node, loop, length, startTimePos, bucket.TimePosition, newTimePos, anim, 1.0f); bucket.TimePosition = newTimePos; - bucket.LastUpdateFrame = _currentFrameIndex; + bucket.LastUpdateFrame = context.CurrentFrameIndex; break; } @@ -615,7 +616,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu // Is Playing case 4: // If anim was updated during this or a previous frame - value = bucket.LastUpdateFrame >= _currentFrameIndex - 1; + value = bucket.LastUpdateFrame >= context.CurrentFrameIndex - 1; break; } break; @@ -643,7 +644,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu value = Value::Null; if (inputBox->HasConnection()) value = eatBox(nodeBase, inputBox->FirstConnection()); - box->Cache = value; + context.ValueCache.Add(boxBase, value); return; } const auto nodeIndex = _graph.BaseModel->Skeleton.Bones[boneIndex].NodeIndex; @@ -690,7 +691,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu // Transform every node const auto& skeleton = BaseModel->Skeleton; - for (int32 i = 0; i < _skeletonNodesCount; i++) + for (int32 i = 0; i < nodes->Nodes.Count(); i++) { const int32 parentIndex = skeleton.Nodes[i].ParentIndex; if (parentIndex != -1) @@ -729,7 +730,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu // Inv transform every node const auto& skeleton = BaseModel->Skeleton; - for (int32 i = _skeletonNodesCount - 1; i >= 0; i--) + for (int32 i = nodes->Nodes.Count() - 1; i >= 0; i--) { const int32 parentIndex = skeleton.Nodes[i].ParentIndex; if (parentIndex != -1) @@ -775,7 +776,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu { // Pass through the input value = input; - box->Cache = value; + context.ValueCache.Add(boxBase, value); return; } @@ -836,7 +837,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu if (!ANIM_GRAPH_IS_VALID_PTR(valueB)) nodesB = GetEmptyNodes(); - for (int32 i = 0; i < _skeletonNodesCount; i++) + for (int32 i = 0; i < nodes->Nodes.Count(); i++) { Transform::Lerp(nodesA->Nodes[i], nodesB->Nodes[i], alpha, nodes->Nodes[i]); } @@ -876,7 +877,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu const auto nodesA = static_cast(valueA.AsPointer); const auto nodesB = static_cast(valueB.AsPointer); Transform t, tA, tB; - for (int32 i = 0; i < _skeletonNodesCount; i++) + for (int32 i = 0; i < nodes->Nodes.Count(); i++) { tA = nodesA->Nodes[i]; tB = nodesB->Nodes[i]; @@ -921,7 +922,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu // Blend all nodes masked by the user Transform tA, tB; auto& nodesMask = mask->GetNodesMask(); - for (int32 nodeIndex = 0; nodeIndex < _skeletonNodesCount; nodeIndex++) + for (int32 nodeIndex = 0; nodeIndex < nodes->Nodes.Count(); nodeIndex++) { tA = nodesA->Nodes[nodeIndex]; if (nodesMask[nodeIndex]) @@ -956,7 +957,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu // [1]: Guid Animation // Prepare - auto& bucket = _data->State[node->BucketIndex].MultiBlend; + auto& bucket = context.Data->State[node->BucketIndex].MultiBlend; const auto range = node->Values[0].AsVector4(); const auto speed = (float)tryGetValue(node->GetBox(1), node->Values[1]); const auto loop = (bool)tryGetValue(node->GetBox(2), node->Values[2]); @@ -988,12 +989,12 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu } // Calculate new time position - if (speed < 0.0f && bucket.LastUpdateFrame < _currentFrameIndex - 1) + if (speed < 0.0f && bucket.LastUpdateFrame < context.CurrentFrameIndex - 1) { // If speed is negative and it's the first node update then start playing from end bucket.TimePosition = data.Length; } - float newTimePos = bucket.TimePosition + _deltaTime * speed; + float newTimePos = bucket.TimePosition + context.DeltaTime * speed; ANIM_GRAPH_PROFILE_EVENT("Multi Blend 1D"); @@ -1035,7 +1036,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu } bucket.TimePosition = newTimePos; - bucket.LastUpdateFrame = _currentFrameIndex; + bucket.LastUpdateFrame = context.CurrentFrameIndex; break; } @@ -1054,7 +1055,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu // [1]: Guid Animation // Prepare - auto& bucket = _data->State[node->BucketIndex].MultiBlend; + auto& bucket = context.Data->State[node->BucketIndex].MultiBlend; const auto range = node->Values[0].AsVector4(); const auto speed = (float)tryGetValue(node->GetBox(1), node->Values[1]); const auto loop = (bool)tryGetValue(node->GetBox(2), node->Values[2]); @@ -1090,12 +1091,12 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu } // Calculate new time position - if (speed < 0.0f && bucket.LastUpdateFrame < _currentFrameIndex - 1) + if (speed < 0.0f && bucket.LastUpdateFrame < context.CurrentFrameIndex - 1) { // If speed is negative and it's the first node update then start playing from end bucket.TimePosition = data.Length; } - float newTimePos = bucket.TimePosition + _deltaTime * speed; + float newTimePos = bucket.TimePosition + context.DeltaTime * speed; ANIM_GRAPH_PROFILE_EVENT("Multi Blend 2D"); @@ -1227,7 +1228,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu } bucket.TimePosition = newTimePos; - bucket.LastUpdateFrame = _currentFrameIndex; + bucket.LastUpdateFrame = context.CurrentFrameIndex; break; } @@ -1246,7 +1247,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu // [3]: AlphaBlendMode Mode // Prepare - auto& bucket = _data->State[node->BucketIndex].BlendPose; + auto& bucket = context.Data->State[node->BucketIndex].BlendPose; const int32 poseIndex = (int32)tryGetValue(node->GetBox(1), node->Values[0]); const float blendDuration = (float)tryGetValue(node->GetBox(2), node->Values[1]); const int32 poseCount = Math::Clamp(node->Values[2].AsInt, 0, MaxBlendPoses); @@ -1259,7 +1260,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu } // Check if transition is not active (first update, pose not changing or transition ended) - bucket.TransitionPosition += _deltaTime; + bucket.TransitionPosition += context.DeltaTime; if (bucket.PreviousBlendPoseIndex == -1 || bucket.PreviousBlendPoseIndex == poseIndex || bucket.TransitionPosition >= blendDuration || blendDuration <= ANIM_GRAPH_BLEND_THRESHOLD) { bucket.TransitionPosition = 0.0f; @@ -1356,11 +1357,11 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu ANIM_GRAPH_PROFILE_EVENT("State Machine"); // Prepare - auto& bucket = _data->State[node->BucketIndex].StateMachine; + auto& bucket = context.Data->State[node->BucketIndex].StateMachine; auto& data = node->Data.StateMachine; int32 transitionsLeft = maxTransitionsPerUpdate == 0 ? MAX_uint16 : maxTransitionsPerUpdate; bool isFirstUpdate = bucket.LastUpdateFrame == 0 || bucket.CurrentState == nullptr; - if (bucket.LastUpdateFrame != _currentFrameIndex - 1 && reinitializeOnBecomingRelevant) + if (bucket.LastUpdateFrame != context.CurrentFrameIndex - 1 && reinitializeOnBecomingRelevant) { // Reset on becoming relevant isFirstUpdate = true; @@ -1384,19 +1385,19 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu bucket.TransitionPosition = 0.0f; // Reset all state buckets pof the graphs and nodes included inside the state machine - ResetBuckets(data.Graph); + ResetBuckets(context, data.Graph); } // Update the active transition if (bucket.ActiveTransition) { - bucket.TransitionPosition += _deltaTime; + bucket.TransitionPosition += context.DeltaTime; - // Check ofr transition end + // Check for transition end if (bucket.TransitionPosition >= bucket.ActiveTransition->BlendDuration) { // End transition - ResetBuckets(bucket.CurrentState->Data.State.Graph); + ResetBuckets(context, bucket.CurrentState->Data.State.Graph); bucket.CurrentState = bucket.ActiveTransition->Destination; bucket.ActiveTransition = nullptr; bucket.TransitionPosition = 0.0f; @@ -1422,7 +1423,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu // Evaluate source state transition data (position, length, etc.) const Value sourceStatePtr = SampleState(bucket.CurrentState); - auto& transitionData = _transitionData; // Note: this could support nested transitions but who uses state machine inside transition rule? + auto& transitionData = context.TransitionData; // Note: this could support nested transitions but who uses state machine inside transition rule? if (ANIM_GRAPH_IS_VALID_PTR(sourceStatePtr)) { // Use source state as data provider @@ -1475,7 +1476,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu if (bucket.ActiveTransition && bucket.ActiveTransition->BlendDuration <= ZeroTolerance) { // End transition - ResetBuckets(bucket.CurrentState->Data.State.Graph); + ResetBuckets(context, bucket.CurrentState->Data.State.Graph); bucket.CurrentState = bucket.ActiveTransition->Destination; bucket.ActiveTransition = nullptr; bucket.TransitionPosition = 0.0f; @@ -1498,7 +1499,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu } // Update bucket - bucket.LastUpdateFrame = _currentFrameIndex; + bucket.LastUpdateFrame = context.CurrentFrameIndex; break; } @@ -1537,7 +1538,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu // Transition Source State Anim case 23: { - const AnimGraphTransitionData& transitionsData = _transitionData; + const AnimGraphTransitionData& transitionsData = context.TransitionData; switch (box->ID) { // Length @@ -1587,7 +1588,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu if (callFunc == function) { value = Value::Zero; - box->Cache = value; + context.ValueCache.Add(boxBase, value); return; } } @@ -1606,12 +1607,12 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu Box* functionOutputBox = functionOutputNode->TryGetBox(0); // Cache relation between current node in the call stack to the actual function graph - _functions[nodeBase] = (Graph*)data.Graph; + context.Functions[nodeBase] = (Graph*)data.Graph; // Evaluate the function output - _graphStack.Push((Graph*)data.Graph); + context.GraphStack.Push((Graph*)data.Graph); value = functionOutputBox && functionOutputBox->HasConnection() ? eatBox(nodeBase, functionOutputBox->FirstConnection()) : Value::Zero; - _graphStack.Pop(); + context.GraphStack.Pop(); break; } // Transform Bone (local/model space) @@ -1635,7 +1636,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu value = Value::Null; if (inputBox->HasConnection()) value = eatBox(nodeBase, inputBox->FirstConnection()); - box->Cache = value; + context.ValueCache.Add(boxBase, value); return; } const auto nodes = node->GetNodes(this); @@ -1704,7 +1705,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu { // Pass through the input value = input; - box->Cache = value; + context.ValueCache.Add(boxBase, value); return; } @@ -1859,18 +1860,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu default: break; } - box->Cache = value; + context.ValueCache.Add(boxBase, value); } void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& value) { - auto box = (AnimGraphBox*)boxBase; - if (box->IsCacheValid()) - { - // Return cache - value = box->Cache; + auto& context = Context.Get(); + if (context.ValueCache.TryGet(boxBase, value)) return; - } switch (node->TypeID) { // Function Input @@ -1878,13 +1875,13 @@ void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& va { // Find the function call AnimGraphNode* functionCallNode = nullptr; - ASSERT(_graphStack.Count() >= 2); + ASSERT(context.GraphStack.Count() >= 2); Graph* graph; - for (int32 i = _callStack.Count() - 1; i >= 0; i--) + for (int32 i = context.CallStack.Count() - 1; i >= 0; i--) { - if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(9, 24) && _functions.TryGet(_callStack[i], graph) && _graphStack[_graphStack.Count() - 1] == (Graph*)graph) + if (context.CallStack[i]->Type == GRAPH_NODE_MAKE_TYPE(9, 24) && context.Functions.TryGet(context.CallStack[i], graph) && context.GraphStack.Last() == (Graph*)graph) { - functionCallNode = (AnimGraphNode*)_callStack[i]; + functionCallNode = (AnimGraphNode*)context.CallStack[i]; break; } } @@ -1926,19 +1923,19 @@ void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& va if (functionCallBox && functionCallBox->HasConnection()) { // Use provided input value from the function call - _graphStack.Pop(); + context.GraphStack.Pop(); value = eatBox(node, functionCallBox->FirstConnection()); - _graphStack.Push(graph); + context.GraphStack.Push(graph); } else { // Use the default value from the function graph value = tryGetValue(node->TryGetBox(1), Value::Zero); } + context.ValueCache.Add(boxBase, value); break; } default: break; } - box->Cache = value; } diff --git a/Source/Engine/Visject/VisjectGraph.h b/Source/Engine/Visject/VisjectGraph.h index 7be06566e..d38234c6d 100644 --- a/Source/Engine/Visject/VisjectGraph.h +++ b/Source/Engine/Visject/VisjectGraph.h @@ -18,13 +18,6 @@ class VisjectGraphNode; class VisjectGraphBox : public GraphBox { -public: - - /// - /// The cached value. - /// - Variant Cache; - public: VisjectGraphBox() From 47af31a8c4ddd7fd21d50fa1575487ae6eace435 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 12 Jun 2021 19:35:27 +0200 Subject: [PATCH 144/198] Tweaks --- Source/Engine/Profiler/ProfilerCPU.cpp | 2 +- Source/Engine/Threading/JobSystem.cpp | 15 +++++++++++++++ Source/Engine/Threading/ThreadLocal.h | 6 ++++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Source/Engine/Profiler/ProfilerCPU.cpp b/Source/Engine/Profiler/ProfilerCPU.cpp index 2119a25d7..cab429d2c 100644 --- a/Source/Engine/Profiler/ProfilerCPU.cpp +++ b/Source/Engine/Profiler/ProfilerCPU.cpp @@ -12,7 +12,7 @@ bool ProfilerCPU::Enabled = false; ProfilerCPU::EventBuffer::EventBuffer() { - _capacity = Math::RoundUpToPowerOf2(10 * 1000); + _capacity = 8192; _capacityMask = _capacity - 1; _data = NewArray(_capacity); _head = 0; diff --git a/Source/Engine/Threading/JobSystem.cpp b/Source/Engine/Threading/JobSystem.cpp index 0a29bdecf..08bbef410 100644 --- a/Source/Engine/Threading/JobSystem.cpp +++ b/Source/Engine/Threading/JobSystem.cpp @@ -20,6 +20,7 @@ // JOB_SYSTEM_USE_MUTEX=0, enqueue=300-700 cycles, dequeue=10-16 cycles // So using RingBuffer+Mutex+Signals is better than moodycamel::ConcurrentQueue +#define JOB_SYSTEM_ENABLED 1 #define JOB_SYSTEM_USE_MUTEX 1 #define JOB_SYSTEM_USE_STATS 0 @@ -32,6 +33,8 @@ #include "ConcurrentQueue.h" #endif +#if JOB_SYSTEM_ENABLED + class JobSystemService : public EngineService { public: @@ -201,11 +204,14 @@ int32 JobSystemThread::Run() return 0; } +#endif + int64 JobSystem::Dispatch(const Function& job, int32 jobCount) { PROFILE_CPU(); if (jobCount <= 0) return 0; +#if JOB_SYSTEM_ENABLED #if JOB_SYSTEM_USE_STATS const auto start = Platform::GetTimeCycles(); #endif @@ -234,15 +240,23 @@ int64 JobSystem::Dispatch(const Function& job, int32 jobCount) JobsSignal.NotifyAll(); return label; +#else + for (int32 i = 0; i < jobCount; i++) + job(i); + return 0; +#endif } void JobSystem::Wait() { +#if JOB_SYSTEM_ENABLED Wait(Platform::AtomicRead(&NextLabel)); +#endif } void JobSystem::Wait(int64 label) { +#if JOB_SYSTEM_ENABLED PROFILE_CPU(); // Early out @@ -262,4 +276,5 @@ void JobSystem::Wait(int64 label) LOG(Info, "Job average dequeue time: {0} cycles", DequeueSum / DequeueCount); DequeueSum = DequeueCount = 0; #endif +#endif } diff --git a/Source/Engine/Threading/ThreadLocal.h b/Source/Engine/Threading/ThreadLocal.h index b37b18661..c3395ff56 100644 --- a/Source/Engine/Threading/ThreadLocal.h +++ b/Source/Engine/Threading/ThreadLocal.h @@ -69,7 +69,8 @@ public: return result; } - void GetValues(Array& result) const + template + void GetValues(Array& result) const { result.EnsureCapacity(MaxThreads); for (int32 i = 0; i < MaxThreads; i++) @@ -134,7 +135,8 @@ public: } } - void GetNotNullValues(Array& result) const + template + void GetNotNullValues(Array& result) const { result.EnsureCapacity(MaxThreads); for (int32 i = 0; i < MaxThreads; i++) From 93cdb7ce8f982034b341f199635f45ab6f1df47e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 12 Jun 2021 19:35:37 +0200 Subject: [PATCH 145/198] Bump up build number --- Flax.flaxproj | 2 +- Source/FlaxEngine.Gen.cs | 4 ++-- Source/FlaxEngine.Gen.h | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index 6826bedfe..a7178c5cd 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -3,7 +3,7 @@ "Version": { "Major": 1, "Minor": 1, - "Build": 6219 + "Build": 6220 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.", diff --git a/Source/FlaxEngine.Gen.cs b/Source/FlaxEngine.Gen.cs index f7f535a40..5206c98f4 100644 --- a/Source/FlaxEngine.Gen.cs +++ b/Source/FlaxEngine.Gen.cs @@ -13,5 +13,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] [assembly: Guid("b8442186-4a70-7c85-704a-857c262d00f6")] -[assembly: AssemblyVersion("1.1.6219")] -[assembly: AssemblyFileVersion("1.1.6219")] +[assembly: AssemblyVersion("1.1.6220")] +[assembly: AssemblyFileVersion("1.1.6220")] diff --git a/Source/FlaxEngine.Gen.h b/Source/FlaxEngine.Gen.h index 46eefd64f..0e083b5ab 100644 --- a/Source/FlaxEngine.Gen.h +++ b/Source/FlaxEngine.Gen.h @@ -3,11 +3,11 @@ #pragma once #define FLAXENGINE_NAME "FlaxEngine" -#define FLAXENGINE_VERSION Version(1, 1, 6219) -#define FLAXENGINE_VERSION_TEXT "1.1.6219" +#define FLAXENGINE_VERSION Version(1, 1, 6220) +#define FLAXENGINE_VERSION_TEXT "1.1.6220" #define FLAXENGINE_VERSION_MAJOR 1 #define FLAXENGINE_VERSION_MINOR 1 -#define FLAXENGINE_VERSION_BUILD 6219 +#define FLAXENGINE_VERSION_BUILD 6220 #define FLAXENGINE_COMPANY "Flax" #define FLAXENGINE_COPYRIGHT "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved." From 25c00a0d55f2cd82c4e8370d6d6046116431e113 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 12 Jun 2021 20:10:16 +0200 Subject: [PATCH 146/198] Fix C# profiler events from other threads --- Source/Engine/Profiler/ProfilerCPU.cpp | 19 +++++++++++++++---- Source/Engine/Profiler/ProfilerCPU.h | 10 ++++++++++ .../Engine/Scripting/Scripting.Internal.cpp | 16 ++++------------ 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/Source/Engine/Profiler/ProfilerCPU.cpp b/Source/Engine/Profiler/ProfilerCPU.cpp index cab429d2c..e3b1caad4 100644 --- a/Source/Engine/Profiler/ProfilerCPU.cpp +++ b/Source/Engine/Profiler/ProfilerCPU.cpp @@ -122,6 +122,14 @@ void ProfilerCPU::Thread::EndEvent(int32 index) e.End = time; } +void ProfilerCPU::Thread::EndEvent() +{ + const double time = Platform::GetTimeSeconds() * 1000.0; + _depth--; + Event& e = Buffer.Get(Buffer.GetCount() - 1); + e.End = time; +} + bool ProfilerCPU::IsProfilingCurrentThread() { return Enabled && Thread::Current != nullptr; @@ -194,11 +202,14 @@ int32 ProfilerCPU::BeginEvent(const char* name) void ProfilerCPU::EndEvent(int32 index) { - if (!Enabled) - return; + if (Enabled && Thread::Current) + Thread::Current->EndEvent(index); +} - ASSERT(Thread::Current); - Thread::Current->EndEvent(index); +void ProfilerCPU::EndEvent() +{ + if (Enabled && Thread::Current) + Thread::Current->EndEvent(); } void ProfilerCPU::Dispose() diff --git a/Source/Engine/Profiler/ProfilerCPU.h b/Source/Engine/Profiler/ProfilerCPU.h index feccdd79b..2a48e6c15 100644 --- a/Source/Engine/Profiler/ProfilerCPU.h +++ b/Source/Engine/Profiler/ProfilerCPU.h @@ -289,6 +289,11 @@ public: /// /// The event index returned by the BeginEvent method. void EndEvent(int32 index); + + /// + /// Ends the last event running on a this thread. + /// + void EndEvent(); }; public: @@ -341,6 +346,11 @@ public: /// The event index returned by the BeginEvent method. static void EndEvent(int32 index); + /// + /// Ends the last event. + /// + static void EndEvent(); + /// /// Releases resources. Calls to the profiling API after Dispose are not valid. /// diff --git a/Source/Engine/Scripting/Scripting.Internal.cpp b/Source/Engine/Scripting/Scripting.Internal.cpp index 79e578401..781dc161e 100644 --- a/Source/Engine/Scripting/Scripting.Internal.cpp +++ b/Source/Engine/Scripting/Scripting.Internal.cpp @@ -14,29 +14,21 @@ namespace ProfilerInternal { - /// - /// The managed events IDs. - /// - Array ManagedEvents; - - /// - /// The managed events IDs for GPU profiling. - /// +#if COMPILE_WITH_PROFILER Array ManagedEventsGPU; +#endif void BeginEvent(MonoString* nameObj) { #if COMPILE_WITH_PROFILER - const auto index = ProfilerCPU::BeginEvent((const Char*)mono_string_chars(nameObj)); - ManagedEvents.Push(index); + ProfilerCPU::BeginEvent((const Char*)mono_string_chars(nameObj)); #endif } void EndEvent() { #if COMPILE_WITH_PROFILER - const auto index = ManagedEvents.Pop(); - ProfilerCPU::EndEvent(index); + ProfilerCPU::EndEvent(); #endif } From d7e7dcc823bb1547396fe79700c0d303afeab8eb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 12 Jun 2021 22:43:37 +0200 Subject: [PATCH 147/198] Add Task Graph --- Source/Engine/Core/Collections/Array.h | 13 +-- Source/Engine/Threading/TaskGraph.cpp | 118 +++++++++++++++++++++++++ Source/Engine/Threading/TaskGraph.h | 95 ++++++++++++++++++++ 3 files changed, 215 insertions(+), 11 deletions(-) create mode 100644 Source/Engine/Threading/TaskGraph.cpp create mode 100644 Source/Engine/Threading/TaskGraph.h diff --git a/Source/Engine/Core/Collections/Array.h b/Source/Engine/Core/Collections/Array.h index 9b8c41245..5069c0ac2 100644 --- a/Source/Engine/Core/Collections/Array.h +++ b/Source/Engine/Core/Collections/Array.h @@ -493,17 +493,8 @@ public: /// Adds the other collection to the collection. /// /// The other collection to add. - FORCE_INLINE void Add(const Array& other) - { - Add(other.Get(), other.Count()); - } - - /// - /// Adds the other collection to the collection. - /// - /// The other collection to add. - template - FORCE_INLINE void Add(const Array& other) + template + FORCE_INLINE void Add(const Array& other) { Add(other.Get(), other.Count()); } diff --git a/Source/Engine/Threading/TaskGraph.cpp b/Source/Engine/Threading/TaskGraph.cpp new file mode 100644 index 000000000..aa86a94a9 --- /dev/null +++ b/Source/Engine/Threading/TaskGraph.cpp @@ -0,0 +1,118 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "TaskGraph.h" +#include "JobSystem.h" +#include "Engine/Core/Collections/Sorting.h" +#include "Engine/Profiler/ProfilerCPU.h" + +namespace +{ + bool SortTaskGraphSystem(TaskGraphSystem* const& a, TaskGraphSystem* const& b) + { + return b->Order < a->Order; + }; +} + +TaskGraphSystem::TaskGraphSystem(const SpawnParams& params) + : PersistentScriptingObject(params) +{ +} + +void TaskGraphSystem::AddDependency(TaskGraphSystem* system) +{ + _dependencies.Add(system); +} + +void TaskGraphSystem::PreExecute(TaskGraph* graph) +{ +} + +void TaskGraphSystem::Execute(TaskGraph* graph) +{ +} + +void TaskGraphSystem::PostExecute(TaskGraph* graph) +{ +} + +TaskGraph::TaskGraph(const SpawnParams& params) + : PersistentScriptingObject(params) +{ +} + +const Array>& TaskGraph::GetSystems() const +{ + return _systems; +} + +void TaskGraph::AddSystem(TaskGraphSystem* system) +{ + _systems.Add(system); +} + +void TaskGraph::RemoveSystem(TaskGraphSystem* system) +{ + _systems.Remove(system); +} + +void TaskGraph::Execute() +{ + PROFILE_CPU(); + + for (auto system : _systems) + system->PreExecute(this); + + _queue.Clear(); + _remaining.Clear(); + _remaining.Add(_systems); + + while (_remaining.HasItems()) + { + // Find systems without dependencies or with already executed dependencies + for (int32 i = _remaining.Count() - 1; i >= 0; i--) + { + auto e = _remaining[i]; + bool hasReadyDependencies = true; + for (auto d : e->_dependencies) + { + if (_remaining.Contains(d)) + { + hasReadyDependencies = false; + break; + } + } + if (hasReadyDependencies) + { + _queue.Add(e); + _remaining.RemoveAt(i); + } + } + + // End if no systems left + if (_queue.IsEmpty()) + break; + + // Execute in order + Sorting::QuickSort(_queue.Get(), _queue.Count(), &SortTaskGraphSystem); + _currentLabel = 0; + for (int32 i = 0; i < _queue.Count(); i++) + { + _currentSystem = _queue[i]; + _currentSystem->Execute(this); + } + _currentSystem = nullptr; + _queue.Clear(); + + // Wait for async jobs to finish + JobSystem::Wait(_currentLabel); + } + + for (auto system : _systems) + system->PostExecute(this); +} + +void TaskGraph::DispatchJob(const Function& job, int32 jobCount) +{ + ASSERT(_currentSystem); + _currentLabel = JobSystem::Dispatch(job, jobCount); +} diff --git a/Source/Engine/Threading/TaskGraph.h b/Source/Engine/Threading/TaskGraph.h new file mode 100644 index 000000000..7268e644c --- /dev/null +++ b/Source/Engine/Threading/TaskGraph.h @@ -0,0 +1,95 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Scripting/ScriptingObject.h" +#include "Engine/Core/Collections/Array.h" + +class TaskGraph; + +/// +/// System that can generate work into Task Graph for asynchronous execution. +/// +API_CLASS(Abstract) class FLAXENGINE_API TaskGraphSystem : public PersistentScriptingObject +{ +DECLARE_SCRIPTING_TYPE(TaskGraphSystem); + friend TaskGraph; +private: + Array> _dependencies; + +public: + /// + /// The execution order of the system (systems with higher order are executed earlier). + /// + API_FIELD() int32 Order = 0; + +public: + /// + /// Adds the dependency on the system execution. Before this system can be executed the given dependant system has to be executed first. + /// + /// The system to depend on. + API_FUNCTION() void AddDependency(TaskGraphSystem* system); + + /// + /// Called before executing any systems of the graph. Can be used to initialize data (synchronous). + /// + /// The graph executing the system. + API_FUNCTION() virtual void PreExecute(TaskGraph* graph); + + /// + /// Executes the system logic and schedules the asynchronous work. + /// + /// The graph executing the system. + API_FUNCTION() virtual void Execute(TaskGraph* graph); + + /// + /// Called after executing all systems of the graph. Can be used to cleanup data (synchronous). + /// + /// The graph executing the system. + API_FUNCTION() virtual void PostExecute(TaskGraph* graph); +}; + +/// +/// Graph-based asynchronous tasks scheduler for high-performance computing and processing. +/// +API_CLASS() class FLAXENGINE_API TaskGraph : public PersistentScriptingObject +{ +DECLARE_SCRIPTING_TYPE(TaskGraph); +private: + Array> _systems; + Array> _remaining; + Array> _queue; + TaskGraphSystem* _currentSystem = nullptr; + int64 _currentLabel = 0; + +public: + /// + /// Gets the list of systems. + /// + API_PROPERTY() const Array>& GetSystems() const; + + /// + /// Adds the system to the graph for the execution. + /// + /// The system to add. + API_FUNCTION() void AddSystem(TaskGraphSystem* system); + + /// + /// Removes the system from the graph. + /// + /// The system to add. + API_FUNCTION() void RemoveSystem(TaskGraphSystem* system); + + /// + /// Schedules the asynchronous systems execution including ordering and dependencies handling. + /// + API_FUNCTION() void Execute(); + + /// + /// Dispatches the job for the execution. + /// + /// Call only from system's Execute method to properly schedule job. + /// The job. Argument is an index of the job execution. + /// The job executions count. + API_FUNCTION() void DispatchJob(const Function& job, int32 jobCount = 1); +}; From 360f498e47c074cf68df4340e31cb813c071ffad Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 12 Jun 2021 22:44:16 +0200 Subject: [PATCH 148/198] Add `Engine::UpdateGraph` for async engine/game update --- Source/Engine/Engine/Engine.cpp | 5 +++++ Source/Engine/Engine/Engine.h | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index a5fd460fb..bfa2338c6 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -27,6 +27,7 @@ #include "Engine/Graphics/RenderTargetPool.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Profiler/Profiler.h" +#include "Engine/Threading/TaskGraph.h" #if USE_EDITOR #include "Editor/Editor.h" #include "Editor/ProjectInfo.h" @@ -62,6 +63,7 @@ bool Engine::HasFocus = false; uint64 Engine::FrameCount = 0; Action Engine::FixedUpdate; Action Engine::Update; +TaskGraph* Engine::UpdateGraph = nullptr; Action Engine::LateUpdate; Action Engine::Draw; Action Engine::Pause; @@ -122,6 +124,7 @@ int32 Engine::Main(const Char* cmdLine) #endif // Initialize engine + UpdateGraph = New(); EngineService::OnInit(); if (Application::Init()) return -10; @@ -289,6 +292,7 @@ void Engine::OnUpdate() // Call event Update(); + UpdateGraph->Execute(); // Update services EngineService::OnUpdate(); @@ -436,6 +440,7 @@ void Engine::OnExit() // Unload Engine services EngineService::OnDispose(); + Delete(UpdateGraph); LOG_FLUSH(); diff --git a/Source/Engine/Engine/Engine.h b/Source/Engine/Engine/Engine.h index 13f3e1fe8..51860e5a2 100644 --- a/Source/Engine/Engine/Engine.h +++ b/Source/Engine/Engine/Engine.h @@ -6,6 +6,7 @@ #include "Engine/Core/Types/DateTime.h" #include "Engine/Scripting/ScriptingType.h" +class TaskGraph; class JsonAsset; /// @@ -43,6 +44,11 @@ public: /// static Action Update; + /// + /// Task graph for engine update. + /// + API_FIELD(ReadOnly) static TaskGraph* UpdateGraph; + /// /// Event called after engine update. /// From 4a92850d9abbafc04562fa391c3cfb280f630f66 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sat, 12 Jun 2021 23:18:04 +0200 Subject: [PATCH 149/198] Add async animations updating via Task Graph --- Source/Engine/Animations/Animations.cpp | 147 ++++++++++++------- Source/Engine/Animations/Animations.h | 6 + Source/Engine/Level/Actors/AnimatedModel.cpp | 38 +++-- Source/Engine/Level/Actors/AnimatedModel.h | 4 +- 4 files changed, 127 insertions(+), 68 deletions(-) diff --git a/Source/Engine/Animations/Animations.cpp b/Source/Engine/Animations/Animations.cpp index 037c87a5f..179e710e4 100644 --- a/Source/Engine/Animations/Animations.cpp +++ b/Source/Engine/Animations/Animations.cpp @@ -6,8 +6,7 @@ #include "Engine/Level/Actors/AnimatedModel.h" #include "Engine/Engine/Time.h" #include "Engine/Engine/EngineService.h" - -Array UpdateList; +#include "Engine/Threading/TaskGraph.h" class AnimationsService : public EngineService { @@ -18,70 +17,110 @@ public: { } - void Update() override; + bool Init() override; void Dispose() override; }; +class AnimationsSystem : public TaskGraphSystem +{ +public: + float DeltaTime, UnscaledDeltaTime, Time, UnscaledTime; + void Job(int32 index); + void Execute(TaskGraph* graph) override; + void PostExecute(TaskGraph* graph) override; +}; + AnimationsService AnimationManagerInstance; +Array UpdateList; +TaskGraphSystem* Animations::System = nullptr; Delegate Animations::DebugFlow; -void AnimationsService::Update() +bool AnimationsService::Init() { - PROFILE_CPU_NAMED("Animations"); - - // TODO: implement the thread jobs pipeline to run set of tasks at once (use it for multi-threaded rendering and animations evaluation) - - const auto& tickData = Time::Update; - const float deltaTime = tickData.DeltaTime.GetTotalSeconds(); - const float unscaledDeltaTime = tickData.UnscaledDeltaTime.GetTotalSeconds(); - const float time = tickData.Time.GetTotalSeconds(); - const float unscaledTime = tickData.UnscaledTime.GetTotalSeconds(); - - for (int32 i = 0; i < UpdateList.Count(); i++) - { - auto animatedModel = UpdateList[i]; - if (animatedModel->SkinnedModel == nullptr || !animatedModel->SkinnedModel->IsLoaded()) - continue; - - // Prepare skinning data - animatedModel->SetupSkinningData(); - - // Update the animation graph and the skinning - auto graph = animatedModel->AnimationGraph.Get(); - if (graph && graph->IsLoaded() && graph->Graph.CanUseWithSkeleton(animatedModel->SkinnedModel) -#if USE_EDITOR - && graph->Graph.Parameters.Count() == animatedModel->GraphInstance.Parameters.Count() // It may happen in editor so just add safe check to prevent any crashes -#endif - ) - { -#if USE_EDITOR - // Lock in editor only (more reloads during asset live editing) - ScopeLock lock(animatedModel->AnimationGraph->Locker); -#endif - - // Animation delta time can be based on a time since last update or the current delta - float dt = animatedModel->UseTimeScale ? deltaTime : unscaledDeltaTime; - float t = animatedModel->UseTimeScale ? time : unscaledTime; - const float lastUpdateTime = animatedModel->GraphInstance.LastUpdateTime; - if (lastUpdateTime > 0 && t > lastUpdateTime) - { - dt = t - lastUpdateTime; - } - animatedModel->GraphInstance.LastUpdateTime = t; - - // Evaluate animated nodes pose - graph->GraphExecutor.Update(animatedModel->GraphInstance, dt); - - // Update gameplay - animatedModel->OnAnimationUpdated(); - } - } - UpdateList.Clear(); + Animations::System = New(); + Engine::UpdateGraph->AddSystem(Animations::System); + return false; } void AnimationsService::Dispose() { UpdateList.Resize(0); + SAFE_DELETE(Animations::System); +} + +void AnimationsSystem::Job(int32 index) +{ + PROFILE_CPU_NAMED("Animations.Job"); + auto animatedModel = UpdateList[index]; + auto skinnedModel = animatedModel->SkinnedModel.Get(); + auto graph = animatedModel->AnimationGraph.Get(); + if (graph && graph->IsLoaded() && graph->Graph.CanUseWithSkeleton(skinnedModel) +#if USE_EDITOR + && graph->Graph.Parameters.Count() == animatedModel->GraphInstance.Parameters.Count() // It may happen in editor so just add safe check to prevent any crashes +#endif + ) + { + // Prepare skinning data + animatedModel->SetupSkinningData(); + + // Animation delta time can be based on a time since last update or the current delta + float dt = animatedModel->UseTimeScale ? DeltaTime : UnscaledDeltaTime; + float t = animatedModel->UseTimeScale ? Time : UnscaledTime; + const float lastUpdateTime = animatedModel->GraphInstance.LastUpdateTime; + if (lastUpdateTime > 0 && t > lastUpdateTime) + { + dt = t - lastUpdateTime; + } + animatedModel->GraphInstance.LastUpdateTime = t; + + // Evaluate animated nodes pose + graph->GraphExecutor.Update(animatedModel->GraphInstance, dt); + + // Update gameplay + animatedModel->OnAnimationUpdated_Async(); + } +} + +void AnimationsSystem::Execute(TaskGraph* graph) +{ + if (UpdateList.Count() == 0) + return; + + // Setup data for async update + const auto& tickData = Time::Update; + DeltaTime = tickData.DeltaTime.GetTotalSeconds(); + UnscaledDeltaTime = tickData.UnscaledDeltaTime.GetTotalSeconds(); + Time = tickData.Time.GetTotalSeconds(); + UnscaledTime = tickData.UnscaledTime.GetTotalSeconds(); + + // Schedule work to update all animated models in async + Function job; + job.Bind(this); + graph->DispatchJob(job, UpdateList.Count()); +} + +void AnimationsSystem::PostExecute(TaskGraph* graph) +{ + PROFILE_CPU_NAMED("Animations.PostExecute"); + + // Update gameplay + for (int32 index = 0; index < UpdateList.Count(); index++) + { + auto animatedModel = UpdateList[index]; + auto skinnedModel = animatedModel->SkinnedModel.Get(); + auto animGraph = animatedModel->AnimationGraph.Get(); + if (animGraph && animGraph->IsLoaded() && animGraph->Graph.CanUseWithSkeleton(skinnedModel) +#if USE_EDITOR + && animGraph->Graph.Parameters.Count() == animatedModel->GraphInstance.Parameters.Count() // It may happen in editor so just add safe check to prevent any crashes +#endif + ) + { + animatedModel->OnAnimationUpdated_Sync(); + } + } + + // Cleanup + UpdateList.Clear(); } void Animations::AddToUpdate(AnimatedModel* obj) diff --git a/Source/Engine/Animations/Animations.h b/Source/Engine/Animations/Animations.h index 8b5b79c67..014104eac 100644 --- a/Source/Engine/Animations/Animations.h +++ b/Source/Engine/Animations/Animations.h @@ -5,6 +5,7 @@ #include "Engine/Scripting/ScriptingType.h" #include "Engine/Core/Delegate.h" +class TaskGraphSystem; class AnimatedModel; class Asset; @@ -15,6 +16,11 @@ API_CLASS(Static) class FLAXENGINE_API Animations { DECLARE_SCRIPTING_TYPE_NO_SPAWN(Content); + /// + /// The system for Animations update. + /// + API_FIELD(ReadOnly) static TaskGraphSystem* System; + #if USE_EDITOR // Custom event that is called every time the Anim Graph signal flows over the graph (including the data connections). Can be used to read and visualize the animation blending logic. Args are: anim graph asset, animated object, node id, box id API_EVENT() static Delegate DebugFlow; diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp index 701df3d6e..45bc701c6 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.cpp +++ b/Source/Engine/Level/Actors/AnimatedModel.cpp @@ -113,16 +113,6 @@ void AnimatedModel::PreInitSkinningData() UpdateSockets(); } -void AnimatedModel::UpdateSockets() -{ - for (int32 i = 0; i < Children.Count(); i++) - { - auto socket = dynamic_cast(Children[i]); - if (socket) - socket->UpdateTransformation(); - } -} - void AnimatedModel::GetCurrentPose(Array& nodesTransformation, bool worldSpace) const { nodesTransformation = GraphInstance.NodesPose; @@ -451,9 +441,19 @@ void AnimatedModel::UpdateBounds() BoundingSphere::FromBox(_box, _sphere); } -void AnimatedModel::OnAnimationUpdated() +void AnimatedModel::UpdateSockets() { - ANIM_GRAPH_PROFILE_EVENT("OnAnimationUpdated"); + for (int32 i = 0; i < Children.Count(); i++) + { + auto socket = dynamic_cast(Children[i]); + if (socket) + socket->UpdateTransformation(); + } +} + +void AnimatedModel::OnAnimationUpdated_Async() +{ + // Update asynchronous stuff auto& skeleton = SkinnedModel->Skeleton; // Copy pose from the master @@ -482,12 +482,24 @@ void AnimatedModel::OnAnimationUpdated() } UpdateBounds(); + _blendShapes.Update(SkinnedModel.Get()); +} + +void AnimatedModel::OnAnimationUpdated_Sync() +{ + // Update synchronous stuff UpdateSockets(); ApplyRootMotion(GraphInstance.RootMotion); - _blendShapes.Update(SkinnedModel.Get()); AnimationUpdated(); } +void AnimatedModel::OnAnimationUpdated() +{ + ANIM_GRAPH_PROFILE_EVENT("OnAnimationUpdated"); + OnAnimationUpdated_Async(); + OnAnimationUpdated_Sync(); +} + void AnimatedModel::OnSkinnedModelChanged() { Entries.Release(); diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h index 8c9023d23..771833f2c 100644 --- a/Source/Engine/Level/Actors/AnimatedModel.h +++ b/Source/Engine/Level/Actors/AnimatedModel.h @@ -15,7 +15,7 @@ API_CLASS() class FLAXENGINE_API AnimatedModel : public ModelInstanceActor { DECLARE_SCENE_OBJECT(AnimatedModel); - friend class AnimationsService; + friend class AnimationsSystem; public: /// @@ -306,6 +306,8 @@ private: void UpdateLocalBounds(); void UpdateBounds(); void UpdateSockets(); + void OnAnimationUpdated_Async(); + void OnAnimationUpdated_Sync(); void OnAnimationUpdated(); void OnSkinnedModelChanged(); From eb6e3a7e8eba962223ebddb960749fe87ff1e009 Mon Sep 17 00:00:00 2001 From: W2Wizard <63303990+W2Wizard@users.noreply.github.com> Date: Sun, 13 Jun 2021 14:49:10 +0200 Subject: [PATCH 150/198] Replace clickable images with actual buttons With the new icons the clickable images for adding functions for the Visject surface got too small and unreadable. Instead using more visible buttons should counteract this. --- Source/Editor/EditorIcons.cs | 8 ++ .../Windows/Assets/VisualScriptWindow.cs | 77 ++++++++----------- 2 files changed, 38 insertions(+), 47 deletions(-) diff --git a/Source/Editor/EditorIcons.cs b/Source/Editor/EditorIcons.cs index d4b89b8d3..ff3164dcc 100644 --- a/Source/Editor/EditorIcons.cs +++ b/Source/Editor/EditorIcons.cs @@ -46,6 +46,10 @@ namespace FlaxEditor public SpriteHandle Down32; public SpriteHandle FolderClosed32; public SpriteHandle FolderOpen32; + public SpriteHandle FolderFill32; + public SpriteHandle Info32; + public SpriteHandle Warning32; + public SpriteHandle Error32; // Visject public SpriteHandle VisjectBoxOpen32; @@ -128,6 +132,10 @@ namespace FlaxEditor public SpriteHandle AndroidIcon128; public SpriteHandle PS4Icon128; public SpriteHandle FlaxLogo128; + public SpriteHandle SwitchIcon128; + public SpriteHandle SwitchSettings128; + public SpriteHandle Json128; + public SpriteHandle LocalizationSettings128; internal void LoadIcons() { diff --git a/Source/Editor/Windows/Assets/VisualScriptWindow.cs b/Source/Editor/Windows/Assets/VisualScriptWindow.cs index b9ddafa87..545f15867 100644 --- a/Source/Editor/Windows/Assets/VisualScriptWindow.cs +++ b/Source/Editor/Windows/Assets/VisualScriptWindow.cs @@ -167,6 +167,8 @@ namespace FlaxEditor.Windows.Assets /// public override DisplayStyle Style => DisplayStyle.InlineIntoParent; + private Control _overrideButton; + /// public override void Initialize(LayoutElementsContainer layout) { @@ -178,6 +180,21 @@ namespace FlaxEditor.Windows.Assets var group = layout.Group("Functions"); var nodes = window.VisjectSurface.Nodes; + var grid = group.CustomContainer(); + var gridControl = grid.CustomControl; + gridControl.ClipChildren = false; + gridControl.Height = Button.DefaultHeight; + gridControl.SlotsHorizontally = 2; + gridControl.SlotsVertically = 1; + + var addOverride = grid.Button("Add Override"); + addOverride.Button.Clicked += OnOverrideMethodClicked; + // TODO: Add sender arg to button clicked action? + _overrideButton = addOverride.Control; + + var addFuncction = grid.Button("Add Function"); + addFuncction.Button.Clicked += OnAddNewFunctionClicked; + // List of functions in the graph for (int i = 0; i < nodes.Count; i++) { @@ -192,60 +209,20 @@ namespace FlaxEditor.Windows.Assets } else if (node is Surface.Archetypes.Function.MethodOverrideNode overrideNode) { - var label = group.ClickableLabel(overrideNode.Title + " (override)").CustomControl; + var label = group.ClickableLabel($"{overrideNode.Title} (override)").CustomControl; label.TextColorHighlighted = Color.FromBgra(0xFFA0A0A0); label.TooltipText = overrideNode.TooltipText; label.DoubleClick += () => ((VisualScriptWindow)Values[0]).Surface.FocusNode(overrideNode); label.RightClick += () => ShowContextMenu(overrideNode, label); } } - - // New function button - const float groupPanelButtonSize = 14; - var addNewFunction = new Image - { - TooltipText = "Add new function", - AutoFocus = true, - AnchorPreset = AnchorPresets.TopRight, - Parent = group.Panel, - Bounds = new Rectangle(group.Panel.Width - groupPanelButtonSize, 0, groupPanelButtonSize, groupPanelButtonSize), - IsScrollable = false, - Color = FlaxEngine.GUI.Style.Current.ForegroundGrey, - Margin = new Margin(1), - Brush = new SpriteBrush(Editor.Instance.Icons.Add64), - }; - addNewFunction.Clicked += OnAddNewFunctionClicked; - - // Override method button - var overrideMethod = new Image - { - TooltipText = "Override method", - AutoFocus = true, - AnchorPreset = AnchorPresets.TopRight, - Parent = group.Panel, - Bounds = new Rectangle(group.Panel.Width - groupPanelButtonSize * 2, 0, groupPanelButtonSize, groupPanelButtonSize), - IsScrollable = false, - Color = FlaxEngine.GUI.Style.Current.ForegroundGrey, - Margin = new Margin(1), - Brush = new SpriteBrush(Editor.Instance.Icons.Import64), - }; - overrideMethod.Clicked += OnOverrideMethodClicked; - } - - private void OnAddNewFunctionClicked(Image image, MouseButton button) - { - if (button != MouseButton.Left) - return; - var surface = ((VisualScriptWindow)Values[0]).Surface; - var surfaceBounds = surface.AllNodesBounds; - surface.ShowArea(new Rectangle(surfaceBounds.BottomLeft, new Vector2(200, 150)).MakeExpanded(400.0f)); - var node = surface.Context.SpawnNode(16, 6, surfaceBounds.BottomLeft + new Vector2(0, 50)); - surface.Select(node); } private void ShowContextMenu(SurfaceNode node, ClickableLabel label) { + // TODO: Execute only on "Edit Signature...", this makes the "Show" button useless ((VisualScriptWindow)Values[0]).Surface.FocusNode(node); + var cm = new ContextMenu(); cm.AddButton("Show", () => ((VisualScriptWindow)Values[0]).Surface.FocusNode(node)).Icon = Editor.Instance.Icons.Search12; cm.AddButton("Delete", () => ((VisualScriptWindow)Values[0]).Surface.Delete(node)).Icon = Editor.Instance.Icons.Cross12; @@ -253,11 +230,17 @@ namespace FlaxEditor.Windows.Assets cm.Show(label, new Vector2(0, label.Height)); } - private void OnOverrideMethodClicked(Image image, MouseButton button) + private void OnAddNewFunctionClicked() { - if (button != MouseButton.Left) - return; + var surface = ((VisualScriptWindow)Values[0]).Surface; + var surfaceBounds = surface.AllNodesBounds; + surface.ShowArea(new Rectangle(surfaceBounds.BottomLeft, new Vector2(200, 150)).MakeExpanded(400.0f)); + var node = surface.Context.SpawnNode(16, 6, surfaceBounds.BottomLeft + new Vector2(0, 50)); + surface.Select(node); + } + private void OnOverrideMethodClicked() + { var cm = new ContextMenu(); var window = (VisualScriptWindow)Values[0]; var scriptMeta = window.Asset.Meta; @@ -310,7 +293,7 @@ namespace FlaxEditor.Windows.Assets { cm.AddButton("Nothing to override"); } - cm.Show(image, new Vector2(0, image.Height)); + cm.Show(_overrideButton, new Vector2(0, _overrideButton.Height)); } } From 96089d5396f6f259ec93ae5c1b94a2bf7bfcc03a Mon Sep 17 00:00:00 2001 From: W2Wizard <63303990+W2Wizard@users.noreply.github.com> Date: Sun, 13 Jun 2021 16:20:23 +0200 Subject: [PATCH 151/198] Fix timeline track icons --- Source/Editor/EditorIcons.cs | 4 +++- Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs | 4 ++-- Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs | 2 +- Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs | 6 +++--- Source/Editor/GUI/Timeline/Tracks/FolderTrack.cs | 2 +- Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs | 12 ++++++------ Source/Editor/Windows/ContentWindow.cs | 2 +- 7 files changed, 17 insertions(+), 15 deletions(-) diff --git a/Source/Editor/EditorIcons.cs b/Source/Editor/EditorIcons.cs index ff3164dcc..da92a159f 100644 --- a/Source/Editor/EditorIcons.cs +++ b/Source/Editor/EditorIcons.cs @@ -46,7 +46,9 @@ namespace FlaxEditor public SpriteHandle Down32; public SpriteHandle FolderClosed32; public SpriteHandle FolderOpen32; - public SpriteHandle FolderFill32; + public SpriteHandle Folder32; + public SpriteHandle CameraFill32; + public SpriteHandle Search32; public SpriteHandle Info32; public SpriteHandle Warning32; public SpriteHandle Error32; diff --git a/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs b/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs index 580d1f266..fc096ca56 100644 --- a/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs @@ -70,7 +70,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks : base(ref options) { // Select Actor button - const float buttonSize = 14; + const float buttonSize = 20; var icons = Editor.Instance.Icons; _selectActor = new Image { @@ -80,7 +80,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks IsScrollable = false, Color = Style.Current.ForegroundGrey, Margin = new Margin(1), - Brush = new SpriteBrush(icons.Search12), + Brush = new SpriteBrush(icons.Search32), Offsets = new Margin(-buttonSize - 2 + _addButton.Offsets.Left, buttonSize, buttonSize * -0.5f, buttonSize), Parent = this, }; diff --git a/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs b/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs index 98cb3979f..1df00c1b0 100644 --- a/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs @@ -312,7 +312,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks Curve.UnlockChildrenRecursive(); // Navigation buttons - const float buttonSize = 14; + const float buttonSize = 20; var icons = Editor.Instance.Icons; var rightKey = new Image { diff --git a/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs b/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs index 359fa0126..f6f80ab22 100644 --- a/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs @@ -682,10 +682,10 @@ namespace FlaxEditor.GUI.Timeline.Tracks public CameraCutTrack(ref TrackCreateOptions options) : base(ref options) { - Height = CameraCutThumbnailRenderer.Height + 4 + 4; + Height = CameraCutThumbnailRenderer.Height + 8; // Pilot Camera button - const float buttonSize = 14; + const float buttonSize = 20; var icons = Editor.Instance.Icons; _pilotCamera = new Image { @@ -695,7 +695,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks IsScrollable = false, Color = Style.Current.ForegroundGrey, Margin = new Margin(1), - Brush = new SpriteBrush(icons.Camera64), + Brush = new SpriteBrush(icons.CameraFill32), Offsets = new Margin(-buttonSize - 2 + _selectActor.Offsets.Left, buttonSize, buttonSize * -0.5f, buttonSize), Parent = this, }; diff --git a/Source/Editor/GUI/Timeline/Tracks/FolderTrack.cs b/Source/Editor/GUI/Timeline/Tracks/FolderTrack.cs index 5e251b6d2..c01719b97 100644 --- a/Source/Editor/GUI/Timeline/Tracks/FolderTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/FolderTrack.cs @@ -24,7 +24,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks { TypeId = 1, Name = "Folder", - Icon = Editor.Instance.Icons.Folder64, + Icon = Editor.Instance.Icons.Folder32, Create = options => new FolderTrack(ref options), Load = LoadTrack, Save = SaveTrack, diff --git a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs index 302570fae..d6fa64a98 100644 --- a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs @@ -118,7 +118,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks if (useNavigationButtons) { // Navigation buttons - const float buttonSize = 14; + const float buttonSize = 20; var icons = Editor.Instance.Icons; _rightKey = new Image { @@ -128,7 +128,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks IsScrollable = false, Color = Style.Current.ForegroundGrey, Margin = new Margin(1), - Brush = new SpriteBrush(icons.Right64), + Brush = new SpriteBrush(icons.Right32), Offsets = new Margin(-buttonSize - 2 + uiLeft, buttonSize, buttonSize * -0.5f, buttonSize), Parent = this, }; @@ -138,9 +138,9 @@ namespace FlaxEditor.GUI.Timeline.Tracks AutoFocus = true, AnchorPreset = AnchorPresets.MiddleRight, IsScrollable = false, - Color = Style.Current.Foreground, + Color = Style.Current.ForegroundGrey, Margin = new Margin(3), - Brush = new SpriteBrush(icons.Add64), + Brush = new SpriteBrush(icons.Add32), Offsets = new Margin(-buttonSize - 2 + _rightKey.Offsets.Left, buttonSize, buttonSize * -0.5f, buttonSize), Parent = this, }; @@ -150,9 +150,9 @@ namespace FlaxEditor.GUI.Timeline.Tracks AutoFocus = true, AnchorPreset = AnchorPresets.MiddleRight, IsScrollable = false, - Color = Style.Current.Foreground, + Color = Style.Current.ForegroundGrey, Margin = new Margin(1), - Brush = new SpriteBrush(icons.Left64), + Brush = new SpriteBrush(icons.Left32), Offsets = new Margin(-buttonSize - 2 + _addKey.Offsets.Left, buttonSize, buttonSize * -0.5f, buttonSize), Parent = this, }; diff --git a/Source/Editor/Windows/ContentWindow.cs b/Source/Editor/Windows/ContentWindow.cs index 88a976d2b..8a4db3789 100644 --- a/Source/Editor/Windows/ContentWindow.cs +++ b/Source/Editor/Windows/ContentWindow.cs @@ -66,7 +66,7 @@ namespace FlaxEditor.Windows : base(editor, true, ScrollBars.None) { Title = "Content"; - Icon = editor.Icons.Folder64; + Icon = editor.Icons.Folder32; // Content database events editor.ContentDatabase.WorkspaceModified += () => _isWorkspaceDirty = true; From 7e3b64b328a8f449085c3d5cde80c85b5a949ad4 Mon Sep 17 00:00:00 2001 From: W2Wizard <63303990+W2Wizard@users.noreply.github.com> Date: Sun, 13 Jun 2021 16:46:08 +0200 Subject: [PATCH 152/198] Adjust track icon size Go down from 20 to 18 instead. --- Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs | 2 +- Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs | 9 +++++---- Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs | 2 +- Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs | 9 +++++---- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs b/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs index fc096ca56..22dae35e0 100644 --- a/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/ActorTrack.cs @@ -70,7 +70,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks : base(ref options) { // Select Actor button - const float buttonSize = 20; + const float buttonSize = 18; var icons = Editor.Instance.Icons; _selectActor = new Image { diff --git a/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs b/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs index 1df00c1b0..f9b7fa3f7 100644 --- a/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/AudioTrack.cs @@ -312,7 +312,8 @@ namespace FlaxEditor.GUI.Timeline.Tracks Curve.UnlockChildrenRecursive(); // Navigation buttons - const float buttonSize = 20; + const float keySize = 18; + const float addSize = 20; var icons = Editor.Instance.Icons; var rightKey = new Image { @@ -323,7 +324,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks Color = Style.Current.ForegroundGrey, Margin = new Margin(1), Brush = new SpriteBrush(icons.Right32), - Offsets = new Margin(-buttonSize - 2 + _muteCheckbox.Offsets.Left, buttonSize, buttonSize * -0.5f, buttonSize), + Offsets = new Margin(-keySize - 2 + _muteCheckbox.Offsets.Left, keySize, keySize * -0.5f, keySize), Parent = this, }; rightKey.Clicked += OnRightKeyClicked; @@ -336,7 +337,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks Color = Style.Current.ForegroundGrey, Margin = new Margin(3), Brush = new SpriteBrush(icons.Add32), - Offsets = new Margin(-buttonSize - 2 + rightKey.Offsets.Left, buttonSize, buttonSize * -0.5f, buttonSize), + Offsets = new Margin(-addSize - 2 + rightKey.Offsets.Left, addSize, addSize * -0.5f, addSize), Parent = this, }; addKey.Clicked += OnAddKeyClicked; @@ -349,7 +350,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks Color = Style.Current.ForegroundGrey, Margin = new Margin(1), Brush = new SpriteBrush(icons.Left32), - Offsets = new Margin(-buttonSize - 2 + addKey.Offsets.Left, buttonSize, buttonSize * -0.5f, buttonSize), + Offsets = new Margin(-keySize - 2 + addKey.Offsets.Left, keySize, keySize * -0.5f, keySize), Parent = this, }; leftKey.Clicked += OnLeftKeyClicked; diff --git a/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs b/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs index f6f80ab22..05f0c42ae 100644 --- a/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/CameraCutTrack.cs @@ -685,7 +685,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks Height = CameraCutThumbnailRenderer.Height + 8; // Pilot Camera button - const float buttonSize = 20; + const float buttonSize = 18; var icons = Editor.Instance.Icons; _pilotCamera = new Image { diff --git a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs index d6fa64a98..64beb855d 100644 --- a/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs +++ b/Source/Editor/GUI/Timeline/Tracks/MemberTrack.cs @@ -118,7 +118,8 @@ namespace FlaxEditor.GUI.Timeline.Tracks if (useNavigationButtons) { // Navigation buttons - const float buttonSize = 20; + const float keySize = 18; + const float addSize = 20; var icons = Editor.Instance.Icons; _rightKey = new Image { @@ -129,7 +130,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks Color = Style.Current.ForegroundGrey, Margin = new Margin(1), Brush = new SpriteBrush(icons.Right32), - Offsets = new Margin(-buttonSize - 2 + uiLeft, buttonSize, buttonSize * -0.5f, buttonSize), + Offsets = new Margin(-keySize - 2 + uiLeft, keySize, keySize * -0.5f, keySize), Parent = this, }; _addKey = new Image @@ -141,7 +142,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks Color = Style.Current.ForegroundGrey, Margin = new Margin(3), Brush = new SpriteBrush(icons.Add32), - Offsets = new Margin(-buttonSize - 2 + _rightKey.Offsets.Left, buttonSize, buttonSize * -0.5f, buttonSize), + Offsets = new Margin(-addSize - 2 + _rightKey.Offsets.Left, addSize, addSize * -0.5f, addSize), Parent = this, }; _leftKey = new Image @@ -153,7 +154,7 @@ namespace FlaxEditor.GUI.Timeline.Tracks Color = Style.Current.ForegroundGrey, Margin = new Margin(1), Brush = new SpriteBrush(icons.Left32), - Offsets = new Margin(-buttonSize - 2 + _addKey.Offsets.Left, buttonSize, buttonSize * -0.5f, buttonSize), + Offsets = new Margin(-keySize - 2 + _addKey.Offsets.Left, keySize, keySize * -0.5f, keySize), Parent = this, }; uiLeft = _leftKey.Offsets.Left; From 8e781ccf7b1363ed9e6e9e3c5601f862a480e718 Mon Sep 17 00:00:00 2001 From: W2Wizard <63303990+W2Wizard@users.noreply.github.com> Date: Sun, 13 Jun 2021 17:33:58 +0200 Subject: [PATCH 153/198] Apply asset new icons Apply the Icons for SwitchSettings, LocalizationSettings and Json Asset. --- Source/Editor/Content/Items/JsonAssetItem.cs | 2 +- Source/Editor/EditorIcons.cs | 2 +- Source/Editor/GUI/PlatformSelector.cs | 5 ++--- Source/Editor/Modules/ContentDatabaseModule.cs | 11 +++++++---- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Source/Editor/Content/Items/JsonAssetItem.cs b/Source/Editor/Content/Items/JsonAssetItem.cs index 1c9232c9c..80957f181 100644 --- a/Source/Editor/Content/Items/JsonAssetItem.cs +++ b/Source/Editor/Content/Items/JsonAssetItem.cs @@ -22,7 +22,7 @@ namespace FlaxEditor.Content public JsonAssetItem(string path, Guid id, string typeName) : base(path, typeName, ref id) { - _thumbnail = Editor.Instance.Icons.Document128; + _thumbnail = Editor.Instance.Icons.Json128; } /// diff --git a/Source/Editor/EditorIcons.cs b/Source/Editor/EditorIcons.cs index da92a159f..1f7be216e 100644 --- a/Source/Editor/EditorIcons.cs +++ b/Source/Editor/EditorIcons.cs @@ -136,8 +136,8 @@ namespace FlaxEditor public SpriteHandle FlaxLogo128; public SpriteHandle SwitchIcon128; public SpriteHandle SwitchSettings128; - public SpriteHandle Json128; public SpriteHandle LocalizationSettings128; + public SpriteHandle Json128; internal void LoadIcons() { diff --git a/Source/Editor/GUI/PlatformSelector.cs b/Source/Editor/GUI/PlatformSelector.cs index cc7bc003a..3b9802873 100644 --- a/Source/Editor/GUI/PlatformSelector.cs +++ b/Source/Editor/GUI/PlatformSelector.cs @@ -89,11 +89,10 @@ namespace FlaxEditor.GUI new PlatformData(PlatformType.PS4, icons.PS4Icon128, "PlayStation 4"), new PlatformData(PlatformType.XboxScarlett, icons.XBoxScarletIcon128, "Xbox Scarlett"), new PlatformData(PlatformType.Android, icons.AndroidIcon128, "Android"), - new PlatformData(PlatformType.Switch, icons.ColorWheel128, "Switch"), - + new PlatformData(PlatformType.Switch, icons.SwitchIcon128, "Switch"), }; - const float IconSize = 48.0f; + const float IconSize = 64.0f; TileSize = new Vector2(IconSize); AutoResize = true; Offsets = new Margin(0, 0, 0, IconSize); diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs index ec71b8564..ca4577d55 100644 --- a/Source/Editor/Modules/ContentDatabaseModule.cs +++ b/Source/Editor/Modules/ContentDatabaseModule.cs @@ -936,23 +936,26 @@ namespace FlaxEditor.Modules Proxy.Add(new SettingsProxy(typeof(PhysicsSettings), Editor.Instance.Icons.PhysicsSettings128)); Proxy.Add(new SettingsProxy(typeof(GraphicsSettings), Editor.Instance.Icons.GraphicsSettings128)); Proxy.Add(new SettingsProxy(typeof(NavigationSettings), Editor.Instance.Icons.NavigationSettings128)); - Proxy.Add(new SettingsProxy(typeof(LocalizationSettings), Editor.Instance.Icons.Document128)); + Proxy.Add(new SettingsProxy(typeof(LocalizationSettings), Editor.Instance.Icons.LocalizationSettings128)); + Proxy.Add(new SettingsProxy(typeof(AudioSettings), Editor.Instance.Icons.AudioSettings128)); Proxy.Add(new SettingsProxy(typeof(BuildSettings), Editor.Instance.Icons.BuildSettings128)); Proxy.Add(new SettingsProxy(typeof(InputSettings), Editor.Instance.Icons.InputSettings128)); Proxy.Add(new SettingsProxy(typeof(WindowsPlatformSettings), Editor.Instance.Icons.WindowsSettings128)); Proxy.Add(new SettingsProxy(typeof(UWPPlatformSettings), Editor.Instance.Icons.UWPSettings128)); Proxy.Add(new SettingsProxy(typeof(LinuxPlatformSettings), Editor.Instance.Icons.LinuxSettings128)); + Proxy.Add(new SettingsProxy(typeof(AndroidPlatformSettings), Editor.Instance.Icons.AndroidSettings128)); + var typePS4PlatformSettings = TypeUtils.GetManagedType(GameSettings.PS4PlatformSettingsTypename); if (typePS4PlatformSettings != null) Proxy.Add(new SettingsProxy(typePS4PlatformSettings, Editor.Instance.Icons.PlaystationSettings128)); + var typeXboxScarlettPlatformSettings = TypeUtils.GetManagedType(GameSettings.XboxScarlettPlatformSettingsTypename); if (typeXboxScarlettPlatformSettings != null) Proxy.Add(new SettingsProxy(typeXboxScarlettPlatformSettings, Editor.Instance.Icons.XBoxScarletIcon128)); - Proxy.Add(new SettingsProxy(typeof(AndroidPlatformSettings), Editor.Instance.Icons.AndroidSettings128)); + var typeSwitchPlatformSettings = TypeUtils.GetManagedType(GameSettings.SwitchPlatformSettingsTypename); if (typeSwitchPlatformSettings != null) - Proxy.Add(new SettingsProxy(typeSwitchPlatformSettings, Editor.Instance.Icons.Document128)); - Proxy.Add(new SettingsProxy(typeof(AudioSettings), Editor.Instance.Icons.AudioSettings128)); + Proxy.Add(new SettingsProxy(typeSwitchPlatformSettings, Editor.Instance.Icons.SwitchSettings128)); // Last add generic json (won't override other json proxies) Proxy.Add(new GenericJsonAssetProxy()); From 5408898e15c0fcbb301e97217f135822fa95f7f4 Mon Sep 17 00:00:00 2001 From: W2Wizard <63303990+W2Wizard@users.noreply.github.com> Date: Sun, 13 Jun 2021 20:16:06 +0200 Subject: [PATCH 154/198] Increase BoxSize & overhaul debug log icons --- Source/Editor/Surface/Constants.cs | 2 +- Source/Editor/Windows/DebugLogWindow.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Editor/Surface/Constants.cs b/Source/Editor/Surface/Constants.cs index 3636d193c..a63d8b489 100644 --- a/Source/Editor/Surface/Constants.cs +++ b/Source/Editor/Surface/Constants.cs @@ -48,7 +48,7 @@ namespace FlaxEditor.Surface /// /// The box size (with and height). /// - public const float BoxSize = 16.0f; + public const float BoxSize = 20.0f; /// /// The node layout offset on the y axis (height of the boxes rows, etc.). It's used to make the design more consistent. diff --git a/Source/Editor/Windows/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs index bc80a0ba7..e95debfff 100644 --- a/Source/Editor/Windows/DebugLogWindow.cs +++ b/Source/Editor/Windows/DebugLogWindow.cs @@ -312,9 +312,9 @@ namespace FlaxEditor.Windows _clearOnPlayButton = (ToolStripButton)toolstrip.AddButton("Clear on Play").SetAutoCheck(true).SetChecked(true).LinkTooltip("Clears all log entries on enter playmode"); _pauseOnErrorButton = (ToolStripButton)toolstrip.AddButton("Pause on Error").SetAutoCheck(true).LinkTooltip("Performs auto pause on error"); toolstrip.AddSeparator(); - _groupButtons[0] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Error64, () => UpdateLogTypeVisibility(LogGroup.Error, _groupButtons[0].Checked)).SetAutoCheck(true).SetChecked(true).LinkTooltip("Shows/hides error messages"); - _groupButtons[1] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Warning64, () => UpdateLogTypeVisibility(LogGroup.Warning, _groupButtons[1].Checked)).SetAutoCheck(true).SetChecked(true).LinkTooltip("Shows/hides warning messages"); - _groupButtons[2] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Info64, () => UpdateLogTypeVisibility(LogGroup.Info, _groupButtons[2].Checked)).SetAutoCheck(true).SetChecked(true).LinkTooltip("Shows/hides info messages"); + _groupButtons[0] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Error32, () => UpdateLogTypeVisibility(LogGroup.Error, _groupButtons[0].Checked)).SetAutoCheck(true).SetChecked(true).LinkTooltip("Shows/hides error messages"); + _groupButtons[1] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Warning32, () => UpdateLogTypeVisibility(LogGroup.Warning, _groupButtons[1].Checked)).SetAutoCheck(true).SetChecked(true).LinkTooltip("Shows/hides warning messages"); + _groupButtons[2] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Info32, () => UpdateLogTypeVisibility(LogGroup.Info, _groupButtons[2].Checked)).SetAutoCheck(true).SetChecked(true).LinkTooltip("Shows/hides info messages"); UpdateCount(); // Split panel @@ -387,10 +387,10 @@ namespace FlaxEditor.Windows switch (_timestampsFormats) { case InterfaceOptions.TimestampsFormats.Utc: - desc.Title = string.Format("[{0}] ", DateTime.UtcNow) + desc.Title; + desc.Title = $"[{DateTime.UtcNow}] {desc.Title}"; break; case InterfaceOptions.TimestampsFormats.LocalTime: - desc.Title = string.Format("[{0}] ", DateTime.Now) + desc.Title; + desc.Title = $"[{DateTime.Now}] {desc.Title}"; break; case InterfaceOptions.TimestampsFormats.TimeSinceStartup: desc.Title = string.Format("[{0:g}] ", TimeSpan.FromSeconds(Time.TimeSinceStartup)) + desc.Title; From 83e6319643ad33c3bd60cd6c0185b9c306f5f753 Mon Sep 17 00:00:00 2001 From: W2Wizard <63303990+W2Wizard@users.noreply.github.com> Date: Sun, 13 Jun 2021 22:37:47 +0200 Subject: [PATCH 155/198] Adjust debug log tab icon --- Source/Editor/GUI/Docking/DockPanel.cs | 2 +- Source/Editor/Windows/DebugLogWindow.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Editor/GUI/Docking/DockPanel.cs b/Source/Editor/GUI/Docking/DockPanel.cs index f968c2501..0f31759f2 100644 --- a/Source/Editor/GUI/Docking/DockPanel.cs +++ b/Source/Editor/GUI/Docking/DockPanel.cs @@ -84,7 +84,7 @@ namespace FlaxEditor.GUI.Docking /// /// The default tabs header buttons size. /// - public const float DefaultButtonsSize = 12; + public const float DefaultButtonsSize = 15; /// /// The default tabs header buttons margin. diff --git a/Source/Editor/Windows/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs index e95debfff..13f72992c 100644 --- a/Source/Editor/Windows/DebugLogWindow.cs +++ b/Source/Editor/Windows/DebugLogWindow.cs @@ -480,15 +480,15 @@ namespace FlaxEditor.Windows { if (_iconType == LogType.Warning) { - Icon = IconWarning; + Icon = Editor.Icons.Warning32; } else if (_iconType == LogType.Error) { - Icon = IconError; + Icon = Editor.Icons.Error32; } else { - Icon = IconInfo; + Icon = Editor.Icons.Info32; } } From b225622eaaf19b14838f376344693105cc72fe26 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 14 Jun 2021 09:31:19 +0200 Subject: [PATCH 156/198] Fix typo --- Source/Editor/CustomEditors/LayoutElementsContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/CustomEditors/LayoutElementsContainer.cs b/Source/Editor/CustomEditors/LayoutElementsContainer.cs index 6f645789a..d7df2fbb1 100644 --- a/Source/Editor/CustomEditors/LayoutElementsContainer.cs +++ b/Source/Editor/CustomEditors/LayoutElementsContainer.cs @@ -114,7 +114,7 @@ namespace FlaxEditor.CustomEditors } /// - /// Adds new horizontal panel element. + /// Adds new vertical panel element. /// /// The created element. public VerticalPanelElement VerticalPanel() From 22c62439eef8d8e99aefc133070073b4445f008f Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 14 Jun 2021 10:41:51 +0200 Subject: [PATCH 157/198] Fixes and tweaks for profiler window --- Source/Editor/Windows/Profiler/CPU.cs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/Source/Editor/Windows/Profiler/CPU.cs b/Source/Editor/Windows/Profiler/CPU.cs index 5d752c0fb..18317216f 100644 --- a/Source/Editor/Windows/Profiler/CPU.cs +++ b/Source/Editor/Windows/Profiler/CPU.cs @@ -274,10 +274,11 @@ namespace FlaxEditor.Windows.Profiler ref ProfilerCPU.Event e = ref events[index]; double length = e.End - e.Start; + if (length <= 0.0) + return; double scale = 100.0; float x = (float)((e.Start - startTime) * scale); float width = (float)(length * scale); - Timeline.Event control; if (_timelineEventsCache.Count != 0) { @@ -301,7 +302,6 @@ namespace FlaxEditor.Windows.Profiler while (++index < events.Length) { int subDepth = events[index].Depth; - if (subDepth <= e.Depth) break; if (subDepth == childrenDepth) @@ -376,11 +376,8 @@ namespace FlaxEditor.Windows.Profiler for (int j = 0; j < events.Length; j++) { var e = events[j]; - - // Reject events outside the view range if (viewRange.SkipEvent(ref e)) continue; - maxDepth = Mathf.Max(maxDepth, e.Depth); } @@ -412,10 +409,8 @@ namespace FlaxEditor.Windows.Profiler var e = events[j]; if (e.Depth == 0) { - // Reject events outside the view range if (viewRange.SkipEvent(ref e)) continue; - AddEvent(startTime, maxDepth, xOffset, depthOffset, j, events, container); } } @@ -472,9 +467,7 @@ namespace FlaxEditor.Windows.Profiler { var e = events[i]; var time = Math.Max(e.End - e.Start, MinEventTimeMs); - - // Reject events outside the view range - if (viewRange.SkipEvent(ref e)) + if (e.End <= 0.0f || viewRange.SkipEvent(ref e)) continue; // Count sub-events time @@ -483,7 +476,7 @@ namespace FlaxEditor.Windows.Profiler for (int k = i + 1; k < events.Length; k++) { var sub = events[k]; - if (sub.Depth == e.Depth + 1) + if (sub.Depth == e.Depth + 1 && e.End > 0.0f) { subEventsTimeTotal += Math.Max(sub.End - sub.Start, MinEventTimeMs); } @@ -491,7 +484,6 @@ namespace FlaxEditor.Windows.Profiler { break; } - subEventsMemoryTotal += sub.ManagedMemoryAllocation + e.NativeMemoryAllocation; } @@ -532,7 +524,7 @@ namespace FlaxEditor.Windows.Profiler } row.Depth = e.Depth; row.Width = _table.Width; - row.Visible = e.Depth < 3; + row.Visible = e.Depth < 2; row.BackgroundColor = i % 2 == 0 ? rowColor2 : Color.Transparent; row.Parent = _table; } From aec071689ab80874822f131352d326986cf30266 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 14 Jun 2021 10:44:46 +0200 Subject: [PATCH 158/198] Fix game build --- Source/Engine/Animations/Animations.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Source/Engine/Animations/Animations.cpp b/Source/Engine/Animations/Animations.cpp index 179e710e4..efff52998 100644 --- a/Source/Engine/Animations/Animations.cpp +++ b/Source/Engine/Animations/Animations.cpp @@ -33,7 +33,9 @@ public: AnimationsService AnimationManagerInstance; Array UpdateList; TaskGraphSystem* Animations::System = nullptr; +#if USE_EDITOR Delegate Animations::DebugFlow; +#endif bool AnimationsService::Init() { From a8b0612a7f0433d0605b2b78334af8a113ef8183 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 14 Jun 2021 11:08:47 +0200 Subject: [PATCH 159/198] Add new icons and tweaks #550 --- Content/Editor/IconsAtlas.flax | 4 ++-- Source/Editor/Windows/Assets/VisualScriptWindow.cs | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Content/Editor/IconsAtlas.flax b/Content/Editor/IconsAtlas.flax index 13af92065..519aebba2 100644 --- a/Content/Editor/IconsAtlas.flax +++ b/Content/Editor/IconsAtlas.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:de930eef22208b95882bc9f870063e339672b577d381bcfa6603f0be45c814c5 -size 5610110 +oid sha256:63e9f8409580a700befdfceac9f8db3123efb72eb589b54d86429d330ff36a8f +size 5611340 diff --git a/Source/Editor/Windows/Assets/VisualScriptWindow.cs b/Source/Editor/Windows/Assets/VisualScriptWindow.cs index 545f15867..e19fe54ca 100644 --- a/Source/Editor/Windows/Assets/VisualScriptWindow.cs +++ b/Source/Editor/Windows/Assets/VisualScriptWindow.cs @@ -220,9 +220,6 @@ namespace FlaxEditor.Windows.Assets private void ShowContextMenu(SurfaceNode node, ClickableLabel label) { - // TODO: Execute only on "Edit Signature...", this makes the "Show" button useless - ((VisualScriptWindow)Values[0]).Surface.FocusNode(node); - var cm = new ContextMenu(); cm.AddButton("Show", () => ((VisualScriptWindow)Values[0]).Surface.FocusNode(node)).Icon = Editor.Instance.Icons.Search12; cm.AddButton("Delete", () => ((VisualScriptWindow)Values[0]).Surface.Delete(node)).Icon = Editor.Instance.Icons.Cross12; From 428c43e70757a1915258c8af91339ab752c51e39 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 14 Jun 2021 12:04:49 +0200 Subject: [PATCH 160/198] Optimize Debug Log window new entries adding (manual placement instead of full layout) --- Source/Editor/Windows/DebugLogWindow.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Windows/DebugLogWindow.cs b/Source/Editor/Windows/DebugLogWindow.cs index 13f72992c..dd9de3edc 100644 --- a/Source/Editor/Windows/DebugLogWindow.cs +++ b/Source/Editor/Windows/DebugLogWindow.cs @@ -587,13 +587,10 @@ namespace FlaxEditor.Windows /// public override void Update(float deltaTime) { - // Check pending events (in a thread safe manner) lock (_locker) { if (_pendingEntries.Count > 0) { - // TODO: we should provide max limit for entries count and remove if too many - // Check if user want's to scroll view by var (or is viewing earlier entry) var panelScroll = (Panel)_entriesPanel.Parent; bool scrollView = (panelScroll.VScrollBar.Maximum - panelScroll.VScrollBar.TargetValue) < LogEntry.DefaultHeight * 1.5f; @@ -601,14 +598,24 @@ namespace FlaxEditor.Windows // Add pending entries LogEntry newEntry = null; bool anyVisible = false; + _entriesPanel.IsLayoutLocked = true; + var spacing = _entriesPanel.Spacing; + var margin = _entriesPanel.Margin; + var offset = _entriesPanel.Offset; + var width = _entriesPanel.Width - margin.Width; + var top = _entriesPanel.Children.Count != 0 ? _entriesPanel.Children[_entriesPanel.Children.Count - 1].Bottom + spacing : margin.Top; for (int i = 0; i < _pendingEntries.Count; i++) { newEntry = _pendingEntries[i]; newEntry.Visible = _groupButtons[(int)newEntry.Group].Checked; anyVisible |= newEntry.Visible; newEntry.Parent = _entriesPanel; + newEntry.Bounds = new Rectangle(margin.Left + offset.X, top + offset.Y, width, newEntry.Height); + top = newEntry.Bottom + spacing; _logCountPerGroup[(int)newEntry.Group]++; } + _entriesPanel.Height = top + margin.Bottom; + _entriesPanel.IsLayoutLocked = false; _pendingEntries.Clear(); UpdateCount(); Assert.IsNotNull(newEntry); From da0c8ffff9ce8525fae678fdf8844818cea94a5e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 14 Jun 2021 13:29:47 +0200 Subject: [PATCH 161/198] Optimize Debug Draw for large amount of single-frame debug shapes and lines --- Source/Engine/Debug/DebugDraw.cpp | 271 ++++++++++++++++--------- Source/Engine/Graphics/DynamicBuffer.h | 2 +- 2 files changed, 173 insertions(+), 100 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index b443577fb..883c50882 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -154,7 +154,7 @@ void UpdateList(float dt, Array& list) struct DebugDrawData { Array DefaultLines; - Array OneFrameLines; + Array OneFrameLines; Array DefaultTriangles; Array OneFrameTriangles; Array DefaultWireTriangles; @@ -184,14 +184,6 @@ struct DebugDrawData return DefaultText2D.Count() + OneFrameText2D.Count() + DefaultText3D.Count() + OneFrameText3D.Count(); } - inline void Add(const DebugLine& l) - { - if (l.TimeLeft > 0) - DefaultLines.Add(l); - else - OneFrameLines.Add(l); - } - inline void Add(const DebugTriangle& t) { if (t.TimeLeft > 0) @@ -278,12 +270,40 @@ namespace extern int32 BoxTrianglesIndicesCache[]; +int32 BoxLineIndicesCache[] = +{ + // @formatter:off + 0, 1, + 0, 3, + 0, 4, + 1, 2, + 1, 5, + 2, 3, + 2, 6, + 3, 7, + 4, 5, + 4, 7, + 5, 6, + 6, 7, + // @formatter:on +}; + struct DebugDrawCall { int32 StartVertex; int32 VertexCount; }; +DebugDrawCall WriteList(int32& vertexCounter, const Array& list) +{ + DebugDrawCall drawCall; + drawCall.StartVertex = vertexCounter; + drawCall.VertexCount = list.Count(); + DebugDrawVB->Write(list.Get(), sizeof(Vertex) * drawCall.VertexCount); + vertexCounter += drawCall.VertexCount; + return drawCall; +} + DebugDrawCall WriteList(int32& vertexCounter, const Array& list) { DebugDrawCall drawCall; @@ -293,12 +313,10 @@ DebugDrawCall WriteList(int32& vertexCounter, const Array& list) for (int32 i = 0; i < list.Count(); i++) { const DebugLine& l = list[i]; - vv[0].Position = l.Start; vv[0].Color = l.Color; vv[1].Position = l.End; vv[1].Color = vv[0].Color; - DebugDrawVB->Write(vv, sizeof(Vertex) * 2); } @@ -317,14 +335,12 @@ DebugDrawCall WriteList(int32& vertexCounter, const Array& list) for (int32 i = 0; i < list.Count(); i++) { const DebugTriangle& l = list[i]; - vv[0].Position = l.V0; vv[0].Color = l.Color; vv[1].Position = l.V1; vv[1].Color = vv[0].Color; vv[2].Position = l.V2; vv[2].Color = vv[0].Color; - DebugDrawVB->Write(vv, sizeof(Vertex) * 3); } @@ -334,12 +350,11 @@ DebugDrawCall WriteList(int32& vertexCounter, const Array& list) return drawCall; } -template -DebugDrawCall WriteLists(int32& vertexCounter, const Array& listA, const Array& listB) +template +DebugDrawCall WriteLists(int32& vertexCounter, const Array& listA, const Array& listB) { const DebugDrawCall drawCallA = WriteList(vertexCounter, listA); const DebugDrawCall drawCallB = WriteList(vertexCounter, listB); - DebugDrawCall drawCall; drawCall.StartVertex = drawCallA.StartVertex; drawCall.VertexCount = drawCallA.VertexCount + drawCallB.VertexCount; @@ -364,7 +379,6 @@ inline void DrawText3D(const DebugText3D& t, const RenderContext& renderContext, class DebugDrawService : public EngineService { public: - DebugDrawService() : EngineService(TEXT("Debug Draw"), -80) { @@ -640,21 +654,22 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe target = renderContext.Task->GetOutputView(); // Fill vertex buffer and upload data -#if COMPILE_WITH_PROFILER - const auto updateBufferProfileKey = ProfilerCPU::BeginEvent(TEXT("Update Buffer")); -#endif - DebugDrawVB->Clear(); - int32 vertexCounter = 0; - const DebugDrawCall depthTestLines = WriteLists(vertexCounter, Context->DebugDrawDepthTest.DefaultLines, Context->DebugDrawDepthTest.OneFrameLines); - const DebugDrawCall defaultLines = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultLines, Context->DebugDrawDefault.OneFrameLines); - const DebugDrawCall depthTestTriangles = WriteLists(vertexCounter, Context->DebugDrawDepthTest.DefaultTriangles, Context->DebugDrawDepthTest.OneFrameTriangles); - const DebugDrawCall defaultTriangles = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultTriangles, Context->DebugDrawDefault.OneFrameTriangles); - const DebugDrawCall depthTestWireTriangles = WriteLists(vertexCounter, Context->DebugDrawDepthTest.DefaultWireTriangles, Context->DebugDrawDepthTest.OneFrameWireTriangles); - const DebugDrawCall defaultWireTriangles = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultWireTriangles, Context->DebugDrawDefault.OneFrameWireTriangles); - DebugDrawVB->Flush(context); -#if COMPILE_WITH_PROFILER - ProfilerCPU::EndEvent(updateBufferProfileKey); -#endif + DebugDrawCall depthTestLines, defaultLines, depthTestTriangles, defaultTriangles, depthTestWireTriangles, defaultWireTriangles; + { + PROFILE_CPU_NAMED("Update Buffer"); + DebugDrawVB->Clear(); + int32 vertexCounter = 0; + depthTestLines = WriteLists(vertexCounter, Context->DebugDrawDepthTest.DefaultLines, Context->DebugDrawDepthTest.OneFrameLines); + defaultLines = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultLines, Context->DebugDrawDefault.OneFrameLines); + depthTestTriangles = WriteLists(vertexCounter, Context->DebugDrawDepthTest.DefaultTriangles, Context->DebugDrawDepthTest.OneFrameTriangles); + defaultTriangles = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultTriangles, Context->DebugDrawDefault.OneFrameTriangles); + depthTestWireTriangles = WriteLists(vertexCounter, Context->DebugDrawDepthTest.DefaultWireTriangles, Context->DebugDrawDepthTest.OneFrameWireTriangles); + defaultWireTriangles = WriteLists(vertexCounter, Context->DebugDrawDefault.DefaultWireTriangles, Context->DebugDrawDefault.OneFrameWireTriangles); + { + PROFILE_CPU_NAMED("Flush"); + DebugDrawVB->Flush(context); + } + } // Update constant buffer const auto cb = DebugDrawShader->GetShader()->GetCB(0); @@ -785,11 +800,11 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe namespace { - bool DrawActorsTreeWalk(Actor* actor) + bool DrawActorsTreeWalk(Actor* a) { - if (actor->IsActiveInHierarchy()) + if (a->IsActiveInHierarchy()) { - actor->OnDebugDraw(); + a->OnDebugDraw(); return true; } return false; @@ -819,14 +834,19 @@ void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount, bo void DebugDraw::DrawLine(const Vector3& start, const Vector3& end, const Color& color, float duration, bool depthTest) { - // Create draw call entry - DebugLine l = { start, end, Color32(color), duration }; - - // Add line - if (depthTest) - Context->DebugDrawDepthTest.Add(l); + auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault; + if (duration > 0) + { + DebugLine l = { start, end, Color32(color), duration }; + debugDrawData.DefaultLines.Add(l); + } else - Context->DebugDrawDefault.Add(l); + { + Vertex l = { start, Color32(color) }; + debugDrawData.OneFrameLines.Add(l); + l.Position = end; + debugDrawData.OneFrameLines.Add(l); + } } void DebugDraw::DrawLines(const Span& lines, const Matrix& transform, const Color& color, float duration, bool depthTest) @@ -837,37 +857,36 @@ void DebugDraw::DrawLines(const Span& lines, const Matrix& transform, c return; } - // Create draw call entry - DebugLine l = { Vector3::Zero, Vector3::Zero, Color32(color), duration }; - - // Add lines + // Draw lines const Vector3* p = lines.Get(); - Array* list; - - if (depthTest) - list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultLines : &Context->DebugDrawDepthTest.OneFrameLines; - else - list = duration > 0 ? &Context->DebugDrawDefault.DefaultLines : &Context->DebugDrawDefault.OneFrameLines; - - list->EnsureCapacity(list->Count() + lines.Length()); - for (int32 i = 0; i < lines.Length(); i += 2) + auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault; + if (duration > 0) { - Vector3::Transform(*p++, transform, l.Start); - Vector3::Transform(*p++, transform, l.End); - list->Add(l); + DebugLine l = { Vector3::Zero, Vector3::Zero, Color32(color), duration }; + debugDrawData.DefaultLines.EnsureCapacity(debugDrawData.DefaultLines.Count() + lines.Length()); + for (int32 i = 0; i < lines.Length(); i += 2) + { + Vector3::Transform(*p++, transform, l.Start); + Vector3::Transform(*p++, transform, l.End); + debugDrawData.DefaultLines.Add(l); + } + } + else + { + Vertex l = { Vector3::Zero, Color32(color) }; + debugDrawData.OneFrameLines.EnsureCapacity(debugDrawData.OneFrameLines.Count() + lines.Length() * 2); + for (int32 i = 0; i < lines.Length(); i += 2) + { + Vector3::Transform(*p++, transform, l.Position); + debugDrawData.OneFrameLines.Add(l); + Vector3::Transform(*p++, transform, l.Position); + debugDrawData.OneFrameLines.Add(l); + } } } void DebugDraw::DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3& p3, const Vector3& p4, const Color& color, float duration, bool depthTest) { - // Create draw call entry - Array* list; - if (depthTest) - list = duration > 0 ? &Context->DebugDrawDepthTest.DefaultLines : &Context->DebugDrawDepthTest.OneFrameLines; - else - list = duration > 0 ? &Context->DebugDrawDefault.DefaultLines : &Context->DebugDrawDefault.OneFrameLines; - DebugLine l = { p1, Vector3::Zero, Color32(color), duration }; - // Find amount of segments to use const Vector3 d1 = p2 - p1; const Vector3 d2 = p3 - p2; @@ -875,15 +894,32 @@ void DebugDraw::DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3& const float len = d1.Length() + d2.Length() + d3.Length(); const int32 segmentCount = Math::Clamp(Math::CeilToInt(len * 0.05f), 1, 100); const float segmentCountInv = 1.0f / (float)segmentCount; - list->EnsureCapacity(list->Count() + segmentCount + 2); - // Draw segmented curve - for (int32 i = 0; i <= segmentCount; i++) + // Draw segmented curve from lines + auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault; + if (duration > 0) { - const float t = (float)i * segmentCountInv; - AnimationUtils::Bezier(p1, p2, p3, p4, t, l.End); - list->Add(l); - l.Start = l.End; + DebugLine l = { p1, Vector3::Zero, Color32(color), duration }; + debugDrawData.DefaultLines.EnsureCapacity(debugDrawData.DefaultLines.Count() + segmentCount + 2); + for (int32 i = 0; i <= segmentCount; i++) + { + const float t = (float)i * segmentCountInv; + AnimationUtils::Bezier(p1, p2, p3, p4, t, l.End); + debugDrawData.DefaultLines.Add(l); + l.Start = l.End; + } + } + else + { + Vertex l = { p1, Color32(color) }; + debugDrawData.OneFrameLines.EnsureCapacity(debugDrawData.OneFrameLines.Count() + segmentCount * 2 + 4); + for (int32 i = 0; i <= segmentCount; i++) + { + const float t = (float)i * segmentCountInv; + debugDrawData.OneFrameLines.Add(l); + AnimationUtils::Bezier(p1, p2, p3, p4, t, l.Position); + debugDrawData.OneFrameLines.Add(l); + } } } @@ -909,16 +945,28 @@ void DebugDraw::DrawWireBox(const BoundingBox& box, const Color& color, float du box.GetCorners(corners); // Draw lines - DebugLine l; - l.Color = Color32(color); - l.TimeLeft = duration; - if (depthTest) + auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault; + if (duration > 0) { - DRAW_WIRE_BOX(DebugDrawDepthTest); + DebugLine l = { Vector3::Zero, Vector3::Zero, Color32(color), duration }; + for (uint32 i = 0; i < ARRAY_COUNT(BoxLineIndicesCache);) + { + l.Start = corners[BoxLineIndicesCache[i++]]; + l.End = corners[BoxLineIndicesCache[i++]]; + debugDrawData.DefaultLines.Add(l); + } } else { - DRAW_WIRE_BOX(DebugDrawDefault); + // TODO: draw lines as strips to reuse vertices with a single draw call + Vertex l = { Vector3::Zero, Color32(color) }; + for (uint32 i = 0; i < ARRAY_COUNT(BoxLineIndicesCache);) + { + l.Position = corners[BoxLineIndicesCache[i++]]; + debugDrawData.OneFrameLines.Add(l); + l.Position = corners[BoxLineIndicesCache[i++]]; + debugDrawData.OneFrameLines.Add(l); + } } } @@ -929,16 +977,28 @@ void DebugDraw::DrawWireFrustum(const BoundingFrustum& frustum, const Color& col frustum.GetCorners(corners); // Draw lines - DebugLine l; - l.Color = Color32(color); - l.TimeLeft = duration; - if (depthTest) + auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault; + if (duration > 0) { - DRAW_WIRE_BOX(DebugDrawDepthTest); + DebugLine l = { Vector3::Zero, Vector3::Zero, Color32(color), duration }; + for (uint32 i = 0; i < ARRAY_COUNT(BoxLineIndicesCache);) + { + l.Start = corners[BoxLineIndicesCache[i++]]; + l.End = corners[BoxLineIndicesCache[i++]]; + debugDrawData.DefaultLines.Add(l); + } } else { - DRAW_WIRE_BOX(DebugDrawDefault); + // TODO: draw lines as strips to reuse vertices with a single draw call + Vertex l = { Vector3::Zero, Color32(color) }; + for (uint32 i = 0; i < ARRAY_COUNT(BoxLineIndicesCache);) + { + l.Position = corners[BoxLineIndicesCache[i++]]; + debugDrawData.OneFrameLines.Add(l); + l.Position = corners[BoxLineIndicesCache[i++]]; + debugDrawData.OneFrameLines.Add(l); + } } } @@ -949,41 +1009,54 @@ void DebugDraw::DrawWireBox(const OrientedBoundingBox& box, const Color& color, box.GetCorners(corners); // Draw lines - DebugLine l; - l.Color = Color32(color); - l.TimeLeft = duration; - if (depthTest) + auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault; + if (duration > 0) { - DRAW_WIRE_BOX(DebugDrawDepthTest); + DebugLine l = { Vector3::Zero, Vector3::Zero, Color32(color), duration }; + for (uint32 i = 0; i < ARRAY_COUNT(BoxLineIndicesCache);) + { + l.Start = corners[BoxLineIndicesCache[i++]]; + l.End = corners[BoxLineIndicesCache[i++]]; + debugDrawData.DefaultLines.Add(l); + } } else { - DRAW_WIRE_BOX(DebugDrawDefault); + // TODO: draw lines as strips to reuse vertices with a single draw call + Vertex l = { Vector3::Zero, Color32(color) }; + for (uint32 i = 0; i < ARRAY_COUNT(BoxLineIndicesCache);) + { + l.Position = corners[BoxLineIndicesCache[i++]]; + debugDrawData.OneFrameLines.Add(l); + l.Position = corners[BoxLineIndicesCache[i++]]; + debugDrawData.OneFrameLines.Add(l); + } } } void DebugDraw::DrawWireSphere(const BoundingSphere& sphere, const Color& color, float duration, bool depthTest) { // Draw lines of the unit sphere after linear transform - DebugLine l; - l.Color = Color32(color); - l.TimeLeft = duration; - if (depthTest) + auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault; + if (duration > 0) { + DebugLine l = { Vector3::Zero, Vector3::Zero, Color32(color), duration }; for (int32 i = 0; i < DEBUG_DRAW_SPHERE_VERTICES;) { l.Start = sphere.Center + SphereCache[i++] * sphere.Radius; l.End = sphere.Center + SphereCache[i++] * sphere.Radius; - Context->DebugDrawDepthTest.Add(l); + debugDrawData.DefaultLines.Add(l); } } else { + Vertex l = { Vector3::Zero, Color32(color) }; for (int32 i = 0; i < DEBUG_DRAW_SPHERE_VERTICES;) { - l.Start = sphere.Center + SphereCache[i++] * sphere.Radius; - l.End = sphere.Center + SphereCache[i++] * sphere.Radius; - Context->DebugDrawDefault.Add(l); + l.Position = sphere.Center + SphereCache[i++] * sphere.Radius; + debugDrawData.OneFrameLines.Add(l); + l.Position = sphere.Center + SphereCache[i++] * sphere.Radius; + debugDrawData.OneFrameLines.Add(l); } } } diff --git a/Source/Engine/Graphics/DynamicBuffer.h b/Source/Engine/Graphics/DynamicBuffer.h index f22143c1b..eb8e9fb5f 100644 --- a/Source/Engine/Graphics/DynamicBuffer.h +++ b/Source/Engine/Graphics/DynamicBuffer.h @@ -70,7 +70,7 @@ public: /// /// Pointer to data to write /// Amount of data to write (in bytes) - FORCE_INLINE void Write(void* bytes, int32 size) + FORCE_INLINE void Write(const void* bytes, int32 size) { Data.Add((byte*)bytes, size); } From e455ada90a034e6d8d971d68685274ab37a79d2e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 14 Jun 2021 14:22:46 +0200 Subject: [PATCH 162/198] Optimize Debug Draw wireframe sphere to use auto-LOD --- Source/Engine/Debug/DebugDraw.cpp | 118 +++++++++++++++++------------- 1 file changed, 68 insertions(+), 50 deletions(-) diff --git a/Source/Engine/Debug/DebugDraw.cpp b/Source/Engine/Debug/DebugDraw.cpp index 883c50882..ac2b744bc 100644 --- a/Source/Engine/Debug/DebugDraw.cpp +++ b/Source/Engine/Debug/DebugDraw.cpp @@ -16,6 +16,7 @@ #include "Engine/Graphics/GPUPipelineState.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderBuffers.h" +#include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/DynamicBuffer.h" #include "Engine/Graphics/Shaders/GPUConstantBuffer.h" #include "Engine/Graphics/Shaders/GPUShader.h" @@ -31,9 +32,11 @@ // Debug draw service configuration #define DEBUG_DRAW_INITIAL_VB_CAPACITY (4 * 1024) // -#define DEBUG_DRAW_SPHERE_RESOLUTION 64 -#define DEBUG_DRAW_SPHERE_LINES_COUNT (DEBUG_DRAW_SPHERE_RESOLUTION + 1) * 3 -#define DEBUG_DRAW_SPHERE_VERTICES DEBUG_DRAW_SPHERE_LINES_COUNT * 2 +#define DEBUG_DRAW_SPHERE_LOD0_RESOLUTION 64 +#define DEBUG_DRAW_SPHERE_LOD0_SCREEN_SIZE 0.2f +#define DEBUG_DRAW_SPHERE_LOD1_RESOLUTION 16 +#define DEBUG_DRAW_SPHERE_LOD1_SCREEN_SIZE 0.08f +#define DEBUG_DRAW_SPHERE_LOD2_RESOLUTION 8 // #define DEBUG_DRAW_CIRCLE_RESOLUTION 32 #define DEBUG_DRAW_CIRCLE_LINES_COUNT DEBUG_DRAW_CIRCLE_RESOLUTION @@ -44,6 +47,39 @@ // #define DEBUG_DRAW_TRIANGLE_SPHERE_RESOLUTION 12 +struct DebugSphereCache +{ + Array> Vertices; + + void Init(int32 resolution) + { + const int32 verticesCount = (resolution + 1) * 3 * 2; + Vertices.Resize(verticesCount); + int32 index = 0; + const float step = TWO_PI / (float)resolution; + for (float a = 0.0f; a < TWO_PI; a += step) + { + // Calculate sines and cosines + const float sinA = Math::Sin(a); + const float cosA = Math::Cos(a); + const float sinB = Math::Sin(a + step); + const float cosB = Math::Cos(a + step); + + // XY loop + Vertices[index++] = Vector3(cosA, sinA, 0.0f); + Vertices[index++] = Vector3(cosB, sinB, 0.0f); + + // XZ loop + Vertices[index++] = Vector3(cosA, 0.0f, sinA); + Vertices[index++] = Vector3(cosB, 0.0f, sinB); + + // YZ loop + Vertices[index++] = Vector3(0.0f, cosA, sinA); + Vertices[index++] = Vector3(0.0f, cosB, sinB); + } + } +}; + struct DebugLine { Vector3 Start; @@ -248,6 +284,8 @@ struct DebugDrawContext { DebugDrawData DebugDrawDefault; DebugDrawData DebugDrawDepthTest; + Vector3 LastViewPos = Vector3::Zero; + Matrix LastViewProj = Matrix::Identity; }; namespace @@ -263,9 +301,9 @@ namespace PsData DebugDrawPsTrianglesDefault; PsData DebugDrawPsTrianglesDepthTest; DynamicVertexBuffer* DebugDrawVB = nullptr; - Vector3 SphereCache[DEBUG_DRAW_SPHERE_VERTICES]; Vector3 CircleCache[DEBUG_DRAW_CIRCLE_VERTICES]; Array SphereTriangleCache; + DebugSphereCache SphereCache[3]; }; extern int32 BoxTrianglesIndicesCache[]; @@ -396,32 +434,13 @@ bool DebugDrawService::Init() Context = &GlobalContext; // Init wireframe sphere cache - int32 index = 0; - float step = TWO_PI / DEBUG_DRAW_SPHERE_RESOLUTION; - for (float a = 0.0f; a < TWO_PI; a += step) - { - // Calculate sines and cosines - float sinA = Math::Sin(a); - float cosA = Math::Cos(a); - float sinB = Math::Sin(a + step); - float cosB = Math::Cos(a + step); - - // XY loop - SphereCache[index++] = Vector3(cosA, sinA, 0.0f); - SphereCache[index++] = Vector3(cosB, sinB, 0.0f); - - // XZ loop - SphereCache[index++] = Vector3(cosA, 0.0f, sinA); - SphereCache[index++] = Vector3(cosB, 0.0f, sinB); - - // YZ loop - SphereCache[index++] = Vector3(0.0f, cosA, sinA); - SphereCache[index++] = Vector3(0.0f, cosB, sinB); - } + SphereCache[0].Init(DEBUG_DRAW_SPHERE_LOD0_RESOLUTION); + SphereCache[1].Init(DEBUG_DRAW_SPHERE_LOD1_RESOLUTION); + SphereCache[2].Init(DEBUG_DRAW_SPHERE_LOD2_RESOLUTION); // Init wireframe circle cache - index = 0; - step = TWO_PI / DEBUG_DRAW_CIRCLE_RESOLUTION; + int32 index = 0; + float step = TWO_PI / (float)DEBUG_DRAW_CIRCLE_RESOLUTION; for (float a = 0.0f; a < TWO_PI; a += step) { // Calculate sines and cosines @@ -648,6 +667,8 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe if (renderContext.Buffers == nullptr || !DebugDrawVB) return; auto context = GPUDevice::Instance->GetMainContext(); + Context->LastViewPos = renderContext.View.Position; + Context->LastViewProj = renderContext.View.Projection; // Fallback to task buffers if (target == nullptr && renderContext.Task) @@ -923,21 +944,6 @@ void DebugDraw::DrawBezier(const Vector3& p1, const Vector3& p2, const Vector3& } } -#define DRAW_WIRE_BOX_LINE(i0, i1, list) l.Start = corners[i0]; l.End = corners[i1]; Context->list.Add(l) -#define DRAW_WIRE_BOX(list) \ - DRAW_WIRE_BOX_LINE(0, 1, list); \ - DRAW_WIRE_BOX_LINE(0, 3, list); \ - DRAW_WIRE_BOX_LINE(0, 4, list); \ - DRAW_WIRE_BOX_LINE(1, 2, list); \ - DRAW_WIRE_BOX_LINE(1, 5, list); \ - DRAW_WIRE_BOX_LINE(2, 3, list); \ - DRAW_WIRE_BOX_LINE(2, 6, list); \ - DRAW_WIRE_BOX_LINE(3, 7, list); \ - DRAW_WIRE_BOX_LINE(4, 5, list); \ - DRAW_WIRE_BOX_LINE(4, 7, list); \ - DRAW_WIRE_BOX_LINE(5, 6, list); \ - DRAW_WIRE_BOX_LINE(6, 7, list) - void DebugDraw::DrawWireBox(const BoundingBox& box, const Color& color, float duration, bool depthTest) { // Get corners @@ -1036,26 +1042,37 @@ void DebugDraw::DrawWireBox(const OrientedBoundingBox& box, const Color& color, void DebugDraw::DrawWireSphere(const BoundingSphere& sphere, const Color& color, float duration, bool depthTest) { + // Select LOD + int32 index; + const float screenRadiusSquared = RenderTools::ComputeBoundsScreenRadiusSquared(sphere.Center, sphere.Radius, Context->LastViewPos, Context->LastViewProj); + if (screenRadiusSquared > DEBUG_DRAW_SPHERE_LOD0_SCREEN_SIZE * DEBUG_DRAW_SPHERE_LOD0_SCREEN_SIZE * 0.25f) + index = 0; + else if (screenRadiusSquared > DEBUG_DRAW_SPHERE_LOD1_SCREEN_SIZE * DEBUG_DRAW_SPHERE_LOD1_SCREEN_SIZE * 0.25f) + index = 1; + else + index = 2; + auto& cache = SphereCache[index]; + // Draw lines of the unit sphere after linear transform auto& debugDrawData = depthTest ? Context->DebugDrawDepthTest : Context->DebugDrawDefault; if (duration > 0) { DebugLine l = { Vector3::Zero, Vector3::Zero, Color32(color), duration }; - for (int32 i = 0; i < DEBUG_DRAW_SPHERE_VERTICES;) + for (int32 i = 0; i < cache.Vertices.Count();) { - l.Start = sphere.Center + SphereCache[i++] * sphere.Radius; - l.End = sphere.Center + SphereCache[i++] * sphere.Radius; + l.Start = sphere.Center + cache.Vertices.Get()[i++] * sphere.Radius; + l.End = sphere.Center + cache.Vertices.Get()[i++] * sphere.Radius; debugDrawData.DefaultLines.Add(l); } } else { Vertex l = { Vector3::Zero, Color32(color) }; - for (int32 i = 0; i < DEBUG_DRAW_SPHERE_VERTICES;) + for (int32 i = 0; i < cache.Vertices.Count();) { - l.Position = sphere.Center + SphereCache[i++] * sphere.Radius; + l.Position = sphere.Center + cache.Vertices.Get()[i++] * sphere.Radius; debugDrawData.OneFrameLines.Add(l); - l.Position = sphere.Center + SphereCache[i++] * sphere.Radius; + l.Position = sphere.Center + cache.Vertices.Get()[i++] * sphere.Radius; debugDrawData.OneFrameLines.Add(l); } } @@ -1263,7 +1280,8 @@ void DebugDraw::DrawWireTube(const Vector3& position, const Quaternion& orientat else { // Set up - const float step = TWO_PI / DEBUG_DRAW_SPHERE_RESOLUTION; + const int32 resolution = 64; + const float step = TWO_PI / (float)resolution; radius = Math::Max(radius, 0.05f); length = Math::Max(length, 0.05f); const float halfLength = length / 2.0f; From bbbcfb707d31a7a0af0e1ba94e96b823ab5bfb0b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 14 Jun 2021 14:28:16 +0200 Subject: [PATCH 163/198] Fix crash on null type --- Source/Engine/Scripting/Scripting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 37a009ebc..2888497c7 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -865,7 +865,7 @@ bool Scripting::IsEveryAssemblyLoaded() bool Scripting::IsTypeFromGameScripts(MClass* type) { - const auto binaryModule = ManagedBinaryModule::GetModule(type->GetAssembly()); + const auto binaryModule = ManagedBinaryModule::GetModule(type ? type->GetAssembly() : nullptr); return binaryModule && binaryModule != GetBinaryModuleCorlib() && binaryModule != GetBinaryModuleFlaxEngine(); } From 90105c421d19727dcd7ebc32ff9f1fe4f5f2ca92 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 14 Jun 2021 14:30:54 +0200 Subject: [PATCH 164/198] Fix Visual Script method override node auto-size --- Source/Editor/Surface/Archetypes/Function.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Function.cs b/Source/Editor/Surface/Archetypes/Function.cs index 4e017248a..141028081 100644 --- a/Source/Editor/Surface/Archetypes/Function.cs +++ b/Source/Editor/Surface/Archetypes/Function.cs @@ -605,8 +605,7 @@ namespace FlaxEditor.Surface.Archetypes MakeBox(0, string.Empty, typeof(void), true); } - // Update size - Resize(Style.Current.FontLarge.MeasureText(Title).X + 30, 20 + (_parameters?.Length * 20 ?? 0)); + ResizeAuto(); } private void MakeBox(int id, string text, Type type, bool single = false) From f5cdb0abfd6e4f32168e43396e52da10ee968525 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Mon, 14 Jun 2021 16:10:08 +0200 Subject: [PATCH 165/198] Add Delay node to Visual Scripting --- Source/Editor/Surface/Archetypes/Flow.cs | 15 ++++ Source/Editor/Surface/Archetypes/Tools.cs | 10 ++- Source/Engine/Content/Assets/VisualScript.cpp | 76 +++++++++++++++++++ Source/Engine/Content/Assets/VisualScript.h | 2 + Source/Engine/Engine/Engine.cpp | 2 +- Source/Engine/Threading/MainThreadTask.cpp | 63 ++++++++++++--- Source/Engine/Threading/MainThreadTask.h | 49 ++---------- Source/Engine/Threading/Task.cpp | 39 ---------- Source/Engine/Threading/Task.h | 58 +++----------- 9 files changed, 170 insertions(+), 144 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Flow.cs b/Source/Editor/Surface/Archetypes/Flow.cs index ffcfb719a..d7fc5dcc2 100644 --- a/Source/Editor/Surface/Archetypes/Flow.cs +++ b/Source/Editor/Surface/Archetypes/Flow.cs @@ -293,6 +293,21 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(1, "Value", true, null, 1), } }, + new NodeArchetype + { + TypeID = 6, + Title = "Delay", + Description = "Delays the graph execution. If delay is 0 then it will pass though.", + Flags = NodeFlags.VisualScriptGraph, + Size = new Vector2(150, 40), + DefaultValues = new object[] { 1.0f }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0), + NodeElementArchetype.Factory.Input(1, "Duration", true, typeof(float), 1, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 2, true), + } + }, }; } } diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs index 005af20cc..645a243f6 100644 --- a/Source/Editor/Surface/Archetypes/Tools.cs +++ b/Source/Editor/Surface/Archetypes/Tools.cs @@ -677,19 +677,21 @@ namespace FlaxEditor.Surface.Archetypes /// public ThisNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) : base(id, context, nodeArch, groupArch) - {} + { + } /// public override void OnLoaded() { base.OnLoaded(); - var vss = (VisualScriptSurface)this.Context.Surface; - var type = TypeUtils.GetType(vss.Script.ScriptTypeName); + + var surface = (VisualScriptSurface)Context.Surface; + var type = TypeUtils.GetType(surface.Script.ScriptTypeName); var box = (OutputBox)GetBox(0); box.CurrentType = type ? type : new ScriptType(typeof(VisualScript)); } } - + private class AssetReferenceNode : SurfaceNode { /// diff --git a/Source/Engine/Content/Assets/VisualScript.cpp b/Source/Engine/Content/Assets/VisualScript.cpp index 2da76a840..ea8f6ad6c 100644 --- a/Source/Engine/Content/Assets/VisualScript.cpp +++ b/Source/Engine/Content/Assets/VisualScript.cpp @@ -19,6 +19,7 @@ #include "Engine/Serialization/JsonWriter.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Utilities/StringConverter.h" +#include "Engine/Threading/MainThreadTask.h" #include "FlaxEngine.Gen.h" namespace @@ -83,6 +84,41 @@ VisualScriptExecutor::VisualScriptExecutor() _perGroupProcessCall[17] = (ProcessBoxHandler)&VisualScriptExecutor::ProcessGroupFlow; } +void VisualScriptExecutor::Invoke(const Guid& scriptId, int32 nodeId, int32 boxId, const Guid& instanceId, Variant& result) const +{ + auto script = Content::Load(scriptId); + if (!script) + return; + const auto node = script->Graph.GetNode(nodeId); + if (!node) + return; + const auto box = node->GetBox(boxId); + if (!box) + return; + auto instance = Scripting::FindObject(instanceId); + + // Add to the calling stack + VisualScripting::ScopeContext scope; + auto& stack = ThreadStacks.Get(); + VisualScripting::StackFrame frame; + frame.Script = script; + frame.Node = node; + frame.Box = box; + frame.Instance = instance; + frame.PreviousFrame = stack.Stack; + frame.Scope = &scope; + stack.Stack = &frame; + stack.StackFramesCount++; + + // Call per group custom processing event + const auto func = VisualScriptingExecutor._perGroupProcessCall[node->GroupID]; + (VisualScriptingExecutor.*func)(box, node, result); + + // Remove from the calling stack + stack.StackFramesCount--; + stack.Stack = frame.PreviousFrame; +} + VisjectExecutor::Value VisualScriptExecutor::eatBox(Node* caller, Box* box) { // Check if graph is looped or is too deep @@ -1273,6 +1309,46 @@ void VisualScriptExecutor::ProcessGroupFlow(Box* boxBase, Node* node, Value& val } } break; + } + // Delay + case 6: + { + boxBase = node->GetBox(2); + if (!boxBase->HasConnection()) + break; + const float duration = (float)tryGetValue(node->GetBox(1), node->Values[0]); + if (duration > ZeroTolerance) + { + class DelayTask : public MainThreadTask + { + public: + Guid Script; + Guid Instance; + int32 Node; + int32 Box; + + protected: + bool Run() override + { + Variant result; + VisualScriptingExecutor.Invoke(Script, Node, Box, Instance, result); + return false; + } + }; + const auto& stack = ThreadStacks.Get().Stack; + auto task = New(); + task->Script = stack->Script->GetID();; + task->Instance = stack->Instance->GetID();; + task->Node = ((Node*)boxBase->FirstConnection()->Parent)->ID; + task->Box = boxBase->FirstConnection()->ID; + task->InitialDelay = duration; + task->Start(); + } + else + { + eatBox(node, boxBase->FirstConnection()); + } + break; } } } diff --git a/Source/Engine/Content/Assets/VisualScript.h b/Source/Engine/Content/Assets/VisualScript.h index 1a1201191..27ee21b18 100644 --- a/Source/Engine/Content/Assets/VisualScript.h +++ b/Source/Engine/Content/Assets/VisualScript.h @@ -36,6 +36,8 @@ public: /// VisualScriptExecutor(); + void Invoke(const Guid& scriptId, int32 nodeId, int32 boxId, const Guid& instanceId, Variant& result) const; + private: Value eatBox(Node* caller, Box* box) override; Graph* GetCurrentGraph() const override; diff --git a/Source/Engine/Engine/Engine.cpp b/Source/Engine/Engine/Engine.cpp index bfa2338c6..020f53faa 100644 --- a/Source/Engine/Engine/Engine.cpp +++ b/Source/Engine/Engine/Engine.cpp @@ -288,7 +288,7 @@ void Engine::OnUpdate() // Simulate lags //Platform::Sleep(100); - MainThreadTask::RunAll(); + MainThreadTask::RunAll(Time::Update.UnscaledDeltaTime.GetTotalSeconds()); // Call event Update(); diff --git a/Source/Engine/Threading/MainThreadTask.cpp b/Source/Engine/Threading/MainThreadTask.cpp index e6199986c..29bd60dc3 100644 --- a/Source/Engine/Threading/MainThreadTask.cpp +++ b/Source/Engine/Threading/MainThreadTask.cpp @@ -1,25 +1,68 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "MainThreadTask.h" -#include "ConcurrentTaskQueue.h" +#include "Engine/Platform/CriticalSection.h" #include "Engine/Profiler/ProfilerCPU.h" -ConcurrentTaskQueue MainThreadTasks; - -void MainThreadTask::RunAll() +namespace { - // TODO: use bulk dequeue + CriticalSection Locker; + Array Waiting; + Array Queue; +} +void MainThreadTask::RunAll(float dt) +{ PROFILE_CPU(); - - MainThreadTask* task; - while (MainThreadTasks.try_dequeue(task)) + Locker.Lock(); + for (int32 i = Waiting.Count() - 1; i >= 0; i--) { - task->Execute(); + auto task = Waiting[i]; + task->InitialDelay -= dt; + if (task->InitialDelay < ZeroTolerance) + { + Waiting.RemoveAt(i); + Queue.Add(task); + } } + for (int32 i = 0; i < Queue.Count(); i++) + { + Queue[i]->Execute(); + } + Queue.Clear(); + Locker.Unlock(); +} + +String MainThreadTask::ToString() const +{ + return String::Format(TEXT("Main Thread Task ({0})"), ::ToString(GetState())); } void MainThreadTask::Enqueue() { - MainThreadTasks.Add(this); + Locker.Lock(); + if (InitialDelay <= ZeroTolerance) + Queue.Add(this); + else + Waiting.Add(this); + Locker.Unlock(); +} + +bool MainThreadActionTask::Run() +{ + if (_action1.IsBinded()) + { + _action1(); + return false; + } + if (_action2.IsBinded()) + { + return _action2(); + } + return true; +} + +bool MainThreadActionTask::HasReference(Object* obj) const +{ + return obj == _target; } diff --git a/Source/Engine/Threading/MainThreadTask.h b/Source/Engine/Threading/MainThreadTask.h index 625c20c70..254733d9d 100644 --- a/Source/Engine/Threading/MainThreadTask.h +++ b/Source/Engine/Threading/MainThreadTask.h @@ -24,36 +24,20 @@ class FLAXENGINE_API MainThreadTask : public Task { friend class Engine; - -protected: - - /// - /// Initializes a new instance of the class. - /// - MainThreadTask() - : Task() - { - } - private: + static void RunAll(float dt); + +public: /// - /// Runs all main thread tasks. Called only by the Engine class. + /// The initial time delay (in seconds) before task execution. Use 0 to skip this feature. /// - static void RunAll(); + float InitialDelay = 0.0f; public: // [Task] - String ToString() const override - { - return String::Format(TEXT("Main Thread Task ({0})"), ::ToString(GetState())); - } - - bool HasReference(Object* obj) const override - { - return false; - } + String ToString() const override; protected: @@ -127,27 +111,10 @@ public: protected: // [MainThreadTask] - bool Run() override - { - if (_action1.IsBinded()) - { - _action1(); - return false; - } - - if (_action2.IsBinded()) - { - return _action2(); - } - - return true; - } + bool Run() override; public: // [MainThreadTask] - bool HasReference(Object* obj) const override - { - return obj == _target; - } + bool HasReference(Object* obj) const override; }; diff --git a/Source/Engine/Threading/Task.cpp b/Source/Engine/Threading/Task.cpp index 1c4ad3e1e..f01d4b9e5 100644 --- a/Source/Engine/Threading/Task.cpp +++ b/Source/Engine/Threading/Task.cpp @@ -108,45 +108,6 @@ Task* Task::ContinueWith(Function action, Object* target) return ContinueWith(New(action, target)); } -Task* Task::Delay(int32 milliseconds) -{ - class DelayTask : public ThreadPoolTask - { - private: - - int32 _milliseconds; - DateTime _startTimeUTC; - - public: - - DelayTask(int32 milliseconds) - : _milliseconds(milliseconds) - { - } - - protected: - - // [ThreadPoolTask] - bool Run() override - { - // Take into account the different between task enqueue (OnStart event) and the actual task execution - auto diff = DateTime::NowUTC() - _startTimeUTC; - auto ms = Math::Max(0, _milliseconds - (int32)diff.GetTotalMilliseconds()); - - Platform::Sleep(ms); - - return false; - } - - void OnStart() override - { - _startTimeUTC = DateTime::NowUTC(); - } - }; - - return New(milliseconds); -} - Task* Task::StartNew(Task* task) { ASSERT(task); diff --git a/Source/Engine/Threading/Task.h b/Source/Engine/Threading/Task.h index 119529f9f..7004c130e 100644 --- a/Source/Engine/Threading/Task.h +++ b/Source/Engine/Threading/Task.h @@ -45,36 +45,23 @@ protected: /// /// The cancel flag used to indicate that there is request to cancel task operation. /// - volatile int64 _cancelFlag; + volatile int64 _cancelFlag = 0; /// /// The current task state. /// - volatile TaskState _state; + volatile TaskState _state = TaskState::Created; /// /// The task to start after finish. /// - Task* _continueWith; - -protected: - - /// - /// Initializes a new instance of the class. - /// - Task() - : _cancelFlag(0) - , _state(TaskState::Created) - , _continueWith(nullptr) - { - } + Task* _continueWith = nullptr; public: /// - /// Gets work state + /// Gets the task state. /// - /// State FORCE_INLINE TaskState GetState() const { return static_cast(Platform::AtomicRead((int64 volatile*)&_state)); @@ -93,7 +80,6 @@ public: /// /// Gets the task to start after this one. /// - /// The next task. FORCE_INLINE Task* GetContinueWithTask() const { return _continueWith; @@ -102,45 +88,40 @@ public: public: /// - /// Checks if operation failed + /// Checks if operation failed. /// - /// True if operation failed, otherwise false FORCE_INLINE bool IsFailed() const { return GetState() == TaskState::Failed; } /// - /// Checks if operation has been canceled + /// Checks if operation has been canceled. /// - /// True if operation has been canceled, otherwise false FORCE_INLINE bool IsCanceled() const { return GetState() == TaskState::Canceled; } /// - /// Checks if operation has been queued + /// Checks if operation has been queued. /// - /// True if operation has been queued, otherwise false FORCE_INLINE bool IsQueued() const { return GetState() == TaskState::Queued; } /// - /// Checks if operation is running + /// Checks if operation is running. /// - /// True if operation is running, otherwise false FORCE_INLINE bool IsRunning() const { return GetState() == TaskState::Running; } /// - /// Checks if operation has been finished + /// Checks if operation has been finished. /// - /// True if operation has been finished, otherwise false FORCE_INLINE bool IsFinished() const { return GetState() == TaskState::Finished; @@ -149,7 +130,6 @@ public: /// /// Checks if operation has been ended (via cancel, fail or finish). /// - /// True if operation has been ended, otherwise false bool IsEnded() const { auto state = GetState(); @@ -159,7 +139,6 @@ public: /// /// Returns true if task has been requested to cancel it's operation. /// - /// True if task has been canceled and should stop it's work without calling child task start. FORCE_INLINE bool IsCancelRequested() { return Platform::AtomicRead(&_cancelFlag) != 0; @@ -244,25 +223,6 @@ public: /// Enqueued task. Task* ContinueWith(Function action, Object* target = nullptr); -public: - - /// - /// Creates a task that completes after a specified time interval (not started). - /// - /// The time span to wait before completing the returned task. - /// A task that represents the time delay (not started). - FORCE_INLINE static Task* Delay(const TimeSpan& delay) - { - return Delay(static_cast(delay.GetTotalMilliseconds())); - } - - /// - /// Creates a task that completes after a specified time interval (not started). - /// - /// The amount of milliseconds to wait before completing the returned task. - /// A task that represents the time delay (not started). - static Task* Delay(int32 milliseconds); - public: /// From a75e403b35a9365678c439a3c0ffc63e68f43210 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 15 Jun 2021 13:51:16 +0200 Subject: [PATCH 166/198] Fixes --- Source/Engine/Content/Asset.cpp | 10 ++++++---- Source/Engine/Content/AssetReference.h | 17 +++++++++++++++++ Source/Engine/Content/WeakAssetReference.h | 17 +++++++++++++++++ Source/Engine/Render2D/FontManager.cpp | 2 +- .../UI/GUI/Common/RenderToTextureControl.cs | 2 ++ Source/Engine/UI/GUI/Common/RichTextBoxBase.cs | 9 +++++---- 6 files changed, 48 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index 4614f33cf..e8536f58d 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -20,7 +20,6 @@ AssetReferenceBase::~AssetReferenceBase() _asset->OnLoaded.Unbind(this); _asset->OnUnloaded.Unbind(this); _asset->RemoveReference(); - _asset = nullptr; } } @@ -55,13 +54,15 @@ void AssetReferenceBase::OnSet(Asset* asset) void AssetReferenceBase::OnLoaded(Asset* asset) { - ASSERT(_asset == asset); + if (_asset != asset) + return; Loaded(); } void AssetReferenceBase::OnUnloaded(Asset* asset) { - ASSERT(_asset == asset); + if (_asset != asset) + return; Unload(); OnSet(nullptr); } @@ -92,7 +93,8 @@ void WeakAssetReferenceBase::OnSet(Asset* asset) void WeakAssetReferenceBase::OnUnloaded(Asset* asset) { - ASSERT(_asset == asset); + if (_asset != asset) + return; Unload(); asset->OnUnloaded.Unbind(this); _asset = nullptr; diff --git a/Source/Engine/Content/AssetReference.h b/Source/Engine/Content/AssetReference.h index 68adf4a25..90f472fd6 100644 --- a/Source/Engine/Content/AssetReference.h +++ b/Source/Engine/Content/AssetReference.h @@ -35,6 +35,7 @@ public: EventType Changed; public: + NON_COPYABLE(AssetReferenceBase); /// /// Initializes a new instance of the class. @@ -114,6 +115,22 @@ public: OnSet(other.Get()); } + AssetReference(AssetReference&& other) + { + OnSet(other.Get()); + other.OnSet(nullptr); + } + + AssetReference& operator=(AssetReference&& other) + { + if (&other != this) + { + OnSet(other.Get()); + other.OnSet(nullptr); + } + return *this; + } + /// /// Finalizes an instance of the class. /// diff --git a/Source/Engine/Content/WeakAssetReference.h b/Source/Engine/Content/WeakAssetReference.h index 7e4202fe4..678e2b998 100644 --- a/Source/Engine/Content/WeakAssetReference.h +++ b/Source/Engine/Content/WeakAssetReference.h @@ -25,6 +25,7 @@ public: EventType Unload; public: + NON_COPYABLE(WeakAssetReferenceBase); /// /// Initializes a new instance of the class. @@ -100,6 +101,22 @@ public: OnSet(other.Get()); } + WeakAssetReference(WeakAssetReference&& other) + { + OnSet(other.Get()); + other.OnSet(nullptr); + } + + WeakAssetReference& operator=(WeakAssetReference&& other) + { + if (&other != this) + { + OnSet(other.Get()); + other.OnSet(nullptr); + } + return *this; + } + /// /// Finalizes an instance of the class. /// diff --git a/Source/Engine/Render2D/FontManager.cpp b/Source/Engine/Render2D/FontManager.cpp index a8e16f715..79ab045da 100644 --- a/Source/Engine/Render2D/FontManager.cpp +++ b/Source/Engine/Render2D/FontManager.cpp @@ -109,7 +109,7 @@ void FontManagerService::Dispose() FontTextureAtlas* FontManager::GetAtlas(int32 index) { - return index >= 0 && index < Atlases.Count() ? Atlases.Get()[index] : nullptr; + return index >= 0 && index < Atlases.Count() ? Atlases.Get()[index].Get() : nullptr; } bool FontManager::AddNewEntry(Font* font, Char c, FontCharacterEntry& entry) diff --git a/Source/Engine/UI/GUI/Common/RenderToTextureControl.cs b/Source/Engine/UI/GUI/Common/RenderToTextureControl.cs index e2e1532f3..5e3978cd7 100644 --- a/Source/Engine/UI/GUI/Common/RenderToTextureControl.cs +++ b/Source/Engine/UI/GUI/Common/RenderToTextureControl.cs @@ -95,6 +95,8 @@ namespace FlaxEngine.GUI return; } } + if (!_texture || !_texture.IsAllocated) + return; Profiler.BeginEventGPU("RenderToTextureControl"); var context = GPUDevice.Instance.MainContext; diff --git a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs index 0dcfdd797..2cfe11fff 100644 --- a/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs +++ b/Source/Engine/UI/GUI/Common/RichTextBoxBase.cs @@ -241,11 +241,12 @@ namespace FlaxEngine.GUI // Calculate text blocks for drawing var textBlocks = Utils.ExtractArrayFromList(_textBlocks); + var textBlocksCount = _textBlocks?.Count ?? 0; var hasSelection = HasSelection; var selection = new TextRange(SelectionLeft, SelectionRight); var viewRect = new Rectangle(_viewOffset, Size).MakeExpanded(10.0f); - var firstTextBlock = _textBlocks.Count; - for (int i = 0; i < _textBlocks.Count; i++) + var firstTextBlock = textBlocksCount; + for (int i = 0; i < textBlocksCount; i++) { ref TextBlock textBlock = ref textBlocks[i]; if (textBlock.Bounds.Intersects(ref viewRect)) @@ -254,8 +255,8 @@ namespace FlaxEngine.GUI break; } } - var endTextBlock = Mathf.Min(firstTextBlock + 1, _textBlocks.Count); - for (int i = _textBlocks.Count - 1; i > firstTextBlock; i--) + var endTextBlock = Mathf.Min(firstTextBlock + 1, textBlocksCount); + for (int i = textBlocksCount - 1; i > firstTextBlock; i--) { ref TextBlock textBlock = ref textBlocks[i]; if (textBlock.Bounds.Intersects(ref viewRect)) From 8816e4403de4ad97507dc6ab88481170b110cf78 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 15 Jun 2021 16:38:21 +0200 Subject: [PATCH 167/198] Remove UI Controls from Json Asset picker dialog --- Source/Editor/Content/Proxy/JsonAssetProxy.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Content/Proxy/JsonAssetProxy.cs b/Source/Editor/Content/Proxy/JsonAssetProxy.cs index 41cc06ee1..5029c2eb4 100644 --- a/Source/Editor/Content/Proxy/JsonAssetProxy.cs +++ b/Source/Editor/Content/Proxy/JsonAssetProxy.cs @@ -92,6 +92,7 @@ namespace FlaxEditor.Content !type.IsAbstract && !type.IsGenericType && type.Type.GetConstructor(Type.EmptyTypes) != null && + !typeof(FlaxEngine.GUI.Control).IsAssignableFrom(type.Type) && !typeof(FlaxEngine.Object).IsAssignableFrom(type.Type); } } From c91c8b66cee7bb9a8a99840dfa696a143bb8dee9 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 15 Jun 2021 16:39:15 +0200 Subject: [PATCH 168/198] Add `Category` attribute for types grouping in editor dialogs --- .../CustomEditors/Dedicated/ScriptsEditor.cs | 14 +--- .../Dedicated/UIControlEditor.cs | 15 +--- Source/Editor/GUI/ClickableLabel.cs | 1 + Source/Editor/GUI/ItemsListContextMenu.cs | 79 +++++++++++++++---- Source/Editor/GUI/Popups/TypeSearchPopup.cs | 15 ++++ Source/Editor/Surface/Archetypes/Particles.cs | 4 +- Source/Editor/Surface/VisjectSurfaceWindow.cs | 13 +-- .../Attributes/Editor/CategoryAttribute.cs | 27 +++++++ 8 files changed, 116 insertions(+), 52 deletions(-) create mode 100644 Source/Engine/Scripting/Attributes/Editor/CategoryAttribute.cs diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index ffe4ce585..8a6ae54a9 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -68,19 +68,7 @@ namespace FlaxEditor.CustomEditors.Dedicated var cm = new ItemsListContextMenu(180); for (int i = 0; i < scripts.Count; i++) { - var scriptType = scripts[i]; - var item = new ItemsListContextMenu.Item(scriptType.Name, scriptType) - { - TooltipText = scriptType.TypeName, - }; - var attributes = scriptType.GetAttributes(false); - var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute); - if (tooltipAttribute != null) - { - item.TooltipText += '\n'; - item.TooltipText += tooltipAttribute.Text; - } - cm.AddItem(item); + cm.AddItem(new TypeSearchPopup.TypeItemView(scripts[i])); } cm.ItemClicked += item => AddScript((ScriptType)item.Tag); cm.SortChildren(); diff --git a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs index b860d9826..d7a6f2617 100644 --- a/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/UIControlEditor.cs @@ -639,21 +639,8 @@ namespace FlaxEditor.CustomEditors.Dedicated var cm = new ItemsListContextMenu(180); for (int i = 0; i < controlTypes.Count; i++) { - var controlType = controlTypes[i]; - var item = new ItemsListContextMenu.Item(controlType.Name, controlType) - { - TooltipText = controlType.TypeName, - }; - var attributes = controlType.GetAttributes(false); - var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute); - if (tooltipAttribute != null) - { - item.TooltipText += '\n'; - item.TooltipText += tooltipAttribute.Text; - } - cm.AddItem(item); + cm.AddItem(new TypeSearchPopup.TypeItemView(controlTypes[i])); } - cm.ItemClicked += controlType => SetType((ScriptType)controlType.Tag); cm.SortChildren(); cm.Show(button.Parent, button.BottomLeft); diff --git a/Source/Editor/GUI/ClickableLabel.cs b/Source/Editor/GUI/ClickableLabel.cs index f8940e714..325d8822a 100644 --- a/Source/Editor/GUI/ClickableLabel.cs +++ b/Source/Editor/GUI/ClickableLabel.cs @@ -10,6 +10,7 @@ namespace FlaxEditor.GUI /// The label that contains events for mouse interaction. /// /// + [HideInEditor] public class ClickableLabel : Label { private bool _leftClick; diff --git a/Source/Editor/GUI/ItemsListContextMenu.cs b/Source/Editor/GUI/ItemsListContextMenu.cs index 8c4dd723d..4abe39f0b 100644 --- a/Source/Editor/GUI/ItemsListContextMenu.cs +++ b/Source/Editor/GUI/ItemsListContextMenu.cs @@ -32,9 +32,14 @@ namespace FlaxEditor.GUI protected List _highlights; /// - /// Gets or sets the name. + /// The item name. /// - public string Name { get; set; } + public string Name; + + /// + /// The item category name (optional). + /// + public string Category; /// /// Occurs when items gets clicked by the user. @@ -49,18 +54,6 @@ namespace FlaxEditor.GUI { } - /// - /// Initializes a new instance of the class. - /// - /// The item name. - /// The item tag object. - public Item(string name, object tag = null) - : base(0, 0, 120, 12) - { - Name = name; - Tag = tag; - } - /// /// Updates the filter. /// @@ -181,6 +174,7 @@ namespace FlaxEditor.GUI } private readonly TextBox _searchBox; + private List _categoryPanels; private bool _waitingForInput; /// @@ -242,6 +236,23 @@ namespace FlaxEditor.GUI if (items[i] is Item item) item.UpdateFilter(_searchBox.Text); } + if (_categoryPanels != null) + { + for (int i = 0; i < _categoryPanels.Count; i++) + { + var category = _categoryPanels[i]; + bool anyVisible = false; + for (int j = 0; j < category.Children.Count; j++) + { + if (category.Children[j] is Item item2) + { + item2.UpdateFilter(_searchBox.Text); + anyVisible |= item2.Visible; + } + } + category.Visible = anyVisible; + } + } UnlockChildrenRecursive(); PerformLayout(true); @@ -254,8 +265,33 @@ namespace FlaxEditor.GUI /// The item. public void AddItem(Item item) { - item.Parent = ItemsPanel; item.Clicked += OnClickItem; + ContainerControl parent = ItemsPanel; + if (!string.IsNullOrEmpty(item.Category)) + { + if (_categoryPanels == null) + _categoryPanels = new List(); + for (int i = 0; i < _categoryPanels.Count; i++) + { + if (string.Equals(_categoryPanels[i].HeaderText, item.Category, StringComparison.Ordinal)) + { + parent = _categoryPanels[i]; + break; + } + } + if (parent == ItemsPanel) + { + var categoryPanel = new DropPanel + { + HeaderText = item.Category, + Parent = parent, + }; + categoryPanel.Open(false); + _categoryPanels.Add(categoryPanel); + parent = categoryPanel; + } + } + item.Parent = parent; } /// @@ -281,6 +317,19 @@ namespace FlaxEditor.GUI if (items[i] is Item item) item.UpdateFilter(null); } + if (_categoryPanels != null) + { + for (int i = 0; i < _categoryPanels.Count; i++) + { + var category = _categoryPanels[i]; + for (int j = 0; j < category.Children.Count; j++) + { + if (category.Children[j] is Item item2) + item2.UpdateFilter(null); + } + category.Visible = true; + } + } _searchBox.Clear(); UnlockChildrenRecursive(); diff --git a/Source/Editor/GUI/Popups/TypeSearchPopup.cs b/Source/Editor/GUI/Popups/TypeSearchPopup.cs index a35b2a1c5..71a97ed05 100644 --- a/Source/Editor/GUI/Popups/TypeSearchPopup.cs +++ b/Source/Editor/GUI/Popups/TypeSearchPopup.cs @@ -27,6 +27,15 @@ namespace FlaxEditor.GUI /// public ScriptType Type => _type; + /// + /// Initializes a new instance of the class. + /// + /// The type. + public TypeItemView(ScriptType type) + : this(type, type.GetAttributes(false)) + { + } + /// /// Initializes a new instance of the class. /// @@ -38,12 +47,18 @@ namespace FlaxEditor.GUI Name = type.Name; TooltipText = type.TypeName; + Tag = type; var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute); if (tooltipAttribute != null) { TooltipText += '\n'; TooltipText += tooltipAttribute.Text; } + var categoryAttribute = (CategoryAttribute)attributes.FirstOrDefault(x => x is CategoryAttribute); + if (categoryAttribute != null) + { + Category = categoryAttribute.Name; + } } /// diff --git a/Source/Editor/Surface/Archetypes/Particles.cs b/Source/Editor/Surface/Archetypes/Particles.cs index 28d6dce6a..f6fd2d003 100644 --- a/Source/Editor/Surface/Archetypes/Particles.cs +++ b/Source/Editor/Surface/Archetypes/Particles.cs @@ -118,8 +118,10 @@ namespace FlaxEditor.Surface.Archetypes var cm = new ItemsListContextMenu(180); foreach (var module in modules) { - cm.AddItem(new ItemsListContextMenu.Item(module.Title, module.TypeID) + cm.AddItem(new ItemsListContextMenu.Item { + Name = module.Title, + Tag = module.TypeID, TooltipText = module.Description, }); } diff --git a/Source/Editor/Surface/VisjectSurfaceWindow.cs b/Source/Editor/Surface/VisjectSurfaceWindow.cs index 810f125ae..119c01c30 100644 --- a/Source/Editor/Surface/VisjectSurfaceWindow.cs +++ b/Source/Editor/Surface/VisjectSurfaceWindow.cs @@ -447,18 +447,13 @@ namespace FlaxEditor.Surface var cm = new ItemsListContextMenu(180); foreach (var newParameterType in newParameterTypes) { - var name = newParameterType.Type != null ? window.VisjectSurface.GetTypeName(newParameterType) : newParameterType.Name; - var item = new ItemsListContextMenu.Item(name, newParameterType) + var item = new TypeSearchPopup.TypeItemView(newParameterType) { + Tag = newParameterType, TooltipText = newParameterType.TypeName, }; - var attributes = newParameterType.GetAttributes(false); - var tooltipAttribute = (TooltipAttribute)attributes.FirstOrDefault(x => x is TooltipAttribute); - if (tooltipAttribute != null) - { - item.TooltipText += '\n'; - item.TooltipText += tooltipAttribute.Text; - } + if (newParameterType.Type != null) + item.Name = window.VisjectSurface.GetTypeName(newParameterType); cm.AddItem(item); } cm.ItemClicked += OnAddParameterItemClicked; diff --git a/Source/Engine/Scripting/Attributes/Editor/CategoryAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/CategoryAttribute.cs new file mode 100644 index 000000000..e5f4f9794 --- /dev/null +++ b/Source/Engine/Scripting/Attributes/Editor/CategoryAttribute.cs @@ -0,0 +1,27 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using System; + +namespace FlaxEngine +{ + /// + /// Describes the category name for a type. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)] + public sealed class CategoryAttribute : Attribute + { + /// + /// The category name. + /// + public string Name; + + /// + /// Initializes a new instance of the class. + /// + /// The category name. + public CategoryAttribute(string name) + { + Name = name; + } + } +} From 2a73d18d14fc406b6bdb49ab18548310a723157e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 13 Jun 2021 16:25:18 +0200 Subject: [PATCH 169/198] Fix typo --- Source/Engine/Animations/Animations.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Animations/Animations.h b/Source/Engine/Animations/Animations.h index 014104eac..8ebc5b5b7 100644 --- a/Source/Engine/Animations/Animations.h +++ b/Source/Engine/Animations/Animations.h @@ -14,7 +14,7 @@ class Asset; /// API_CLASS(Static) class FLAXENGINE_API Animations { -DECLARE_SCRIPTING_TYPE_NO_SPAWN(Content); +DECLARE_SCRIPTING_TYPE_NO_SPAWN(Animations); /// /// The system for Animations update. From d2d8a83461995307d5e9e73ce2d2facedb830fb7 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 15 Jun 2021 23:48:00 +0200 Subject: [PATCH 170/198] Add support for multi-threaded CPU particles simulation --- .../Animations/Graph/AnimGroup.Animation.cpp | 8 +- ...rticleEmitterGraph.CPU.ParticleModules.cpp | 170 +++++++++--------- .../ParticleEmitterGraph.CPU.Particles.cpp | 77 ++++---- .../Graph/CPU/ParticleEmitterGraph.CPU.cpp | 135 ++++++-------- .../Graph/CPU/ParticleEmitterGraph.CPU.h | 107 ++++------- .../Particles/Graph/ParticleEmitterGraph.h | 32 +--- Source/Engine/Particles/ParticleEmitter.cpp | 2 +- .../Particles/ParticleEmitterFunction.cpp | 8 + Source/Engine/Particles/Particles.cpp | 7 +- .../Engine/Particles/ParticlesSimulation.cpp | 7 +- Source/Engine/Particles/ParticlesSimulation.h | 5 +- 11 files changed, 245 insertions(+), 313 deletions(-) diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index 732390112..89ee914c8 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -1580,11 +1580,11 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu #if 0 // Prevent recursive calls - for (int32 i = _callStack.Count() - 1; i >= 0; i--) + for (int32 i = context.CallStack.Count() - 1; i >= 0; i--) { - if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(9, 24)) + if (context.CallStack[i]->Type == GRAPH_NODE_MAKE_TYPE(9, 24)) { - const auto callFunc = _callStack[i]->Assets[0].Get(); + const auto callFunc = context.CallStack[i]->Assets[0].Get(); if (callFunc == function) { value = Value::Zero; @@ -1879,7 +1879,7 @@ void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& va Graph* graph; for (int32 i = context.CallStack.Count() - 1; i >= 0; i--) { - if (context.CallStack[i]->Type == GRAPH_NODE_MAKE_TYPE(9, 24) && context.Functions.TryGet(context.CallStack[i], graph) && context.GraphStack.Last() == (Graph*)graph) + if (context.CallStack[i]->Type == GRAPH_NODE_MAKE_TYPE(9, 24) && context.Functions.TryGet(context.CallStack[i], graph) && context.GraphStack.Last() == graph) { functionCallNode = (AnimGraphNode*)context.CallStack[i]; break; diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp index 4d8e02a97..3f2c58df8 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.ParticleModules.cpp @@ -103,7 +103,8 @@ namespace int32 ParticleEmitterGraphCPUExecutor::ProcessSpawnModule(int32 index) { const auto node = _graph.SpawnModules[index]; - auto& data = _data->SpawnModulesData[index]; + auto& context = Context.Get(); + auto& data = context.Data->SpawnModulesData[index]; // Accumulate the previous frame fraction float spawnCount = data.SpawnCounter; @@ -115,13 +116,13 @@ int32 ParticleEmitterGraphCPUExecutor::ProcessSpawnModule(int32 index) case 100: { const float rate = Math::Max((float)TryGetValue(node->GetBox(0), node->Values[2]), 0.0f); - spawnCount += rate * _deltaTime; + spawnCount += rate * context.DeltaTime; break; } // Single Burst case 101: { - const bool isFirstUpdate = (_data->Time - _deltaTime) <= 0.0f; + const bool isFirstUpdate = (context.Data->Time - context.DeltaTime) <= 0.0f; if (isFirstUpdate) { const float count = Math::Max((float)TryGetValue(node->GetBox(0), node->Values[2]), 0.0f); @@ -133,11 +134,11 @@ int32 ParticleEmitterGraphCPUExecutor::ProcessSpawnModule(int32 index) case 102: { float& nextSpawnTime = data.NextSpawnTime; - if (nextSpawnTime - _data->Time <= 0.0f) + if (nextSpawnTime - context.Data->Time <= 0.0f) { const float count = Math::Max((float)TryGetValue(node->GetBox(0), node->Values[2]), 0.0f); const float delay = Math::Max((float)TryGetValue(node->GetBox(1), node->Values[3]), 0.0f); - nextSpawnTime = _data->Time + delay; + nextSpawnTime = context.Data->Time + delay; spawnCount += count; } break; @@ -146,13 +147,13 @@ int32 ParticleEmitterGraphCPUExecutor::ProcessSpawnModule(int32 index) case 103: { float& nextSpawnTime = data.NextSpawnTime; - if (nextSpawnTime - _data->Time <= 0.0f) + if (nextSpawnTime - context.Data->Time <= 0.0f) { const Vector2 countMinMax = (Vector2)TryGetValue(node->GetBox(0), node->Values[2]); const Vector2 delayMinMax = (Vector2)TryGetValue(node->GetBox(1), node->Values[3]); const float count = Math::Max(countMinMax.X + RAND * (countMinMax.Y - countMinMax.X), 0.0f); const float delay = Math::Max(delayMinMax.X + RAND * (delayMinMax.Y - delayMinMax.X), 0.0f); - nextSpawnTime = _data->Time + delay; + nextSpawnTime = context.Data->Time + delay; spawnCount += count; } break; @@ -170,8 +171,9 @@ int32 ParticleEmitterGraphCPUExecutor::ProcessSpawnModule(int32 index) void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* node, int32 particlesStart, int32 particlesEnd) { - auto stride = _data->Buffer->Stride; - auto start = _data->Buffer->GetParticleCPU(particlesStart); + auto& context = Context.Get(); + auto stride = context.Data->Buffer->Stride; + auto start = context.Data->Buffer->GetParticleCPU(particlesStart); switch (node->TypeID) { @@ -181,7 +183,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { auto spriteFacingMode = node->Values[2].AsInt; { - auto& attribute = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& attribute = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* spriteFacingModePtr = start + attribute.Offset; for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { @@ -192,14 +194,14 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* if ((ParticleSpriteFacingMode)spriteFacingMode == ParticleSpriteFacingMode::CustomFacingVector || (ParticleSpriteFacingMode)spriteFacingMode == ParticleSpriteFacingMode::FixedAxis) { - auto& attribute = _data->Buffer->Layout->Attributes[node->Attributes[1]]; + auto& attribute = context.Data->Buffer->Layout->Attributes[node->Attributes[1]]; byte* customFacingVectorPtr = start + attribute.Offset; auto box = node->GetBox(0); if (node->UsePerParticleDataResolve()) { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; const Vector3 vector = (Vector3)GetValue(box, 3); *((Vector3*)customFacingVectorPtr) = vector; customFacingVectorPtr += stride; @@ -223,7 +225,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { auto modelFacingMode = node->Values[2].AsInt; { - auto& attribute = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& attribute = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* modelFacingModePtr = start + attribute.Offset; for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { @@ -236,11 +238,11 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Update Age case 300: { - auto& attribute = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& attribute = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* agePtr = start + attribute.Offset; for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - *((float*)agePtr) += _deltaTime; + *((float*)agePtr) += context.DeltaTime; agePtr += stride; } break; @@ -249,16 +251,16 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* case 301: case 304: { - auto& attribute = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& attribute = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* velocityPtr = start + attribute.Offset; auto box = node->GetBox(0); if (node->UsePerParticleDataResolve()) { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; const Vector3 force = (Vector3)GetValue(box, 2); - *((Vector3*)velocityPtr) += force * _deltaTime; + *((Vector3*)velocityPtr) += force * context.DeltaTime; velocityPtr += stride; } } @@ -267,7 +269,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* const Vector3 force = (Vector3)GetValue(box, 2); for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - *((Vector3*)velocityPtr) += force * _deltaTime; + *((Vector3*)velocityPtr) += force * context.DeltaTime; velocityPtr += stride; } } @@ -276,9 +278,9 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Conform to Sphere case 305: { - auto& position = _data->Buffer->Layout->Attributes[node->Attributes[0]]; - auto& velocity = _data->Buffer->Layout->Attributes[node->Attributes[1]]; - auto& mass = _data->Buffer->Layout->Attributes[node->Attributes[2]]; + auto& position = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& velocity = context.Data->Buffer->Layout->Attributes[node->Attributes[1]]; + auto& mass = context.Data->Buffer->Layout->Attributes[node->Attributes[2]]; byte* positionPtr = start + position.Offset; byte* velocityPtr = start + velocity.Offset; @@ -308,7 +310,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* float ratio = Math::SmoothStep(0.0f, stickDistance * 2.0f, Math::Abs(distToSurface)); \ float tgtSpeed = Math::Sign(distToSurface) * attractionSpeed * ratio; \ float deltaSpeed = tgtSpeed - spdNormal; \ - Vector3 deltaVelocity = dir * (Math::Sign(deltaSpeed) * Math::Min(Math::Abs(deltaSpeed), _deltaTime * Math::Lerp(stickForce, attractionForce, ratio)) / Math::Max(*(float*)massPtr, ZeroTolerance)); \ + Vector3 deltaVelocity = dir * (Math::Sign(deltaSpeed) * Math::Min(Math::Abs(deltaSpeed), context.DeltaTime * Math::Lerp(stickForce, attractionForce, ratio)) / Math::Max(*(float*)massPtr, ZeroTolerance)); \ *(Vector3*)velocityPtr = velocity + deltaVelocity; \ positionPtr += stride; \ velocityPtr += stride; \ @@ -318,7 +320,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -338,7 +340,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Kill (sphere) case 306: { - auto& position = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& position = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + position.Offset; @@ -356,8 +358,8 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* if (sign * lengthSqr <= sign * sphereRadiusSqr) \ { \ particlesEnd--; \ - _data->Buffer->CPU.Count--; \ - Platform::MemoryCopy(_data->Buffer->GetParticleCPU(particleIndex), _data->Buffer->GetParticleCPU(_data->Buffer->CPU.Count), _data->Buffer->Stride); \ + context.Data->Buffer->CPU.Count--; \ + Platform::MemoryCopy(context.Data->Buffer->GetParticleCPU(particleIndex), context.Data->Buffer->GetParticleCPU(context.Data->Buffer->CPU.Count), context.Data->Buffer->Stride); \ particleIndex--; \ } \ positionPtr += stride @@ -366,7 +368,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -386,7 +388,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Kill (box) case 307: { - auto& position = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& position = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + position.Offset; @@ -409,8 +411,8 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* if (collision) \ { \ particlesEnd--; \ - _data->Buffer->CPU.Count--; \ - Platform::MemoryCopy(_data->Buffer->GetParticleCPU(particleIndex), _data->Buffer->GetParticleCPU(_data->Buffer->CPU.Count), _data->Buffer->Stride); \ + context.Data->Buffer->CPU.Count--; \ + Platform::MemoryCopy(context.Data->Buffer->GetParticleCPU(particleIndex), context.Data->Buffer->GetParticleCPU(context.Data->Buffer->CPU.Count), context.Data->Buffer->Stride); \ particleIndex--; \ } \ positionPtr += stride @@ -419,7 +421,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -447,8 +449,8 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* if (kill) \ { \ particlesEnd--; \ - _data->Buffer->CPU.Count--; \ - Platform::MemoryCopy(_data->Buffer->GetParticleCPU(particleIndex), _data->Buffer->GetParticleCPU(_data->Buffer->CPU.Count), _data->Buffer->Stride); \ + context.Data->Buffer->CPU.Count--; \ + Platform::MemoryCopy(context.Data->Buffer->GetParticleCPU(particleIndex), context.Data->Buffer->GetParticleCPU(context.Data->Buffer->CPU.Count), context.Data->Buffer->Stride); \ particleIndex--; \ } @@ -456,7 +458,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -479,9 +481,9 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* auto box = node->GetBox(0); const bool useSpriteSize = node->Values[3].AsBool; - auto& velocity = _data->Buffer->Layout->Attributes[node->Attributes[0]]; - auto& mass = _data->Buffer->Layout->Attributes[node->Attributes[1]]; - byte* spriteSizePtr = useSpriteSize ? start + _data->Buffer->Layout->Attributes[node->Attributes[2]].Offset : nullptr; + auto& velocity = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& mass = context.Data->Buffer->Layout->Attributes[node->Attributes[1]]; + byte* spriteSizePtr = useSpriteSize ? start + context.Data->Buffer->Layout->Attributes[node->Attributes[2]].Offset : nullptr; byte* velocityPtr = start + velocity.Offset; byte* massPtr = start + mass.Offset; @@ -492,7 +494,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* float particleDrag = drag; \ if (useSpriteSize) \ particleDrag *= ((Vector2*)spriteSizePtr)->MulValues(); \ - *((Vector3*)velocityPtr) *= Math::Max(0.0f, 1.0f - (particleDrag * _deltaTime) / Math::Max(*(float*)massPtr, ZeroTolerance)); \ + *((Vector3*)velocityPtr) *= Math::Max(0.0f, 1.0f - (particleDrag * context.DeltaTime) / Math::Max(*(float*)massPtr, ZeroTolerance)); \ velocityPtr += stride; \ massPtr += stride; \ spriteSizePtr += stride @@ -501,7 +503,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -521,9 +523,9 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Turbulence case 311: { - auto& position = _data->Buffer->Layout->Attributes[node->Attributes[0]]; - auto& velocity = _data->Buffer->Layout->Attributes[node->Attributes[1]]; - auto& mass = _data->Buffer->Layout->Attributes[node->Attributes[2]]; + auto& position = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& velocity = context.Data->Buffer->Layout->Attributes[node->Attributes[1]]; + auto& mass = context.Data->Buffer->Layout->Attributes[node->Attributes[2]]; byte* positionPtr = start + position.Offset; byte* velocityPtr = start + velocity.Offset; @@ -551,7 +553,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* Vector3 vectorFieldUVW = Vector3::Transform(*((Vector3*)positionPtr), invFieldTransformMatrix); \ Vector3 force = Noise3D(vectorFieldUVW + 0.5f, octavesCount, roughness); \ force = Vector3::Transform(force, fieldTransformMatrix) * intensity; \ - *((Vector3*)velocityPtr) += force * (_deltaTime / Math::Max(*(float*)massPtr, ZeroTolerance)); \ + *((Vector3*)velocityPtr) += force * (context.DeltaTime / Math::Max(*(float*)massPtr, ZeroTolerance)); \ positionPtr += stride; \ velocityPtr += stride; \ massPtr += stride @@ -560,7 +562,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -581,7 +583,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* case 200: case 302: { - auto& attribute = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& attribute = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* dataPtr = start + attribute.Offset; int32 dataSize = attribute.GetSize(); auto box = node->GetBox(0); @@ -590,7 +592,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; const Value value = GetValue(box, 4).Cast(type); Platform::MemoryCopy(dataPtr, &value.AsPointer, dataSize); dataPtr += stride; @@ -637,7 +639,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* case 362: case 363: { - auto& attribute = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& attribute = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* dataPtr = start + attribute.Offset; int32 dataSize = attribute.GetSize(); auto box = node->GetBox(0); @@ -646,7 +648,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; const Value value = GetValue(box, 2).Cast(type); Platform::MemoryCopy(dataPtr, &value.AsPointer, dataSize); dataPtr += stride; @@ -666,7 +668,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (sphere surface) case 202: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -691,7 +693,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -711,7 +713,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (plane) case 203: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -729,7 +731,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -749,7 +751,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (circle) case 204: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -772,7 +774,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -792,7 +794,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (disc) case 205: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -815,7 +817,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -835,7 +837,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (box surface) case 206: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -865,7 +867,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -885,7 +887,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (box volume) case 207: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -903,7 +905,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -923,7 +925,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (cylinder) case 208: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -948,7 +950,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -968,7 +970,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (line) case 209: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -986,7 +988,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -1006,7 +1008,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (torus) case 210: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -1050,7 +1052,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -1070,7 +1072,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (sphere volume) case 211: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; byte* positionPtr = start + positionAttr.Offset; @@ -1095,7 +1097,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -1121,8 +1123,8 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Position (spiral) case 214: { - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; - auto& velocityAttr = _data->Buffer->Layout->Attributes[node->Attributes[1]]; + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; + auto& velocityAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[1]]; byte* positionPtr = start + positionAttr.Offset; byte* velocityPtr = start + velocityAttr.Offset; @@ -1131,7 +1133,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* auto rotationSpeedBox = node->GetBox(1); auto velocityScaleBox = node->GetBox(2); - auto& arc = node->SpiralModuleProgress; + auto& arc = *(float*)&context.Data->CustomData[node->CustomDataOffset]; #define INPUTS_FETCH() \ const Vector3 center = (Vector3)GetValue(centerBox, 2); \ @@ -1151,7 +1153,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -1171,9 +1173,9 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* // Helper macros for collision modules to share the code #define COLLISION_BEGIN() \ - auto& positionAttr = _data->Buffer->Layout->Attributes[node->Attributes[0]]; \ - auto& velocityAttr = _data->Buffer->Layout->Attributes[node->Attributes[1]]; \ - auto& ageAttr = _data->Buffer->Layout->Attributes[node->Attributes[2]]; \ + auto& positionAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[0]]; \ + auto& velocityAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[1]]; \ + auto& ageAttr = context.Data->Buffer->Layout->Attributes[node->Attributes[2]]; \ byte* positionPtr = start + positionAttr.Offset; \ byte* velocityPtr = start + velocityAttr.Offset; \ byte* agePtr = start + ageAttr.Offset; \ @@ -1221,7 +1223,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* #define LOGIC() \ Vector3 position = *(Vector3*)positionPtr; \ Vector3 velocity = *(Vector3*)velocityPtr; \ - Vector3 nextPos = position + velocity * _deltaTime; \ + Vector3 nextPos = position + velocity * context.DeltaTime; \ Vector3 n = planeNormal; \ float distToPlane = Vector3::Dot(nextPos, n) - Vector3::Dot(planePosition, n) - radius; \ if (distToPlane < 0.0f) \ @@ -1233,7 +1235,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -1263,7 +1265,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* #define LOGIC() \ Vector3 position = *(Vector3*)positionPtr; \ Vector3 velocity = *(Vector3*)velocityPtr; \ - Vector3 nextPos = position + velocity * _deltaTime; \ + Vector3 nextPos = position + velocity * context.DeltaTime; \ Vector3 dir = nextPos - spherePosition; \ float sqrLength = Vector3::Dot(dir, dir); \ float totalRadius = sphereRadius + sign * radius; \ @@ -1278,7 +1280,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -1308,7 +1310,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* #define LOGIC() \ Vector3 position = *(Vector3*)positionPtr; \ Vector3 velocity = *(Vector3*)velocityPtr; \ - Vector3 nextPos = position + velocity * _deltaTime; \ + Vector3 nextPos = position + velocity * context.DeltaTime; \ Vector3 dir = nextPos - boxPosition; \ Vector3 absDir = Vector3::Abs(dir); \ Vector3 halfBoxSize = boxSize * 0.5f + radius * sign; \ @@ -1338,7 +1340,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } @@ -1370,7 +1372,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* #define LOGIC() \ Vector3 position = *(Vector3*)positionPtr; \ Vector3 velocity = *(Vector3*)velocityPtr; \ - Vector3 nextPos = position + velocity * _deltaTime; \ + Vector3 nextPos = position + velocity * context.DeltaTime; \ Vector3 dir = nextPos - cylinderPosition; \ float halfHeight = cylinderHeight * 0.5f + radius * sign; \ float cylinderRadiusT = cylinderRadius + radius * sign; \ @@ -1403,7 +1405,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode* { for (int32 particleIndex = particlesStart; particleIndex < particlesEnd; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; INPUTS_FETCH(); LOGIC(); } diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp index 019dc41f3..6d9ba3722 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.Particles.cpp @@ -7,11 +7,12 @@ #include "Engine/Graphics/RenderTask.h" #define GET_VIEW() auto mainViewTask = MainRenderTask::Instance && MainRenderTask::Instance->LastUsedFrame != 0 ? MainRenderTask::Instance : nullptr -#define ACCESS_PARTICLE_ATTRIBUTE(index) (_data->Buffer->GetParticleCPU(_particleIndex) + _data->Buffer->Layout->Attributes[node->Attributes[index]].Offset) +#define ACCESS_PARTICLE_ATTRIBUTE(index) (context.Data->Buffer->GetParticleCPU(context.ParticleIndex) + context.Data->Buffer->Layout->Attributes[node->Attributes[index]].Offset) #define GET_PARTICLE_ATTRIBUTE(index, type) *(type*)ACCESS_PARTICLE_ATTRIBUTE(index) void ParticleEmitterGraphCPUExecutor::ProcessGroupParameters(Box* box, Node* node, Value& value) { + auto& context = Context.Get(); switch (node->TypeID) { // Get @@ -21,7 +22,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParameters(Box* box, Node* nod const auto param = _graph.GetParameter((Guid)node->Values[0], paramIndex); if (param) { - value = _data->Parameters[paramIndex]; + value = context.Data->Parameters[paramIndex]; switch (param->Type.Type) { case VariantType::Vector2: @@ -130,6 +131,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupTextures(Box* box, Node* node, void ParticleEmitterGraphCPUExecutor::ProcessGroupTools(Box* box, Node* node, Value& value) { + auto& context = Context.Get(); switch (node->TypeID) { // Linearize Depth @@ -141,15 +143,13 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupTools(Box* box, Node* node, Va } // Time case 8: - { - value = box->ID == 0 ? _data->Time : _deltaTime; + value = box->ID == 0 ? context.Data->Time : context.DeltaTime; break; - } // Transform Position To Screen UV case 9: { GET_VIEW(); - const Matrix viewProjection = _viewTask ? _viewTask->View.PrevViewProjection : Matrix::Identity; + const Matrix viewProjection = context.ViewTask ? context.ViewTask->View.PrevViewProjection : Matrix::Identity; const Vector3 position = (Vector3)TryGetValue(node->GetBox(0), Value::Zero); Vector4 projPos; Vector3::Transform(position, viewProjection); @@ -165,6 +165,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupTools(Box* box, Node* node, Va void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* nodeBase, Value& value) { + auto& context = Context.Get(); auto node = (ParticleEmitterGraphCPUNode*)nodeBase; switch (node->TypeID) { @@ -199,8 +200,8 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node // Particle Attribute (by index) case 303: { - const auto particleIndex = tryGetValue(node->GetBox(1), _particleIndex); - byte* ptr = (_data->Buffer->GetParticleCPU((uint32)particleIndex) + _data->Buffer->Layout->Attributes[node->Attributes[0]].Offset); + const auto particleIndex = tryGetValue(node->GetBox(1), context.ParticleIndex); + byte* ptr = (context.Data->Buffer->GetParticleCPU((uint32)particleIndex) + context.Data->Buffer->Layout->Attributes[node->Attributes[0]].Offset); switch ((ParticleAttribute::ValueTypes)node->Attributes[1]) { case ParticleAttribute::ValueTypes::Float: @@ -296,19 +297,19 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node // Effect Position case 200: { - value = _effect->GetPosition(); + value = context.Effect->GetPosition(); break; } // Effect Rotation case 201: { - value = _effect->GetOrientation(); + value = context.Effect->GetOrientation(); break; } // Effect Scale case 202: { - value = _effect->GetScale(); + value = context.Effect->GetScale(); break; } // Simulation Mode @@ -320,25 +321,25 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node // View Position case 204: { - value = _viewTask ? _viewTask->View.Position : Vector3::Zero; + value = context.ViewTask ? context.ViewTask->View.Position : Vector3::Zero; break; } // View Direction case 205: { - value = _viewTask ? _viewTask->View.Direction : Vector3::Forward; + value = context.ViewTask ? context.ViewTask->View.Direction : Vector3::Forward; break; } // View Far Plane case 206: { - value = _viewTask ? _viewTask->View.Far : 0.0f; + value = context.ViewTask ? context.ViewTask->View.Far : 0.0f; break; } // Screen Size case 207: { - const Vector4 size = _viewTask ? _viewTask->View.ScreenSize : Vector4::Zero; + const Vector4 size = context.ViewTask ? context.ViewTask->View.ScreenSize : Vector4::Zero; if (box->ID == 0) value = Vector2(size.X, size.Y); else @@ -358,11 +359,11 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node #if 0 // Prevent recursive calls - for (int32 i = _callStack.Count() - 1; i >= 0; i--) + for (int32 i = context.CallStack.Count() - 1; i >= 0; i--) { - if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(14, 300)) + if (context.CallStack[i]->Type == GRAPH_NODE_MAKE_TYPE(14, 300)) { - const auto callFunc = ((ParticleEmitterGraphCPUNode*)_callStack[i])->Assets[0].Get(); + const auto callFunc = context.CallStack[i]->Assets[0].Get(); if (callFunc == function) { value = Value::Zero; @@ -372,14 +373,9 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node } #endif - // Create a instanced version of the function graph - ParticleEmitterGraphCPU* graph; - if (!_functions.TryGet(nodeBase, graph)) - { - graph = New(); - function->LoadSurface((ParticleEmitterGraphCPU&)*graph); - _functions.Add(nodeBase, graph); - } + // Get function graph + Graph* graph = (Graph*)&function->Graph; + context.Functions[nodeBase] = graph; // Peek the function output (function->Outputs maps the functions outputs to output nodes indices) const int32 outputIndex = box->ID - 16; @@ -388,22 +384,22 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node value = Value::Zero; break; } - ParticleEmitterGraphCPU::Node* functionOutputNode = &graph->Nodes[function->Outputs[outputIndex]]; + Node* functionOutputNode = &graph->Nodes[function->Outputs[outputIndex]]; Box* functionOutputBox = functionOutputNode->TryGetBox(0); // Evaluate the function output - _graphStack.Push((Graph*)graph); + context.GraphStack.Push(graph); value = functionOutputBox && functionOutputBox->HasConnection() ? eatBox(nodeBase, functionOutputBox->FirstConnection()) : Value::Zero; - _graphStack.Pop(); + context.GraphStack.Pop(); break; } // Particle Index case 301: - value = _particleIndex; + value = context.ParticleIndex; break; // Particles Count case 302: - value = (uint32)_data->Buffer->CPU.Count; + value = (uint32)context.Data->Buffer->CPU.Count; break; default: VisjectExecutor::ProcessGroupParticles(box, nodeBase, value); @@ -413,20 +409,21 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupParticles(Box* box, Node* node void ParticleEmitterGraphCPUExecutor::ProcessGroupFunction(Box* box, Node* node, Value& value) { + auto& context = Context.Get(); switch (node->TypeID) { // Function Input case 1: { // Find the function call - ParticleEmitterGraphCPUNode* functionCallNode = nullptr; - ASSERT(_graphStack.Count() >= 2); - ParticleEmitterGraphCPU* graph; - for (int32 i = _callStack.Count() - 1; i >= 0; i--) + Node* functionCallNode = nullptr; + ASSERT(context.GraphStack.Count() >= 2); + Graph* graph; + for (int32 i = context.CallStack.Count() - 1; i >= 0; i--) { - if (_callStack[i]->Type == GRAPH_NODE_MAKE_TYPE(14, 300) && _functions.TryGet(_callStack[i], graph) && _graphStack[_graphStack.Count() - 1] == (Graph*)graph) + if (context.CallStack[i]->Type == GRAPH_NODE_MAKE_TYPE(14, 300) && context.Functions.TryGet(context.CallStack[i], graph) && context.GraphStack[context.GraphStack.Count() - 1] == graph) { - functionCallNode = (ParticleEmitterGraphCPUNode*)_callStack[i]; + functionCallNode = context.CallStack[i]; break; } } @@ -436,7 +433,7 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupFunction(Box* box, Node* node, break; } const auto function = functionCallNode->Assets[0].As(); - if (!_functions.TryGet((Node*)functionCallNode, graph) || !function) + if (!context.Functions.TryGet(functionCallNode, graph) || !function) { value = Value::Zero; break; @@ -461,9 +458,9 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupFunction(Box* box, Node* node, if (functionCallBox && functionCallBox->HasConnection()) { // Use provided input value from the function call - _graphStack.Pop(); + context.GraphStack.Pop(); value = eatBox(node, functionCallBox->FirstConnection()); - _graphStack.Push((Graph*)graph); + context.GraphStack.Push(graph); } else { diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp index 52b159947..396cbaee0 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.cpp @@ -7,6 +7,8 @@ #include "Engine/Particles/ParticleEffect.h" #include "Engine/Engine/Time.h" +ThreadLocal ParticleEmitterGraphCPUExecutor::Context; + namespace { bool SortRibbonParticles(const int32& a, const int32& b, ParticleBufferCPUDataAccessor* data) @@ -35,8 +37,7 @@ void ParticleEmitterGraphCPU::CreateDefault() bool ParticleEmitterGraphCPU::Load(ReadStream* stream, bool loadMeta) { - // Base - if (ParticleEmitterGraph::Load(stream, loadMeta)) + if (Base::Load(stream, loadMeta)) return true; // Assign the offset in the sorted indices buffer to the rendering modules @@ -61,7 +62,7 @@ bool ParticleEmitterGraphCPU::Load(ReadStream* stream, bool loadMeta) for (int32 i = 0; i < RibbonRenderingModules.Count(); i++) { const auto module = RibbonRenderingModules[i]; - module->Ribbon.RibbonOrderOffset = ribbonOrderOffset; + module->RibbonOrderOffset = ribbonOrderOffset; ribbonOrderOffset += Capacity; } @@ -96,18 +97,16 @@ void ParticleEmitterGraphCPU::InitializeNode(Node* node) if (node->Used) return; - // Base - ParticleEmitterGraph::InitializeNode(node); + Base::InitializeNode(node); switch (node->Type) { // Position (spiral) case GRAPH_NODE_MAKE_TYPE(15, 214): - { - node->SpiralModuleProgress = 0.0f; + node->CustomDataOffset = CustomDataSize; + CustomDataSize += sizeof(float); break; } - } } ParticleEmitterGraphCPUExecutor::ParticleEmitterGraphCPUExecutor(ParticleEmitterGraphCPU& graph) @@ -120,9 +119,19 @@ ParticleEmitterGraphCPUExecutor::ParticleEmitterGraphCPUExecutor(ParticleEmitter _perGroupProcessCall[16] = (ProcessBoxHandler)&ParticleEmitterGraphCPUExecutor::ProcessGroupFunction; } -ParticleEmitterGraphCPUExecutor::~ParticleEmitterGraphCPUExecutor() +void ParticleEmitterGraphCPUExecutor::Init(ParticleEmitter* emitter, ParticleEffect* effect, ParticleEmitterInstance& data, float dt) { - _functions.ClearDelete(); + auto& context = Context.Get(); + context.GraphStack.Clear(); + context.GraphStack.Push((Graph*)&_graph); + context.Data = &data; + context.Emitter = emitter; + context.Effect = effect; + context.DeltaTime = dt; + context.ParticleIndex = 0; + context.ViewTask = effect->GetRenderTask(); + context.CallStack.Clear(); + context.Functions.Clear(); } bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, ParticleEffect* effect, ParticleEmitterInstance& data, BoundingBox& result) @@ -240,20 +249,14 @@ bool ParticleEmitterGraphCPUExecutor::ComputeBounds(ParticleEmitter* emitter, Pa case 401: { // Prepare graph data - _graphStack.Clear(); - _graphStack.Push((Graph*)&_graph); - _data = &data; - _emitter = emitter; - _effect = effect; - _deltaTime = 0.0f; - _viewTask = effect->GetRenderTask(); - _callStack.Clear(); + auto& context = Context.Get(); + Init(emitter, effect, data); // Find the maximum radius of the particle light float maxRadius = 0.0f; for (int32 particleIndex = 0; particleIndex < count; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; const float radius = (float)GetValue(module->GetBox(1), 3); if (radius > maxRadius) maxRadius = radius; @@ -370,14 +373,8 @@ void ParticleEmitterGraphCPUExecutor::Draw(ParticleEmitter* emitter, ParticleEff const int32 stride = buffer->Stride; // Prepare graph data - _graphStack.Clear(); - _graphStack.Push((Graph*)&_graph); - _data = &data; - _emitter = emitter; - _effect = effect; - _deltaTime = 0.0f; - _viewTask = effect->GetRenderTask(); - _callStack.Clear(); + Init(emitter, effect, data); + auto& context = Context.Get(); // Draw lights for (int32 moduleIndex = 0; moduleIndex < emitter->Graph.LightModules.Count(); moduleIndex++) @@ -405,7 +402,7 @@ void ParticleEmitterGraphCPUExecutor::Draw(ParticleEmitter* emitter, ParticleEff for (int32 particleIndex = 0; particleIndex < count; particleIndex++) { - _particleIndex = particleIndex; + context.ParticleIndex = particleIndex; const Vector4 color = (Vector4)GetValue(module->GetBox(0), 2); const float radius = (float)GetValue(module->GetBox(1), 3); @@ -429,15 +426,8 @@ void ParticleEmitterGraphCPUExecutor::Draw(ParticleEmitter* emitter, ParticleEff void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleEffect* effect, ParticleEmitterInstance& data, float dt, bool canSpawn) { // Prepare data - _graphStack.Clear(); - _graphStack.Push((Graph*)&_graph); - _data = &data; - _emitter = emitter; - _effect = effect; - _particleIndex = 0; - _deltaTime = dt; - _viewTask = effect->GetRenderTask(); - _callStack.Clear(); + Init(emitter, effect, data, dt); + auto& context = Context.Get(); auto& cpu = data.Buffer->CPU; // Update particles @@ -452,20 +442,20 @@ void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleE // Dead particles removal if (_graph._attrAge != -1 && _graph._attrLifetime != -1) { - byte* agePtr = cpu.Buffer.Get() + _data->Buffer->Layout->Attributes[_graph._attrAge].Offset; - byte* lifetimePtr = cpu.Buffer.Get() + _data->Buffer->Layout->Attributes[_graph._attrLifetime].Offset; + byte* agePtr = cpu.Buffer.Get() + data.Buffer->Layout->Attributes[_graph._attrAge].Offset; + byte* lifetimePtr = cpu.Buffer.Get() + data.Buffer->Layout->Attributes[_graph._attrLifetime].Offset; for (int32 particleIndex = 0; particleIndex < cpu.Count; particleIndex++) { if (*(float*)agePtr >= *(float*)lifetimePtr) { cpu.Count--; - Platform::MemoryCopy(_data->Buffer->GetParticleCPU(particleIndex), _data->Buffer->GetParticleCPU(cpu.Count), _data->Buffer->Stride); + Platform::MemoryCopy(data.Buffer->GetParticleCPU(particleIndex), data.Buffer->GetParticleCPU(cpu.Count), data.Buffer->Stride); particleIndex--; } else { - agePtr += _data->Buffer->Stride; - lifetimePtr += _data->Buffer->Stride; + agePtr += data.Buffer->Stride; + lifetimePtr += data.Buffer->Stride; } } } @@ -474,12 +464,12 @@ void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleE // Debug validation for NANs in data if (_graph._attrPosition != -1) { - byte* positionPtr = cpu.Buffer.Get() + _data->Buffer->Layout->Attributes[_graph._attrPosition].Offset; + byte* positionPtr = cpu.Buffer.Get() + data.Buffer->Layout->Attributes[_graph._attrPosition].Offset; for (int32 particleIndex = 0; particleIndex < cpu.Count; particleIndex++) { Vector3 pos = *((Vector3*)positionPtr); ASSERT(!pos.IsNanOrInfinity()); - positionPtr += _data->Buffer->Stride; + positionPtr += data.Buffer->Stride; } } #endif @@ -487,26 +477,26 @@ void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleE // Euler integration if (_graph._attrPosition != -1 && _graph._attrVelocity != -1) { - byte* positionPtr = cpu.Buffer.Get() + _data->Buffer->Layout->Attributes[_graph._attrPosition].Offset; - byte* velocityPtr = cpu.Buffer.Get() + _data->Buffer->Layout->Attributes[_graph._attrVelocity].Offset; + byte* positionPtr = cpu.Buffer.Get() + data.Buffer->Layout->Attributes[_graph._attrPosition].Offset; + byte* velocityPtr = cpu.Buffer.Get() + data.Buffer->Layout->Attributes[_graph._attrVelocity].Offset; for (int32 particleIndex = 0; particleIndex < cpu.Count; particleIndex++) { - *((Vector3*)positionPtr) += *((Vector3*)velocityPtr) * _deltaTime; - positionPtr += _data->Buffer->Stride; - velocityPtr += _data->Buffer->Stride; + *((Vector3*)positionPtr) += *((Vector3*)velocityPtr) * dt; + positionPtr += data.Buffer->Stride; + velocityPtr += data.Buffer->Stride; } } // Angular Euler Integration if (_graph._attrRotation != -1 && _graph._attrAngularVelocity != -1) { - byte* rotationPtr = cpu.Buffer.Get() + _data->Buffer->Layout->Attributes[_graph._attrRotation].Offset; - byte* angularVelocityPtr = cpu.Buffer.Get() + _data->Buffer->Layout->Attributes[_graph._attrAngularVelocity].Offset; + byte* rotationPtr = cpu.Buffer.Get() + data.Buffer->Layout->Attributes[_graph._attrRotation].Offset; + byte* angularVelocityPtr = cpu.Buffer.Get() + data.Buffer->Layout->Attributes[_graph._attrAngularVelocity].Offset; for (int32 particleIndex = 0; particleIndex < cpu.Count; particleIndex++) { - *((Vector3*)rotationPtr) += *((Vector3*)angularVelocityPtr) * _deltaTime; - rotationPtr += _data->Buffer->Stride; - angularVelocityPtr += _data->Buffer->Stride; + *((Vector3*)rotationPtr) += *((Vector3*)angularVelocityPtr) * dt; + rotationPtr += data.Buffer->Stride; + angularVelocityPtr += data.Buffer->Stride; } } @@ -545,16 +535,14 @@ void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleE // Sort ribbon particles if (cpu.RibbonOrder.IsEmpty()) { - cpu.RibbonOrder.Resize(_graph.RibbonRenderingModules.Count() * _data->Buffer->Capacity); + cpu.RibbonOrder.Resize(_graph.RibbonRenderingModules.Count() * data.Buffer->Capacity); } - ASSERT(cpu.RibbonOrder.Count() == _graph.RibbonRenderingModules.Count() * _data->Buffer->Capacity); + ASSERT(cpu.RibbonOrder.Count() == _graph.RibbonRenderingModules.Count() * data.Buffer->Capacity); for (int32 i = 0; i < _graph.RibbonRenderingModules.Count(); i++) { const auto module = _graph.RibbonRenderingModules[i]; - - ParticleBufferCPUDataAccessor sortKeyData(_data->Buffer, emitter->Graph.Layout.GetAttributeOffset(module->Attributes[1])); - - int32* ribbonOrderData = cpu.RibbonOrder.Get() + module->Ribbon.RibbonOrderOffset; + ParticleBufferCPUDataAccessor sortKeyData(data.Buffer, emitter->Graph.Layout.GetAttributeOffset(module->Attributes[1])); + int32* ribbonOrderData = cpu.RibbonOrder.Get() + module->RibbonOrderOffset; for (int32 j = 0; j < cpu.Count; j++) { @@ -567,23 +555,13 @@ void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleE } } } - - // Cleanup - _data = nullptr; } int32 ParticleEmitterGraphCPUExecutor::UpdateSpawn(ParticleEmitter* emitter, ParticleEffect* effect, ParticleEmitterInstance& data, float dt) { // Prepare data - _graphStack.Clear(); - _graphStack.Push((Graph*)&_graph); - _data = &data; - _emitter = emitter; - _effect = effect; - _particleIndex = 0; - _deltaTime = dt; - _viewTask = effect->GetRenderTask(); - _callStack.Clear(); + auto& context = Context.Get(); + Init(emitter, effect, data, dt); // Spawn particles int32 spawnCount = 0; @@ -592,16 +570,14 @@ int32 ParticleEmitterGraphCPUExecutor::UpdateSpawn(ParticleEmitter* emitter, Par spawnCount += ProcessSpawnModule(i); } - // Cleanup - _data = nullptr; - return spawnCount; } VisjectExecutor::Value ParticleEmitterGraphCPUExecutor::eatBox(Node* caller, Box* box) { // Check if graph is looped or is too deep - if (_callStack.Count() >= PARTICLE_EMITTER_MAX_CALL_STACK) + auto& context = Context.Get(); + if (context.CallStack.Count() >= PARTICLE_EMITTER_MAX_CALL_STACK) { OnError(caller, box, TEXT("Graph is looped or too deep!")); return Value::Zero; @@ -615,7 +591,7 @@ VisjectExecutor::Value ParticleEmitterGraphCPUExecutor::eatBox(Node* caller, Box #endif // Add to the calling stack - _callStack.Add(caller); + context.CallStack.Add(caller); // Call per group custom processing event Value value; @@ -624,12 +600,13 @@ VisjectExecutor::Value ParticleEmitterGraphCPUExecutor::eatBox(Node* caller, Box (this->*func)(box, parentNode, value); // Remove from the calling stack - _callStack.RemoveLast(); + context.CallStack.RemoveLast(); return value; } VisjectExecutor::Graph* ParticleEmitterGraphCPUExecutor::GetCurrentGraph() const { - return _graphStack.Peek(); + auto& context = Context.Get(); + return context.GraphStack.Peek(); } diff --git a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h index 34d7cec2e..587999aae 100644 --- a/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h +++ b/Source/Engine/Particles/Graph/CPU/ParticleEmitterGraph.CPU.h @@ -7,6 +7,7 @@ #include "Engine/Particles/ParticlesData.h" #include "Engine/Visject/VisjectGraph.h" #include "Engine/Core/Collections/Dictionary.h" +#include "Engine/Threading/ThreadLocal.h" struct RenderContext; class ParticleEffect; @@ -31,16 +32,6 @@ class ParticleEmitterGraphCPUExecutor; class ParticleEmitterGraphCPUBox : public VisjectGraphBox { -public: - - ParticleEmitterGraphCPUBox() - { - } - - ParticleEmitterGraphCPUBox(void* parent, byte id, VariantType type) - : VisjectGraphBox(parent, id, type) - { - } }; class ParticleEmitterGraphCPUNode : public ParticleEmitterGraphNode> @@ -54,23 +45,13 @@ public: union { - /// - /// The spiral position module progress value. - /// - float SpiralModuleProgress; - - struct - { - int32 RibbonOrderOffset; - } Ribbon; + int32 CustomDataOffset; + int32 RibbonOrderOffset; }; -public: - /// /// True if this node uses the per-particle data resolve instead of optimized whole-collection fetch. /// - /// True if use per particle data resolve, otherwise can optimize resolve pass. FORCE_INLINE bool UsePerParticleDataResolve() const { return UsesParticleData || !IsConstant; @@ -83,32 +64,31 @@ public: class ParticleEmitterGraphCPU : public ParticleEmitterGraph, ParticleEmitterGraphCPUNode, Variant> { friend ParticleEmitterGraphCPUExecutor; + typedef ParticleEmitterGraph, ParticleEmitterGraphCPUNode, Variant> Base; private: + struct NodeState + { + union + { + int32 SpiralProgress; + }; + }; Array _defaultParticleData; public: + // Size of the custom pre-node data buffer used for state tracking (eg. position on spiral arc progression). + int32 CustomDataSize = 0; + /// /// Creates the default surface graph (the main root node) for the particle emitter. Ensure to dispose the previous graph data before. /// void CreateDefault(); -public: - - /// - /// Determines whenever this emitter uses lights rendering. - /// - /// True if emitter uses lights rendering, otherwise false. - FORCE_INLINE bool UsesLightRendering() const - { - return LightModules.HasItems(); - } - /// /// Gets the position attribute offset from the particle data layout start (in bytes). /// - /// The offset in bytes. FORCE_INLINE int32 GetPositionAttributeOffset() const { return _attrPosition != -1 ? Layout.Attributes[_attrPosition].Offset : -1; @@ -117,7 +97,6 @@ public: /// /// Gets the age attribute offset from the particle data layout start (in bytes). /// - /// The offset in bytes. FORCE_INLINE int32 GetAgeAttributeOffset() const { return _attrAge != -1 ? Layout.Attributes[_attrAge].Offset : -1; @@ -127,37 +106,35 @@ public: // [ParticleEmitterGraph] bool Load(ReadStream* stream, bool loadMeta) override; - - bool onNodeLoaded(Node* n) override - { - ParticleEmitterGraph::onNodeLoaded(n); - return VisjectGraph::onNodeLoaded(n); - } - -protected: - - // [ParticleEmitterGraph] void InitializeNode(Node* node) override; }; +/// +/// The CPU particles emitter graph evaluation context. +/// +struct ParticleEmitterGraphCPUContext +{ + float DeltaTime; + uint32 ParticleIndex; + ParticleEmitterInstance* Data; + ParticleEmitter* Emitter; + ParticleEffect* Effect; + class SceneRenderTask* ViewTask; + Array> CallStack; + Array> GraphStack; + Dictionary Functions; +}; + /// /// The Particle Emitter Graph simulation on a CPU. /// class ParticleEmitterGraphCPUExecutor : public VisjectExecutor { private: - - // Runtime ParticleEmitterGraphCPU& _graph; - float _deltaTime; - uint32 _particleIndex; - ParticleEmitterInstance* _data; - ParticleEmitter* _emitter; - ParticleEffect* _effect; - class SceneRenderTask* _viewTask; - Array> _callStack; - Array> _graphStack; - Dictionary _functions; + + // Per-thread context to allow async execution + static ThreadLocal Context; public: @@ -167,11 +144,6 @@ public: /// The graph to execute. explicit ParticleEmitterGraphCPUExecutor(ParticleEmitterGraphCPU& graph); - /// - /// Finalizes an instance of the class. - /// - ~ParticleEmitterGraphCPUExecutor(); - /// /// Computes the local bounds of the particle emitter instance. /// @@ -214,6 +186,7 @@ public: private: + void Init(ParticleEmitter* emitter, ParticleEffect* effect, ParticleEmitterInstance& data, float dt = 0.0f); Value eatBox(Node* caller, Box* box) override; Graph* GetCurrentGraph() const override; @@ -226,17 +199,7 @@ private: int32 ProcessSpawnModule(int32 index); void ProcessModule(ParticleEmitterGraphCPUNode* node, int32 particlesStart, int32 particlesEnd); - Value TryGetValue(Box* box, int32 defaultValueBoxIndex, const Value& defaultValue) - { - const auto parentNode = box->GetParent(); - if (box->HasConnection()) - return eatBox(parentNode, box->FirstConnection()); - if (parentNode->Values.Count() > defaultValueBoxIndex) - return parentNode->Values[defaultValueBoxIndex]; - return defaultValue; - } - - Value GetValue(Box* box, int32 defaultValueBoxIndex) + FORCE_INLINE Value GetValue(Box* box, int32 defaultValueBoxIndex) { const auto parentNode = box->GetParent(); if (box->HasConnection()) diff --git a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h index 4d353b3ed..8ee2d20d0 100644 --- a/Source/Engine/Particles/Graph/ParticleEmitterGraph.h +++ b/Source/Engine/Particles/Graph/ParticleEmitterGraph.h @@ -52,36 +52,18 @@ public: /// /// The Particle Emitter Graph used to simulate particles. /// -template -class ParticleEmitterGraph : public Base +template +class ParticleEmitterGraph : public BaseType { public: typedef ValueType Value; - /// - /// The particle emitter module types. - /// enum class ModuleType { - /// - /// The spawn module. - /// Spawn, - - /// - /// The init module. - /// Initialize, - - /// - /// The update module. - /// Update, - - /// - /// The render module. - /// Render, }; @@ -172,8 +154,6 @@ public: bool UsesVolumetricFogRendering = false; -protected: - virtual void InitializeNode(NodeType* node) { // Skip if already initialized @@ -314,10 +294,8 @@ protected: } // Particle Emitter Function case GRAPH_NODE_MAKE_TYPE(14, 300): - { node->Assets[0] = Content::LoadAsync((Guid)node->Values[0]); break; - } // Particle Index case GRAPH_NODE_MAKE_TYPE(14, 301): node->UsesParticleData = true; @@ -566,7 +544,7 @@ public: UsesVolumetricFogRendering = false; // Base - Base::Clear(); + BaseType::Clear(); } bool Load(ReadStream* stream, bool loadMeta) override @@ -575,7 +553,7 @@ public: Version++; // Base - if (Base::Load(stream, loadMeta)) + if (BaseType::Load(stream, loadMeta)) return true; // Compute particle data layout and initialize used nodes (for only used nodes, start depth searching rom the modules) @@ -678,6 +656,6 @@ public: } } - return Base::onNodeLoaded(n); + return BaseType::onNodeLoaded(n); } }; diff --git a/Source/Engine/Particles/ParticleEmitter.cpp b/Source/Engine/Particles/ParticleEmitter.cpp index b5c3ecdae..75de7b697 100644 --- a/Source/Engine/Particles/ParticleEmitter.cpp +++ b/Source/Engine/Particles/ParticleEmitter.cpp @@ -105,7 +105,7 @@ Asset::LoadResult ParticleEmitter::load() EnablePooling = root->Values[3].AsBool; CustomBounds = (BoundingBox)root->Values[4]; UseAutoBounds = root->Values[5].AsBool; - IsUsingLights = Graph.UsesLightRendering(); + IsUsingLights = Graph.LightModules.HasItems(); } // Select simulation mode diff --git a/Source/Engine/Particles/ParticleEmitterFunction.cpp b/Source/Engine/Particles/ParticleEmitterFunction.cpp index 24282f083..b02c42cbd 100644 --- a/Source/Engine/Particles/ParticleEmitterFunction.cpp +++ b/Source/Engine/Particles/ParticleEmitterFunction.cpp @@ -24,6 +24,14 @@ Asset::LoadResult ParticleEmitterFunction::load() MemoryReadStream stream(surfaceChunk->Get(), surfaceChunk->Size()); if (Graph.Load(&stream, false)) return LoadResult::Failed; + for (int32 i = 0; i < Graph.Nodes.Count(); i++) + { + // Initialize all used nodes (starting from function output as roots) + if (Graph.Nodes[i].Type == GRAPH_NODE_MAKE_TYPE(16, 2)) + { + Graph.InitializeNode(&Graph.Nodes[i]); + } + } #if COMPILE_WITH_PARTICLE_GPU_GRAPH stream.SetPosition(0); if (GraphGPU.Load(&stream, false)) diff --git a/Source/Engine/Particles/Particles.cpp b/Source/Engine/Particles/Particles.cpp index 468906a3b..17c1e3da4 100644 --- a/Source/Engine/Particles/Particles.cpp +++ b/Source/Engine/Particles/Particles.cpp @@ -335,7 +335,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa break; int32 count = buffer->CPU.Count; ASSERT(buffer->CPU.RibbonOrder.Count() == emitter->Graph.RibbonRenderingModules.Count() * buffer->Capacity); - int32* ribbonOrderData = buffer->CPU.RibbonOrder.Get() + module->Ribbon.RibbonOrderOffset; + int32* ribbonOrderData = buffer->CPU.RibbonOrder.Get() + module->RibbonOrderOffset; ParticleBufferCPUDataAccessor positionData(buffer, emitter->Graph.Layout.GetAttributeOffset(module->Attributes[0])); @@ -483,7 +483,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa Vector2 uvOffset = module->Values[5].AsVector2(); ParticleBufferCPUDataAccessor sortKeyData(buffer, emitter->Graph.Layout.GetAttributeOffset(module->Attributes[1])); - int32* ribbonOrderData = buffer->CPU.RibbonOrder.Get() + module->Ribbon.RibbonOrderOffset; + int32* ribbonOrderData = buffer->CPU.RibbonOrder.Get() + module->RibbonOrderOffset; int32 count = buffer->CPU.Count; // Setup ribbon data @@ -1073,6 +1073,7 @@ ParticleBuffer* Particles::AcquireParticleBuffer(ParticleEmitter* emitter) if (emitter->EnablePooling && EnableParticleBufferPooling) { + PoolLocker.Lock(); const auto entries = Pool.TryGet(emitter); if (entries) { @@ -1087,7 +1088,6 @@ ParticleBuffer* Particles::AcquireParticleBuffer(ParticleEmitter* emitter) { Delete(result); result = nullptr; - if (entries->IsEmpty()) { Pool.Remove(emitter); @@ -1096,6 +1096,7 @@ ParticleBuffer* Particles::AcquireParticleBuffer(ParticleEmitter* emitter) } } } + PoolLocker.Unlock(); } if (!result) diff --git a/Source/Engine/Particles/ParticlesSimulation.cpp b/Source/Engine/Particles/ParticlesSimulation.cpp index e85665afa..97c687685 100644 --- a/Source/Engine/Particles/ParticlesSimulation.cpp +++ b/Source/Engine/Particles/ParticlesSimulation.cpp @@ -24,6 +24,7 @@ void ParticleEmitterInstance::ClearState() Version = 0; Time = 0; SpawnModulesData.Clear(); + CustomData.Clear(); #if COMPILE_WITH_GPU_PARTICLES GPU.DeltaTime = 0.0f; GPU.SpawnCount = 0; @@ -61,12 +62,16 @@ void ParticleEmitterInstance::Sync(ParticleSystemInstance& systemInstance, Parti if (SpawnModulesData.Count() != emitter->Graph.SpawnModules.Count()) { SpawnModulesData.Resize(emitter->Graph.SpawnModules.Count(), false); - SpawnerData data; data.SpawnCounter = 0; data.NextSpawnTime = 0; SpawnModulesData.SetAll(data); } + if (CustomData.Count() != emitter->Graph.CustomDataSize) + { + CustomData.Resize(emitter->Graph.CustomDataSize, false); + Platform::MemoryClear(CustomData.Get(), CustomData.Count()); + } } // Sync buffer version diff --git a/Source/Engine/Particles/ParticlesSimulation.h b/Source/Engine/Particles/ParticlesSimulation.h index c9230c1c6..7008957f0 100644 --- a/Source/Engine/Particles/ParticlesSimulation.h +++ b/Source/Engine/Particles/ParticlesSimulation.h @@ -83,8 +83,10 @@ public: /// Array SpawnModulesData; -#if COMPILE_WITH_GPU_PARTICLES + // Custom per-node data (eg. position on spiral module for arc progress tracking) + Array CustomData; +#if COMPILE_WITH_GPU_PARTICLES struct { /// @@ -97,7 +99,6 @@ public: /// int32 SpawnCount; } GPU; - #endif /// From bf70a42d86f0ca84715a00ac85245e1ae04af758 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 15 Jun 2021 23:48:35 +0200 Subject: [PATCH 171/198] Change TaskGraphSystem ordering --- Source/Engine/Threading/TaskGraph.cpp | 2 +- Source/Engine/Threading/TaskGraph.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Engine/Threading/TaskGraph.cpp b/Source/Engine/Threading/TaskGraph.cpp index aa86a94a9..621636203 100644 --- a/Source/Engine/Threading/TaskGraph.cpp +++ b/Source/Engine/Threading/TaskGraph.cpp @@ -9,7 +9,7 @@ namespace { bool SortTaskGraphSystem(TaskGraphSystem* const& a, TaskGraphSystem* const& b) { - return b->Order < a->Order; + return b->Order > a->Order; }; } diff --git a/Source/Engine/Threading/TaskGraph.h b/Source/Engine/Threading/TaskGraph.h index 7268e644c..142b0d2a1 100644 --- a/Source/Engine/Threading/TaskGraph.h +++ b/Source/Engine/Threading/TaskGraph.h @@ -19,7 +19,7 @@ private: public: /// - /// The execution order of the system (systems with higher order are executed earlier). + /// The execution order of the system (systems with higher order are executed later, lower first). /// API_FIELD() int32 Order = 0; From 1dfcfb8aa9beca5797b0563576d833e7c9f7ac63 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 15 Jun 2021 23:48:52 +0200 Subject: [PATCH 172/198] Fix d3d12 compilation with older Win10 SDK version --- Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp index caab244de..b2af2e6d7 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUDeviceDX12.cpp @@ -64,6 +64,7 @@ GPUDevice* GPUDeviceDX12::Create() LOG(Info, "DirectX debugging layer enabled"); } #if 0 +#ifdef __ID3D12Debug1_FWD_DEFINED__ ComPtr debugLayer1; D3D12GetDebugInterface(IID_PPV_ARGS(&debugLayer1)); if (debugLayer1) @@ -73,6 +74,8 @@ GPUDevice* GPUDeviceDX12::Create() debugLayer1->SetEnableSynchronizedCommandQueueValidation(true); } #endif +#endif +#ifdef __ID3D12DeviceRemovedExtendedDataSettings_FWD_DEFINED__ ComPtr dredSettings; VALIDATE_DIRECTX_RESULT(D3D12GetDebugInterface(IID_PPV_ARGS(&dredSettings))); if (dredSettings) @@ -81,6 +84,7 @@ GPUDevice* GPUDeviceDX12::Create() dredSettings->SetAutoBreadcrumbsEnablement(D3D12_DRED_ENABLEMENT_FORCED_ON); dredSettings->SetPageFaultEnablement(D3D12_DRED_ENABLEMENT_FORCED_ON); } +#endif #endif // Create DXGI factory (CreateDXGIFactory2 is supported on Windows 8.1 or newer) From d895789296afbc5f82027e02ab7ec59095098b12 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 15 Jun 2021 23:49:37 +0200 Subject: [PATCH 173/198] Add async particles updating via Task Graph --- .../Editor/Surface/ParticleEmitterSurface.cs | 4 +- Source/Engine/Animations/Graph/AnimGraph.cpp | 1 - Source/Engine/Particles/Particles.cpp | 461 +++++++++--------- Source/Engine/Particles/Particles.h | 14 +- 4 files changed, 256 insertions(+), 224 deletions(-) diff --git a/Source/Editor/Surface/ParticleEmitterSurface.cs b/Source/Editor/Surface/ParticleEmitterSurface.cs index 4368569a1..905d41882 100644 --- a/Source/Editor/Surface/ParticleEmitterSurface.cs +++ b/Source/Editor/Surface/ParticleEmitterSurface.cs @@ -17,12 +17,12 @@ namespace FlaxEditor.Surface [HideInEditor] public class ParticleEmitterSurface : VisjectSurface { - internal Particles.ParticleEmitterNode _rootNode; + internal FlaxEditor.Surface.Archetypes.Particles.ParticleEmitterNode _rootNode; /// /// Gets the root node of the emitter graph. /// - public Particles.ParticleEmitterNode RootNode => _rootNode; + public FlaxEditor.Surface.Archetypes.Particles.ParticleEmitterNode RootNode => _rootNode; /// public ParticleEmitterSurface(IVisjectSurfaceOwner owner, Action onSave, FlaxEditor.Undo undo) diff --git a/Source/Engine/Animations/Graph/AnimGraph.cpp b/Source/Engine/Animations/Graph/AnimGraph.cpp index 6d95cbdfc..e726fbacc 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.cpp +++ b/Source/Engine/Animations/Graph/AnimGraph.cpp @@ -5,7 +5,6 @@ #include "Engine/Content/Assets/SkinnedModel.h" #include "Engine/Graphics/Models/SkeletonData.h" #include "Engine/Scripting/Scripting.h" -#include "Engine/Engine/Time.h" ThreadLocal AnimGraphExecutor::Context; diff --git a/Source/Engine/Particles/Particles.cpp b/Source/Engine/Particles/Particles.cpp index 17c1e3da4..c3f72270c 100644 --- a/Source/Engine/Particles/Particles.cpp +++ b/Source/Engine/Particles/Particles.cpp @@ -1,11 +1,13 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "Particles.h" +#include "ParticleEffect.h" #include "Engine/Content/Assets/Model.h" #include "Engine/Core/Collections/Sorting.h" #include "Engine/Core/Collections/HashSet.h" #include "Engine/Engine/EngineService.h" #include "Engine/Engine/Time.h" +#include "Engine/Engine/Engine.h" #include "Engine/Graphics/GPUBuffer.h" #include "Engine/Graphics/GPUPipelineStatePermutations.h" #include "Engine/Graphics/RenderTask.h" @@ -13,7 +15,7 @@ #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Renderer/DrawCall.h" #include "Engine/Renderer/RenderList.h" -#include "ParticleEffect.h" +#include "Engine/Threading/TaskGraph.h" #if COMPILE_WITH_GPU_PARTICLES #include "Engine/Content/Assets/Shader.h" #include "Engine/Profiler/ProfilerGPU.h" @@ -46,10 +48,8 @@ public: { if (VB) return false; - VB = GPUDevice::Instance->CreateBuffer(TEXT("SpriteParticleRenderer,VB")); IB = GPUDevice::Instance->CreateBuffer(TEXT("SpriteParticleRenderer.IB")); - static SpriteParticleVertex vertexBuffer[] = { { -0.5f, -0.5f, 0.0f, 0.0f }, @@ -57,20 +57,16 @@ public: { +0.5f, +0.5f, 1.0f, 1.0f }, { -0.5f, +0.5f, 0.0f, 1.0f }, }; - static uint16 indexBuffer[] = { 0, 1, 2, - 0, 2, 3, }; - - return VB->Init(GPUBufferDescription::Vertex(sizeof(SpriteParticleVertex), VertexCount, vertexBuffer)) || - IB->Init(GPUBufferDescription::Index(sizeof(uint16), IndexCount, indexBuffer)); + return VB->Init(GPUBufferDescription::Vertex(sizeof(SpriteParticleVertex), VertexCount, vertexBuffer)) || IB->Init(GPUBufferDescription::Index(sizeof(uint16), IndexCount, indexBuffer)); } void Dispose() @@ -103,15 +99,17 @@ namespace ParticleManagerImpl { CriticalSection PoolLocker; Dictionary> Pool; - HashSet UpdateList(256); + Array UpdateList; #if COMPILE_WITH_GPU_PARTICLES - HashSet GpuUpdateList(256); + CriticalSection GpuUpdateListLocker; + Array GpuUpdateList; RenderTask* GpuRenderTask = nullptr; #endif } using namespace ParticleManagerImpl; +TaskGraphSystem* Particles::System = nullptr; bool Particles::EnableParticleBufferPooling = true; float Particles::ParticleBufferRecycleTimeout = 10.0f; @@ -149,14 +147,24 @@ public: { } - void Update() override; + bool Init() override; void Dispose() override; }; +class ParticlesSystem : public TaskGraphSystem +{ +public: + float DeltaTime, UnscaledDeltaTime, Time, UnscaledTime; + void Job(int32 index); + void Execute(TaskGraph* graph) override; + void PostExecute(TaskGraph* graph) override; +}; + ParticleManagerService ParticleManagerServiceInstance; void Particles::UpdateEffect(ParticleEffect* effect) { + ASSERT_LOW_LAYER(!UpdateList.Contains(effect)); UpdateList.Add(effect); } @@ -1027,14 +1035,14 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe void UpdateGPU(RenderTask* task, GPUContext* context) { + ScopeLock lock(GpuUpdateListLocker); if (GpuUpdateList.IsEmpty()) return; PROFILE_GPU("GPU Particles"); - for (auto i = GpuUpdateList.Begin(); i.IsNotEnd(); ++i) + for (ParticleEffect* effect : GpuUpdateList) { - ParticleEffect* effect = i->Item; auto& instance = effect->Instance; const auto particleSystem = effect->ParticleSystem.Get(); if (!particleSystem || !particleSystem->IsLoaded()) @@ -1155,218 +1163,22 @@ void Particles::OnEmitterUnload(ParticleEmitter* emitter) PoolLocker.Unlock(); #if COMPILE_WITH_GPU_PARTICLES - for (auto i = GpuUpdateList.Begin(); i.IsNotEnd(); ++i) + GpuUpdateListLocker.Lock(); + for (int32 i = GpuUpdateList.Count() - 1; i >= 0; i--) { - if (i->Item->Instance.ContainsEmitter(emitter)) - GpuUpdateList.Remove(i); + if (GpuUpdateList[i]->Instance.ContainsEmitter(emitter)) + GpuUpdateList.RemoveAt(i); } + GpuUpdateListLocker.Unlock(); #endif } -void ParticleManagerService::Update() +bool ParticleManagerService::Init() { - PROFILE_CPU_NAMED("Particles"); - - // TODO: implement the thread jobs pipeline to run set of tasks at once (use it for multi threaded rendering and animations evaluation and CPU particles simulation) - - const auto timeSeconds = Platform::GetTimeSeconds(); - const auto& tickData = Time::Update; - const float deltaTimeUnscaled = tickData.UnscaledDeltaTime.GetTotalSeconds(); - const float timeUnscaled = tickData.UnscaledTime.GetTotalSeconds(); - const float deltaTime = tickData.DeltaTime.GetTotalSeconds(); - const float time = tickData.Time.GetTotalSeconds(); - - // Update particle effects - for (auto i = UpdateList.Begin(); i.IsNotEnd(); ++i) - { - ParticleEffect* effect = i->Item; - auto& instance = effect->Instance; - const auto particleSystem = effect->ParticleSystem.Get(); - if (!particleSystem || !particleSystem->IsLoaded()) - continue; - bool anyEmitterNotReady = false; - for (int32 j = 0; j < particleSystem->Tracks.Count(); j++) - { - const auto& track = particleSystem->Tracks[j]; - if (track.Type != ParticleSystem::Track::Types::Emitter || track.Disabled) - continue; - auto emitter = particleSystem->Emitters[track.AsEmitter.Index].Get(); - if (!emitter || !emitter->IsLoaded()) - { - anyEmitterNotReady = true; - break; - } - } - if (anyEmitterNotReady) - continue; - -#if USE_EDITOR - // Lock in editor only (more reloads during asset live editing) - ScopeLock lock(particleSystem->Locker); -#endif - - // Prepare instance data - instance.Sync(particleSystem); - - bool updateBounds = false; - bool updateGpu = false; - - // Simulation delta time can be based on a time since last update or the current delta time - float dt = effect->UseTimeScale ? deltaTime : deltaTimeUnscaled; - float t = effect->UseTimeScale ? time : timeUnscaled; -#if USE_EDITOR - if (!Editor::IsPlayMode) - { - dt = deltaTimeUnscaled; - t = timeUnscaled; - } -#endif - const float lastUpdateTime = instance.LastUpdateTime; - if (lastUpdateTime > 0 && t > lastUpdateTime) - { - dt = t - lastUpdateTime; - } - else if (lastUpdateTime < 0) - { - // Update bounds after first system update - updateBounds = true; - } - // TODO: if using fixed timestep quantize the dt and accumulate remaining part for the next update? - if (dt <= 1.0f / 240.0f) - continue; - dt *= effect->SimulationSpeed; - instance.Time += dt; - const float fps = particleSystem->FramesPerSecond; - const float duration = particleSystem->DurationFrames / fps; - if (instance.Time > duration) - { - if (effect->IsLooping) - { - // Loop - // TODO: accumulate (duration - instance.Time) into next update dt - instance.Time = 0; - for (int32 j = 0; j < instance.Emitters.Count(); j++) - { - auto& e = instance.Emitters[j]; - e.Time = 0; - for (auto& s : e.SpawnModulesData) - { - s.NextSpawnTime = 0.0f; - } - } - } - else - { - // End - instance.Time = duration; - for (auto& emitterInstance : instance.Emitters) - { - if (emitterInstance.Buffer) - { - Particles::RecycleParticleBuffer(emitterInstance.Buffer); - emitterInstance.Buffer = nullptr; - } - } - continue; - } - } - instance.LastUpdateTime = t; - - // Update all emitter tracks - for (int32 j = 0; j < particleSystem->Tracks.Count(); j++) - { - const auto& track = particleSystem->Tracks[j]; - if (track.Type != ParticleSystem::Track::Types::Emitter || track.Disabled) - continue; - auto emitter = particleSystem->Emitters[track.AsEmitter.Index].Get(); - auto& data = instance.Emitters[track.AsEmitter.Index]; - ASSERT(emitter && emitter->IsLoaded()); - ASSERT(emitter->Capacity != 0 && emitter->Graph.Layout.Size != 0); - - // Calculate new time position - const float startTime = track.AsEmitter.StartFrame / fps; - const float durationTime = track.AsEmitter.DurationFrames / fps; - const bool canSpawn = startTime <= instance.Time && instance.Time <= startTime + durationTime; - - // Update instance data - data.Sync(effect->Instance, particleSystem, track.AsEmitter.Index); - if (!data.Buffer) - { - data.Buffer = Particles::AcquireParticleBuffer(emitter); - } - data.Time += dt; - - // Update particles simulation - switch (emitter->SimulationMode) - { - case ParticlesSimulationMode::CPU: - emitter->GraphExecutorCPU.Update(emitter, effect, data, dt, canSpawn); - updateBounds |= emitter->UseAutoBounds; - break; -#if COMPILE_WITH_GPU_PARTICLES - case ParticlesSimulationMode::GPU: - emitter->GPU.Update(emitter, effect, data, dt, canSpawn); - updateGpu = true; - break; -#endif - default: - CRASH; - break; - } - } - - // Update bounds if any of the emitters uses auto-bounds - if (updateBounds) - { - effect->UpdateBounds(); - } - -#if COMPILE_WITH_GPU_PARTICLES - // Register for GPU update - if (updateGpu) - { - GpuUpdateList.Add(effect); - } -#endif - } - UpdateList.Clear(); - -#if COMPILE_WITH_GPU_PARTICLES - // Create GPU render task if missing but required - if (GpuUpdateList.HasItems() && !GpuRenderTask) - { - GpuRenderTask = New(); - GpuRenderTask->Order = -10000000; - GpuRenderTask->Render.Bind(UpdateGPU); - ScopeLock lock(RenderTask::TasksLocker); - RenderTask::Tasks.Add(GpuRenderTask); - } - else if (GpuRenderTask) - { - ScopeLock lock(RenderTask::TasksLocker); - GpuRenderTask->Enabled = GpuUpdateList.HasItems(); - } -#endif - - // Recycle buffers - PoolLocker.Lock(); - for (auto i = Pool.Begin(); i.IsNotEnd(); ++i) - { - auto& entries = i->Value; - for (int32 j = 0; j < entries.Count(); j++) - { - auto& e = entries[j]; - if (timeSeconds - e.LastTimeUsed >= Particles::ParticleBufferRecycleTimeout) - { - Delete(e.Buffer); - entries.RemoveAt(j--); - } - } - - if (entries.IsEmpty()) - Pool.Remove(i); - } - PoolLocker.Unlock(); + Particles::System = New(); + Particles::System->Order = 10000; + Engine::UpdateGraph->AddSystem(Particles::System); + return false; } void ParticleManagerService::Dispose() @@ -1401,4 +1213,215 @@ void ParticleManagerService::Dispose() PoolLocker.Unlock(); SpriteRenderer.Dispose(); + SAFE_DELETE(Particles::System); +} + +void ParticlesSystem::Job(int32 index) +{ + PROFILE_CPU_NAMED("Particles.Job"); + auto effect = UpdateList[index]; + auto& instance = effect->Instance; + const auto particleSystem = effect->ParticleSystem.Get(); + if (!particleSystem || !particleSystem->IsLoaded()) + return; + bool anyEmitterNotReady = false; + for (int32 j = 0; j < particleSystem->Tracks.Count(); j++) + { + const auto& track = particleSystem->Tracks[j]; + if (track.Type != ParticleSystem::Track::Types::Emitter || track.Disabled) + continue; + auto emitter = particleSystem->Emitters[track.AsEmitter.Index].Get(); + if (!emitter || !emitter->IsLoaded()) + { + anyEmitterNotReady = true; + break; + } + } + if (anyEmitterNotReady) + return; + + // Prepare instance data + instance.Sync(particleSystem); + + bool updateBounds = false; + bool updateGpu = false; + + // Simulation delta time can be based on a time since last update or the current delta time + bool useTimeScale = effect->UseTimeScale; +#if USE_EDITOR + if (!Editor::IsPlayMode) + useTimeScale = false; +#endif + float dt = useTimeScale ? DeltaTime : UnscaledDeltaTime; + float t = useTimeScale ? Time : UnscaledTime; + const float lastUpdateTime = instance.LastUpdateTime; + if (lastUpdateTime > 0 && t > lastUpdateTime) + { + dt = t - lastUpdateTime; + } + else if (lastUpdateTime < 0) + { + // Update bounds after first system update + updateBounds = true; + } + // TODO: if using fixed timestep quantize the dt and accumulate remaining part for the next update? + if (dt <= 1.0f / 240.0f) + return; + dt *= effect->SimulationSpeed; + instance.Time += dt; + const float fps = particleSystem->FramesPerSecond; + const float duration = (float)particleSystem->DurationFrames / fps; + if (instance.Time > duration) + { + if (effect->IsLooping) + { + // Loop + // TODO: accumulate (duration - instance.Time) into next update dt + instance.Time = 0; + for (int32 j = 0; j < instance.Emitters.Count(); j++) + { + auto& e = instance.Emitters[j]; + e.Time = 0; + for (auto& s : e.SpawnModulesData) + { + s.NextSpawnTime = 0.0f; + } + } + } + else + { + // End + instance.Time = duration; + for (auto& emitterInstance : instance.Emitters) + { + if (emitterInstance.Buffer) + { + Particles::RecycleParticleBuffer(emitterInstance.Buffer); + emitterInstance.Buffer = nullptr; + } + } + return; + } + } + instance.LastUpdateTime = t; + + // Update all emitter tracks + for (int32 j = 0; j < particleSystem->Tracks.Count(); j++) + { + const auto& track = particleSystem->Tracks[j]; + if (track.Type != ParticleSystem::Track::Types::Emitter || track.Disabled) + continue; + auto emitter = particleSystem->Emitters[track.AsEmitter.Index].Get(); + auto& data = instance.Emitters[track.AsEmitter.Index]; + ASSERT(emitter && emitter->IsLoaded()); + ASSERT(emitter->Capacity != 0 && emitter->Graph.Layout.Size != 0); + + // Calculate new time position + const float startTime = (float)track.AsEmitter.StartFrame / fps; + const float durationTime = (float)track.AsEmitter.DurationFrames / fps; + const bool canSpawn = startTime <= instance.Time && instance.Time <= startTime + durationTime; + + // Update instance data + data.Sync(effect->Instance, particleSystem, track.AsEmitter.Index); + if (!data.Buffer) + { + data.Buffer = Particles::AcquireParticleBuffer(emitter); + } + data.Time += dt; + + // Update particles simulation + switch (emitter->SimulationMode) + { + case ParticlesSimulationMode::CPU: + emitter->GraphExecutorCPU.Update(emitter, effect, data, dt, canSpawn); + updateBounds |= emitter->UseAutoBounds; + break; +#if COMPILE_WITH_GPU_PARTICLES + case ParticlesSimulationMode::GPU: + emitter->GPU.Update(emitter, effect, data, dt, canSpawn); + updateGpu = true; + break; +#endif + default: + break; + } + } + + // Update bounds if any of the emitters uses auto-bounds + if (updateBounds) + { + effect->UpdateBounds(); + } + +#if COMPILE_WITH_GPU_PARTICLES + // Register for GPU update + if (updateGpu) + { + ScopeLock lock(GpuUpdateListLocker); + GpuUpdateList.Add(effect); + } +#endif +} + +void ParticlesSystem::Execute(TaskGraph* graph) +{ + if (UpdateList.Count() == 0) + return; + + // Setup data for async update + const auto& tickData = Time::Update; + DeltaTime = tickData.DeltaTime.GetTotalSeconds(); + UnscaledDeltaTime = tickData.UnscaledDeltaTime.GetTotalSeconds(); + Time = tickData.Time.GetTotalSeconds(); + UnscaledTime = tickData.UnscaledTime.GetTotalSeconds(); + + // Schedule work to update all particles in async + Function job; + job.Bind(this); + graph->DispatchJob(job, UpdateList.Count()); +} + +void ParticlesSystem::PostExecute(TaskGraph* graph) +{ + PROFILE_CPU_NAMED("Particles.PostExecute"); + + UpdateList.Clear(); + +#if COMPILE_WITH_GPU_PARTICLES + // Create GPU render task if missing but required + if (GpuUpdateList.HasItems() && !GpuRenderTask) + { + GpuRenderTask = New(); + GpuRenderTask->Order = -10000000; + GpuRenderTask->Render.Bind(UpdateGPU); + ScopeLock lock(RenderTask::TasksLocker); + RenderTask::Tasks.Add(GpuRenderTask); + } + else if (GpuRenderTask) + { + ScopeLock lock(RenderTask::TasksLocker); + GpuRenderTask->Enabled = GpuUpdateList.HasItems(); + } +#endif + + // Recycle buffers + const auto timeSeconds = Platform::GetTimeSeconds(); + PoolLocker.Lock(); + for (auto i = Pool.Begin(); i.IsNotEnd(); ++i) + { + auto& entries = i->Value; + for (int32 j = 0; j < entries.Count(); j++) + { + auto& e = entries[j]; + if (timeSeconds - e.LastTimeUsed >= Particles::ParticleBufferRecycleTimeout) + { + Delete(e.Buffer); + entries.RemoveAt(j--); + } + } + + if (entries.IsEmpty()) + Pool.Remove(i); + } + PoolLocker.Unlock(); } diff --git a/Source/Engine/Particles/Particles.h b/Source/Engine/Particles/Particles.h index ed66d7bb4..276eeb5af 100644 --- a/Source/Engine/Particles/Particles.h +++ b/Source/Engine/Particles/Particles.h @@ -2,6 +2,9 @@ #pragma once +#include "Engine/Scripting/ScriptingType.h" + +class TaskGraphSystem; struct RenderContext; struct RenderView; class ParticleEmitter; @@ -13,10 +16,17 @@ class SceneRenderTask; class Actor; /// -/// The particles service used for simulation and emitters data pooling. +/// The particles simulation service. /// -class FLAXENGINE_API Particles +API_CLASS(Static) class FLAXENGINE_API Particles { +DECLARE_SCRIPTING_TYPE_NO_SPAWN(Particles); + + /// + /// The system for Particles update. + /// + API_FIELD(ReadOnly) static TaskGraphSystem* System; + public: /// From 2068a584148891e5124f17f73c7cced2a9d188d3 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 16 Jun 2021 11:23:12 +0200 Subject: [PATCH 174/198] Fix using custom allocator on array field in scripting class --- Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 16ef34de8..722dc7158 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -2091,7 +2091,8 @@ namespace Flax.Build.Bindings header.Append(" for (int32 i = 0; i < data.Length(); i++)").AppendLine(); header.AppendFormat(" mono_array_set(result, {0}Managed, i, ToManaged(data[i]));", apiType.Name).AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" void ToNativeArray(Array<{0}>& result, MonoArray* data, int32 length)", fullName).AppendLine(); + header.Append(" template").AppendLine(); + header.AppendFormat(" void ToNativeArray(Array<{0}, AllocationType>& result, MonoArray* data, int32 length)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.Append(" for (int32 i = 0; i < length; i++)").AppendLine(); header.AppendFormat(" result.Add(ToNative(mono_array_get(data, {0}Managed, i)));", apiType.Name).AppendLine(); @@ -2271,7 +2272,8 @@ namespace Flax.Build.Bindings header.Append(" mono_array_setref(result, i, Box(data[i]));").AppendLine(); header.Append(" }").AppendLine(); - header.AppendFormat(" void ToNativeArray(Array<{0}>& result, MonoArray* data, int32 length)", fullName).AppendLine(); + header.Append(" template").AppendLine(); + header.AppendFormat(" void ToNativeArray(Array<{0}, AllocationType>& result, MonoArray* data, int32 length)", fullName).AppendLine(); header.Append(" {").AppendLine(); header.Append(" for (int32 i = 0; i < length; i++)").AppendLine(); header.AppendFormat(" Unbox(result[i], (MonoObject*)mono_array_addr_with_size(data, sizeof({0}Managed), length));", fullName).AppendLine(); From 1e08ed177b8a9e889d0dd5bfe45dc2b3fbd15b94 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 16 Jun 2021 12:05:56 +0200 Subject: [PATCH 175/198] Fix parsing doc comments in scripting api with inlined summary tag --- Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs index 8ed4a1634..9d0bd10e3 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Parsing.cs @@ -76,6 +76,10 @@ namespace Flax.Build.Bindings if (commentLine.StartsWith("// ")) commentLine = "/// " + commentLine.Substring(3); + // Fix inlined summary + if (commentLine.StartsWith("/// ") && commentLine.EndsWith("")) + commentLine = "/// " + commentLine.Substring(13, commentLine.Length - 23); + context.StringCache.Insert(0, commentLine); break; } From e8348f2c7ac097b5af5b9602745542db20aa1b76 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 16 Jun 2021 12:06:39 +0200 Subject: [PATCH 176/198] Fix default value generation for numbers and enums in scripting api --- .../Bindings/BindingsGenerator.CSharp.cs | 68 +++++++++++-------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs index d3d6eb0f6..a4952c7e0 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.CSharp.cs @@ -62,7 +62,39 @@ namespace Flax.Build.Bindings if (value.StartsWith("TEXT(\"") && value.EndsWith("\")")) return value.Substring(5, value.Length - 6); + // In-built constants + switch (value) + { + case "nullptr": + case "NULL": + case "String::Empty": + case "StringView::Empty": return "null"; + case "MAX_int8": return "sbyte.MaxValue"; + case "MAX_uint8": return "byte.MaxValue"; + case "MAX_int16": return "short.MaxValue"; + case "MAX_uint16": return "ushort.MaxValue"; + case "MAX_int32": return "int.MaxValue"; + case "MAX_uint32": return "uint.MaxValue"; + case "MAX_int64": return "long.MaxValue"; + case "MAX_uint64": return "ulong.MaxValue"; + case "MAX_float": return "float.MaxValue"; + case "MAX_double": return "double.MaxValue"; + case "true": + case "false": return value; + } + + // Numbers + if (float.TryParse(value, out _) || (value[value.Length - 1] == 'f' && float.TryParse(value.Substring(0, value.Length - 1), out _))) + return value; + value = value.Replace("::", "."); + var dot = value.LastIndexOf('.'); + ApiTypeInfo apiType = null; + if (dot != -1) + { + var type = new TypeInfo { Type = value.Substring(0, dot) }; + apiType = FindApiTypeInfo(buildData, type, caller); + } if (attribute) { @@ -95,45 +127,23 @@ namespace Flax.Build.Bindings case "Quaternion.Identity": return "typeof(Quaternion), \"0,0,0,1\""; } + // Enums + if (apiType != null && apiType.IsEnum) + return value; + return null; } } // Skip constants unsupported in C# - var dot = value.LastIndexOf('.'); - if (dot != -1) - { - var type = new TypeInfo { Type = value.Substring(0, dot) }; - var apiType = FindApiTypeInfo(buildData, type, caller); - if (apiType != null && apiType.IsStruct) - return null; - } + if (apiType != null && apiType.IsStruct) + return null; // Special case for value constructors if (value.Contains('(') && value.Contains(')')) return "new " + value; - // Convert from C++ to C# - switch (value) - { - case "nullptr": - case "NULL": - case "String.Empty": - case "StringView.Empty": return "null"; - - case "MAX_int8": return "sbyte.MaxValue"; - case "MAX_uint8": return "byte.MaxValue"; - case "MAX_int16": return "short.MaxValue"; - case "MAX_uint16": return "ushort.MaxValue"; - case "MAX_int32": return "int.MaxValue"; - case "MAX_uint32": return "uint.MaxValue"; - case "MAX_int64": return "long.MaxValue"; - case "MAX_uint64": return "ulong.MaxValue"; - case "MAX_float": return "float.MaxValue"; - case "MAX_double": return "double.MaxValue"; - - default: return value; - } + return value; } private static string GenerateCSharpNativeToManaged(BuildData buildData, TypeInfo typeInfo, ApiTypeInfo caller) From 4de3d134c95d4fdad29f681a3bbcb9b7bf9b4b23 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 16 Jun 2021 17:40:00 +0200 Subject: [PATCH 177/198] Fix generating includes list in c++ glue code for script (non-pod and variant wrappers can produce additional includes) --- .../Bindings/BindingsGenerator.Cpp.cs | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs index 722dc7158..d0b615f66 100644 --- a/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs +++ b/Source/Tools/Flax.Build/Bindings/BindingsGenerator.Cpp.cs @@ -1925,16 +1925,6 @@ namespace Flax.Build.Bindings { var header = new StringBuilder(); - // Includes - CppReferencesFiles.Remove(null); - CppIncludeFilesList.Clear(); - foreach (var fileInfo in CppReferencesFiles) - CppIncludeFilesList.Add(fileInfo.Name); - CppIncludeFilesList.AddRange(CppIncludeFiles); - CppIncludeFilesList.Sort(); - foreach (var path in CppIncludeFilesList) - header.AppendFormat("#include \"{0}\"", path).AppendLine(); - // Variant converting helper methods foreach (var typeInfo in CppVariantToTypes) { @@ -2284,6 +2274,18 @@ namespace Flax.Build.Bindings } contents.Insert(headerPos, header.ToString()); + + // Includes + header.Clear(); + CppReferencesFiles.Remove(null); + CppIncludeFilesList.Clear(); + foreach (var fileInfo in CppReferencesFiles) + CppIncludeFilesList.Add(fileInfo.Name); + CppIncludeFilesList.AddRange(CppIncludeFiles); + CppIncludeFilesList.Sort(); + foreach (var path in CppIncludeFilesList) + header.AppendFormat("#include \"{0}\"", path).AppendLine(); + contents.Insert(headerPos, header.ToString()); } Utilities.WriteFileIfChanged(bindings.GeneratedCppFilePath, contents.ToString()); From c2590fc0d9c45946f090f5fdceb6b87b9ed37a82 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 16 Jun 2021 19:02:02 +0200 Subject: [PATCH 178/198] Optimize multiple dispatches on JobSystem done via TaskGraph --- Source/Engine/Threading/JobSystem.cpp | 30 +++++++++++++++++++++++---- Source/Engine/Threading/JobSystem.h | 5 +++++ Source/Engine/Threading/TaskGraph.cpp | 2 ++ 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Threading/JobSystem.cpp b/Source/Engine/Threading/JobSystem.cpp index 08bbef410..c89afed8f 100644 --- a/Source/Engine/Threading/JobSystem.cpp +++ b/Source/Engine/Threading/JobSystem.cpp @@ -87,6 +87,7 @@ namespace JobSystemService JobSystemInstance; Thread* Threads[32] = {}; int32 ThreadsCount = 0; + bool JobStartingOnDispatch = true; volatile int64 ExitFlag = 0; volatile int64 DoneLabel = 0; volatile int64 NextLabel = 0; @@ -234,10 +235,13 @@ int64 JobSystem::Dispatch(const Function& job, int32 jobCount) LOG(Info, "Job enqueue time: {0} cycles", (int64)(Platform::GetTimeCycles() - start)); #endif - if (jobCount == 1) - JobsSignal.NotifyOne(); - else - JobsSignal.NotifyAll(); + if (JobStartingOnDispatch) + { + if (jobCount == 1) + JobsSignal.NotifyOne(); + else + JobsSignal.NotifyAll(); + } return label; #else @@ -278,3 +282,21 @@ void JobSystem::Wait(int64 label) #endif #endif } + +void JobSystem::SetJobStartingOnDispatch(bool value) +{ +#if JOB_SYSTEM_ENABLED + JobStartingOnDispatch = value; + + if (value) + { + JobsLocker.Lock(); + const int32 count = Jobs.Count(); + JobsLocker.Unlock(); + if (count == 1) + JobsSignal.NotifyOne(); + else if (count != 0) + JobsSignal.NotifyAll(); + } +#endif +} diff --git a/Source/Engine/Threading/JobSystem.h b/Source/Engine/Threading/JobSystem.h index cf9b79073..7a7e8cf7f 100644 --- a/Source/Engine/Threading/JobSystem.h +++ b/Source/Engine/Threading/JobSystem.h @@ -29,4 +29,9 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(JobSystem); /// /// The label. API_FUNCTION() static void Wait(int64 label); + + /// + /// Sets whether automatically start jobs execution on Dispatch. If disabled jobs won't be executed until it gets re-enabled. Can be used to optimize execution of multiple dispatches that should overlap. + /// + API_FUNCTION() static void SetJobStartingOnDispatch(bool value); }; diff --git a/Source/Engine/Threading/TaskGraph.cpp b/Source/Engine/Threading/TaskGraph.cpp index 621636203..b36a85e31 100644 --- a/Source/Engine/Threading/TaskGraph.cpp +++ b/Source/Engine/Threading/TaskGraph.cpp @@ -94,6 +94,7 @@ void TaskGraph::Execute() // Execute in order Sorting::QuickSort(_queue.Get(), _queue.Count(), &SortTaskGraphSystem); + JobSystem::SetJobStartingOnDispatch(false); _currentLabel = 0; for (int32 i = 0; i < _queue.Count(); i++) { @@ -104,6 +105,7 @@ void TaskGraph::Execute() _queue.Clear(); // Wait for async jobs to finish + JobSystem::SetJobStartingOnDispatch(true); JobSystem::Wait(_currentLabel); } From 1237946a8daa0ebdcee5d2eb1dc2274cbf846c27 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Wed, 16 Jun 2021 23:39:05 +0200 Subject: [PATCH 179/198] Fix synchronization for dbg symbols on Windows --- .../Platform/Windows/WindowsPlatform.cpp | 39 ++++++++++++++----- Source/ThirdParty/tracy/tracy.Build.cs | 2 + 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/Source/Engine/Platform/Windows/WindowsPlatform.cpp b/Source/Engine/Platform/Windows/WindowsPlatform.cpp index 89e050267..52b0bdaa3 100644 --- a/Source/Engine/Platform/Windows/WindowsPlatform.cpp +++ b/Source/Engine/Platform/Windows/WindowsPlatform.cpp @@ -29,6 +29,28 @@ const Char* WindowsPlatform::ApplicationWindowClass = TEXT("FlaxWindow"); void* WindowsPlatform::Instance = nullptr; +#if CRASH_LOG_ENABLE || TRACY_ENABLE +// Lock for symbols list, shared with Tracy +extern "C" { +static HANDLE dbgHelpLock; + +void DbgHelpInit() +{ + dbgHelpLock = CreateMutexW(nullptr, FALSE, nullptr); +} + +void DbgHelpLock() +{ + WaitForSingleObject(dbgHelpLock, INFINITE); +} + +void DbgHelpUnlock() +{ + ReleaseMutex(dbgHelpLock); +} +} +#endif + namespace { String UserLocale, ComputerName, UserName, WindowsName; @@ -39,7 +61,6 @@ namespace int32 VersionBuild = 0; int32 SystemDpi = 96; #if CRASH_LOG_ENABLE - CriticalSection SymLocker; #if TRACY_ENABLE bool SymInitialized = true; #else @@ -501,7 +522,7 @@ void WindowsPlatform::PreInit(void* hInstance) #if CRASH_LOG_ENABLE TCHAR buffer[MAX_PATH] = { 0 }; - SymLocker.Lock(); + DbgHelpLock(); if (::GetModuleFileNameW(::GetModuleHandleW(nullptr), buffer, MAX_PATH)) SymbolsPath.Add(StringUtils::GetDirectoryName(buffer)); if (::GetEnvironmentVariableW(TEXT("_NT_SYMBOL_PATH"), buffer, MAX_PATH)) @@ -510,7 +531,7 @@ void WindowsPlatform::PreInit(void* hInstance) options |= SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_DEFERRED_LOADS | SYMOPT_EXACT_SYMBOLS; SymSetOptions(options); OnSymbolsPathModified(); - SymLocker.Unlock(); + DbgHelpUnlock(); #endif GetWindowsVersion(WindowsName, VersionMajor, VersionMinor, VersionBuild); @@ -654,7 +675,7 @@ void WindowsPlatform::BeforeExit() void WindowsPlatform::Exit() { #if CRASH_LOG_ENABLE - SymLocker.Lock(); + DbgHelpLock(); #if !TRACY_ENABLE if (SymInitialized) { @@ -663,7 +684,7 @@ void WindowsPlatform::Exit() } #endif SymbolsPath.Resize(0); - SymLocker.Unlock(); + DbgHelpUnlock(); #endif // Unregister app class @@ -1170,13 +1191,13 @@ void* WindowsPlatform::LoadLibrary(const Char* filename) #if CRASH_LOG_ENABLE // Refresh modules info during next stack trace collecting to have valid debug symbols information - SymLocker.Lock(); + DbgHelpLock(); if (folder.HasChars() && !SymbolsPath.Contains(folder)) { SymbolsPath.Add(folder); OnSymbolsPathModified(); } - SymLocker.Unlock(); + DbgHelpUnlock(); #endif return handle; @@ -1186,7 +1207,7 @@ Array WindowsPlatform::GetStackFrames(int32 skipCount, { Array result; #if CRASH_LOG_ENABLE - SymLocker.Lock(); + DbgHelpLock(); // Initialize HANDLE process = GetCurrentProcess(); @@ -1310,7 +1331,7 @@ Array WindowsPlatform::GetStackFrames(int32 skipCount, } } - SymLocker.Unlock(); + DbgHelpUnlock(); #endif return result; } diff --git a/Source/ThirdParty/tracy/tracy.Build.cs b/Source/ThirdParty/tracy/tracy.Build.cs index aa10c514f..abcc83da4 100644 --- a/Source/ThirdParty/tracy/tracy.Build.cs +++ b/Source/ThirdParty/tracy/tracy.Build.cs @@ -33,6 +33,8 @@ public class tracy : ThirdPartyModule options.SourceFiles.Add(Path.Combine(FolderPath, "TracyClient.cpp")); options.PublicDefinitions.Add("TRACY_ENABLE"); + if (options.Platform.Target == TargetPlatform.Windows) + options.PrivateDefinitions.Add("TRACY_DBGHELP_LOCK=DbgHelp"); } /// From f0cb979c5e7ca8e57e7f9861014d5eb6c63e73bc Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 17 Jun 2021 14:09:01 +0200 Subject: [PATCH 180/198] Add Nameof helper utility --- Source/Engine/Utilities/Nameof.h | 95 ++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 Source/Engine/Utilities/Nameof.h diff --git a/Source/Engine/Utilities/Nameof.h b/Source/Engine/Utilities/Nameof.h new file mode 100644 index 000000000..8a50b7e39 --- /dev/null +++ b/Source/Engine/Utilities/Nameof.h @@ -0,0 +1,95 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Core/Types/StringView.h" + +// Helper utility to get type name at compile time. Example: +// constexpr StringAnsiView name = Nameof::Get() + +namespace Nameof +{ + // [Reference: https://github.com/Neargye/nameof] + constexpr StringAnsiView PrettyName(StringAnsiView name) noexcept + { + if (name.Length() >= 1 && (name.Get()[0] == '"' || name.Get()[0] == '\'')) + return {}; + if (name.Length() >= 2 && name.Get()[0] == 'R' && (name.Get()[1] == '"' || name.Get()[1] == '\'')) + return {}; + if (name.Length() >= 2 && name.Get()[0] == 'L' && (name.Get()[1] == '"' || name.Get()[1] == '\'')) + return {}; + if (name.Length() >= 2 && name.Get()[0] == 'U' && (name.Get()[1] == '"' || name.Get()[1] == '\'')) + return {}; + if (name.Length() >= 2 && name.Get()[0] == 'u' && (name.Get()[1] == '"' || name.Get()[1] == '\'')) + return {}; + if (name.Length() >= 3 && name.Get()[0] == 'u' && name.Get()[1] == '8' && (name.Get()[2] == '"' || name.Get()[2] == '\'')) + return {}; + if (name.Length() >= 1 && (name.Get()[0] >= '0' && name.Get()[0] <= '9')) + return {}; + for (int32 i = name.Length(), h = 0, s = 0; i > 0; --i) + { + if (name.Get()[i - 1] == ')') + { + ++h; + ++s; + continue; + } + if (name.Get()[i - 1] == '(') + { + --h; + ++s; + continue; + } + if (h == 0) + { + name = StringAnsiView(*name, name.Length() - s); + break; + } + ++s; + } + int32 s = 0; + for (int32 i = name.Length(), h = 0; i > 0; --i) + { + if (name.Get()[i - 1] == '>') + { + ++h; + ++s; + continue; + } + if (name.Get()[i - 1] == '<') + { + --h; + ++s; + continue; + } + if (h == 0) + break; + ++s; + } + for (int32 i = name.Length() - s; i > 0; --i) + { + if (!((name.Get()[i - 1] >= '0' && name.Get()[i - 1] <= '9') || (name.Get()[i - 1] >= 'a' && name.Get()[i - 1] <= 'z') || (name.Get()[i - 1] >= 'A' && name.Get()[i - 1] <= 'Z') || (name.Get()[i - 1] == '_'))) + { + name = StringAnsiView(*name + i, name.Length() - i); + break; + } + } + name = StringAnsiView(*name, name.Length() - s); + if (name.Length() > 0 && ((name.Get()[0] >= 'a' && name.Get()[0] <= 'z') || (name.Get()[0] >= 'A' && name.Get()[0] <= 'Z') || (name.Get()[0] == '_'))) + return name; + return {}; + } + + // Gets the type name as constant expression (compile-time string) + template + constexpr StringAnsiView Get() noexcept + { +#if defined(__clang__) || defined(__GNUC__) + return PrettyName(StringAnsiView(__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2)); +#elif defined(_MSC_VER) + return PrettyName(StringAnsiView(__FUNCSIG__, sizeof(__FUNCSIG__) - 17)); +#else +#error "Unsupported compiler." +#endif + } +} From 5ca2729a56928420c29cb811341f396f86117b0a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 17 Jun 2021 14:11:25 +0200 Subject: [PATCH 181/198] Fix UI after editing Dictionary key value --- Source/Editor/CustomEditors/Editors/DictionaryEditor.cs | 9 +++++++++ Source/Editor/CustomEditors/Editors/FloatEditor.cs | 2 +- Source/Editor/CustomEditors/Editors/IntegerEditor.cs | 9 ++++++++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs index ba66aa0fa..51309a2fa 100644 --- a/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs +++ b/Source/Editor/CustomEditors/Editors/DictionaryEditor.cs @@ -140,6 +140,7 @@ namespace FlaxEditor.CustomEditors.Editors private bool _readOnly; private bool _notNullItems; private bool _canEditKeys; + private bool _keyEdited; /// /// Determines whether this editor[can edit the specified dictionary type. @@ -351,6 +352,7 @@ namespace FlaxEditor.CustomEditors.Editors newValues[e] = dictionary[e]; } SetValue(newValues); + _keyEdited = true; // TODO: use custom UndoAction to rebuild UI after key modification } /// @@ -463,6 +465,13 @@ namespace FlaxEditor.CustomEditors.Editors /// public override void Refresh() { + if (_keyEdited) + { + _keyEdited = false; + RebuildLayout(); + RebuildParentCollection(); + } + base.Refresh(); // No support for different collections for now diff --git a/Source/Editor/CustomEditors/Editors/FloatEditor.cs b/Source/Editor/CustomEditors/Editors/FloatEditor.cs index 63ef35ca9..456eae9ee 100644 --- a/Source/Editor/CustomEditors/Editors/FloatEditor.cs +++ b/Source/Editor/CustomEditors/Editors/FloatEditor.cs @@ -89,7 +89,7 @@ namespace FlaxEditor.CustomEditors.Editors else if (value is double asDouble) _element.Value = (float)asDouble; else - throw new Exception("Invalid value."); + throw new Exception(string.Format("Invalid value type {0}.", value?.GetType().ToString() ?? "")); } } } diff --git a/Source/Editor/CustomEditors/Editors/IntegerEditor.cs b/Source/Editor/CustomEditors/Editors/IntegerEditor.cs index 3c2ca8872..5822b1c83 100644 --- a/Source/Editor/CustomEditors/Editors/IntegerEditor.cs +++ b/Source/Editor/CustomEditors/Editors/IntegerEditor.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System; using System.Linq; using FlaxEditor.CustomEditors.Elements; using FlaxEngine; @@ -77,7 +78,13 @@ namespace FlaxEditor.CustomEditors.Editors } else { - _element.Value = (int)Values[0]; + var value = Values[0]; + if (value is int asInt) + _element.Value = asInt; + else if (value is float asFloat) + _element.Value = (int)asFloat; + else + throw new Exception(string.Format("Invalid value type {0}.", value?.GetType().ToString() ?? "")); } } } From aa83af477a2fbc89cc11e52cad23db795e750f68 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 17 Jun 2021 14:14:10 +0200 Subject: [PATCH 182/198] Add `GetStringAnsiView` to json value --- Source/Engine/Content/Assets/VisualScript.cpp | 2 +- Source/Engine/Content/JsonAsset.cpp | 2 +- Source/Engine/Level/SceneObjectsFactory.cpp | 2 +- Source/Engine/Serialization/Json.h | 1 + Source/Engine/Serialization/Serialization.cpp | 6 +++--- Source/Engine/Serialization/Serialization.h | 2 +- Source/ThirdParty/rapidjson/document.h | 1 + 7 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/Engine/Content/Assets/VisualScript.cpp b/Source/Engine/Content/Assets/VisualScript.cpp index ea8f6ad6c..0d523b67f 100644 --- a/Source/Engine/Content/Assets/VisualScript.cpp +++ b/Source/Engine/Content/Assets/VisualScript.cpp @@ -2066,7 +2066,7 @@ void VisualScriptingBinaryModule::DeserializeObject(ISerializable::DeserializeSt auto& params = instanceParams->Value.Params; for (auto i = stream.MemberBegin(); i != stream.MemberEnd(); ++i) { - StringAnsiView idNameAnsi(i->name.GetString(), i->name.GetStringLength()); + StringAnsiView idNameAnsi(i->name.GetStringAnsiView()); Guid paramId; if (!Guid::Parse(idNameAnsi, paramId)) { diff --git a/Source/Engine/Content/JsonAsset.cpp b/Source/Engine/Content/JsonAsset.cpp index 44baecb12..c9a07e665 100644 --- a/Source/Engine/Content/JsonAsset.cpp +++ b/Source/Engine/Content/JsonAsset.cpp @@ -75,7 +75,7 @@ void FindIds(ISerializable::DeserializeStream& node, Array& output) { // Try parse as Guid in format `N` (32 hex chars) Guid id; - if (!Guid::Parse(node.GetText(), id)) + if (!Guid::Parse(node.GetStringAnsiView(), id)) output.Add(id); } } diff --git a/Source/Engine/Level/SceneObjectsFactory.cpp b/Source/Engine/Level/SceneObjectsFactory.cpp index 85cbc930f..321b0f6f1 100644 --- a/Source/Engine/Level/SceneObjectsFactory.cpp +++ b/Source/Engine/Level/SceneObjectsFactory.cpp @@ -74,7 +74,7 @@ SceneObject* SceneObjectsFactory::Spawn(ISerializable::DeserializeStream& stream LOG(Warning, "Invalid object type (TypeName must be an object type full name string)."); return nullptr; } - const StringAnsiView typeName(typeNameMember->value.GetString(), typeNameMember->value.GetStringLength()); + const StringAnsiView typeName(typeNameMember->value.GetStringAnsiView()); const ScriptingTypeHandle type = Scripting::FindScriptingType(typeName); if (type) diff --git a/Source/Engine/Serialization/Json.h b/Source/Engine/Serialization/Json.h index 4d47d743e..880796345 100644 --- a/Source/Engine/Serialization/Json.h +++ b/Source/Engine/Serialization/Json.h @@ -3,6 +3,7 @@ #pragma once #include "Engine/Core/Types/String.h" +#include "Engine/Core/Types/StringView.h" // TODO: config RAPIDJSON_SSE2 for rapidjson #define RAPIDJSON_ERROR_CHARTYPE Char diff --git a/Source/Engine/Serialization/Serialization.cpp b/Source/Engine/Serialization/Serialization.cpp index bafc52077..1b0e0b45c 100644 --- a/Source/Engine/Serialization/Serialization.cpp +++ b/Source/Engine/Serialization/Serialization.cpp @@ -74,7 +74,7 @@ void Serialization::Deserialize(ISerializable::DeserializeStream& stream, Varian v.Type = VariantType::Null; const auto mTypeName = SERIALIZE_FIND_MEMBER(stream, "TypeName"); if (mTypeName != stream.MemberEnd() && mTypeName->value.IsString()) - v.SetTypeName(StringAnsiView(mTypeName->value.GetString(), mTypeName->value.GetStringLength())); + v.SetTypeName(StringAnsiView(mTypeName->value.GetStringAnsiView())); } else { @@ -257,7 +257,7 @@ void Serialization::Deserialize(ISerializable::DeserializeStream& stream, Varian break; case VariantType::String: CHECK(value.IsString()); - v.SetString(StringAnsiView(value.GetString(), value.GetStringLength())); + v.SetString(value.GetStringAnsiView()); break; case VariantType::Object: Deserialize(value, id, modifier); @@ -328,7 +328,7 @@ void Serialization::Deserialize(ISerializable::DeserializeStream& stream, Varian break; case VariantType::Typename: CHECK(value.IsString()); - v.SetTypename(StringAnsiView(value.GetString(), value.GetStringLength())); + v.SetTypename(value.GetStringAnsiView()); break; default: Platform::CheckFailed("", __FILE__, __LINE__); diff --git a/Source/Engine/Serialization/Serialization.h b/Source/Engine/Serialization/Serialization.h index 1413525e7..4ccbe533c 100644 --- a/Source/Engine/Serialization/Serialization.h +++ b/Source/Engine/Serialization/Serialization.h @@ -536,7 +536,7 @@ namespace Serialization else if (stream.IsString()) { // byte[] encoded as Base64 - const StringAnsiView streamView(stream.GetString(), stream.GetStringLength()); + const StringAnsiView streamView(stream.GetStringAnsiView()); v.Resize(Encryption::Base64DecodeLength(*streamView, streamView.Length())); Encryption::Base64Decode(*streamView, streamView.Length(), v.Get()); } diff --git a/Source/ThirdParty/rapidjson/document.h b/Source/ThirdParty/rapidjson/document.h index a6a6e4d9c..9d9fe2eb4 100644 --- a/Source/ThirdParty/rapidjson/document.h +++ b/Source/ThirdParty/rapidjson/document.h @@ -1648,6 +1648,7 @@ public: //@{ const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } + ::StringAnsiView GetStringAnsiView() const { RAPIDJSON_ASSERT(IsString()); return data_.f.flags & kInlineStrFlag ? ::StringAnsiView(data_.ss.str, data_.ss.GetLength()) : ::StringAnsiView(GetStringPointer(), data_.s.length); } ::String GetText() const { ::String result; From 99012725dcaed0400c00e5da3fb9d131083044c5 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 17 Jun 2021 14:15:19 +0200 Subject: [PATCH 183/198] Adjust includes --- Source/Editor/Scripting/ScriptsBuilder.cpp | 2 +- Source/Editor/Tools/Terrain/TerrainTools.cpp | 1 + Source/Engine/Audio/AudioSource.cpp | 1 + Source/Engine/Core/Types/String.h | 1 - Source/Engine/Core/Types/StringView.h | 63 ++++++++++++------- Source/Engine/Foliage/Foliage.cpp | 1 + .../Engine/Graphics/Textures/TextureBase.cpp | 3 +- Source/Engine/Level/Actors/SkyLight.cpp | 1 + Source/Engine/Level/Scene/SceneCSGData.cpp | 1 + Source/Engine/Navigation/NavMesh.cpp | 1 + .../Engine/Physics/Actors/WheeledVehicle.cpp | 1 + .../Scripting/ManagedCLR/MClass.Mono.cpp | 5 -- Source/Engine/Scripting/ManagedCLR/MClass.h | 6 -- Source/Engine/Scripting/Scripting.cpp | 12 ---- Source/Engine/Scripting/Scripting.h | 6 +- Source/Engine/Scripting/ScriptingObject.cpp | 2 +- .../Engine/ShadowsOfMordor/Builder.DoWork.cpp | 1 + .../ShadowsOfMordor/Builder.Entries.cpp | 1 + .../ShadowsOfMordor/Builder.Hemispheres.cpp | 1 + Source/Engine/ShadowsOfMordor/Builder.h | 1 + Source/Engine/Terrain/Terrain.cpp | 1 + 21 files changed, 57 insertions(+), 55 deletions(-) diff --git a/Source/Editor/Scripting/ScriptsBuilder.cpp b/Source/Editor/Scripting/ScriptsBuilder.cpp index 71323dc34..ed30922f7 100644 --- a/Source/Editor/Scripting/ScriptsBuilder.cpp +++ b/Source/Editor/Scripting/ScriptsBuilder.cpp @@ -304,7 +304,7 @@ MClass* ScriptsBuilder::FindScript(const StringView& scriptName) GetClassName(mclass->GetFullName(), mclassName); if (className == mclassName) { - LOG(Info, "Found {0} type for type {1} (assembly {2})", mclass->ToString(), String(scriptName.Get(), scriptName.Length()), assembly->ToString()); + LOG(Info, "Found {0} type for type {1} (assembly {2})", String(mclass->GetFullName()), String(scriptName.Get(), scriptName.Length()), assembly->ToString()); return mclass; } } diff --git a/Source/Editor/Tools/Terrain/TerrainTools.cpp b/Source/Editor/Tools/Terrain/TerrainTools.cpp index e89b5e3e9..fb90d786a 100644 --- a/Source/Editor/Tools/Terrain/TerrainTools.cpp +++ b/Source/Editor/Tools/Terrain/TerrainTools.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "TerrainTools.h" +#include "Engine/Core/Log.h" #include "Engine/Core/Cache.h" #include "Engine/Core/Math/Int2.h" #include "Engine/Core/Math/Color32.h" diff --git a/Source/Engine/Audio/AudioSource.cpp b/Source/Engine/Audio/AudioSource.cpp index 192fd845f..2b82ec6ea 100644 --- a/Source/Engine/Audio/AudioSource.cpp +++ b/Source/Engine/Audio/AudioSource.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "AudioSource.h" +#include "Engine/Core/Log.h" #include "Engine/Level/SceneObjectsFactory.h" #include "Engine/Serialization/Serialization.h" #include "Engine/Graphics/RenderTask.h" diff --git a/Source/Engine/Core/Types/String.h b/Source/Engine/Core/Types/String.h index d7d42f211..6871cb287 100644 --- a/Source/Engine/Core/Types/String.h +++ b/Source/Engine/Core/Types/String.h @@ -5,7 +5,6 @@ #include "Engine/Platform/Platform.h" #include "Engine/Platform/StringUtils.h" #include "Engine/Core/Formatting.h" -#include "Engine/Core/Templates.h" /// /// Represents text as a sequence of characters. Container uses a single dynamic memory allocation to store the characters data. Characters sequence is always null-terminated. diff --git a/Source/Engine/Core/Types/StringView.h b/Source/Engine/Core/Types/StringView.h index e267b760e..bb987b3b4 100644 --- a/Source/Engine/Core/Types/StringView.h +++ b/Source/Engine/Core/Types/StringView.h @@ -19,21 +19,6 @@ protected: public: - /// - /// Copies the data. - /// - /// The other object. - /// The reference to this object. - FORCE_INLINE StringViewBase& operator=(const StringViewBase& other) - { - if (this != &other) - { - _data = other._data; - _length = other._length; - } - return *this; - } - /// /// Gets the specific const character from this string. /// @@ -82,7 +67,7 @@ public: /// /// Gets the length of the string. /// - FORCE_INLINE int32 Length() const + FORCE_INLINE constexpr int32 Length() const { return _length; } @@ -90,7 +75,7 @@ public: /// /// Gets the pointer to the string. /// - FORCE_INLINE const T* operator*() const + FORCE_INLINE constexpr const T* operator*() const { return _data; } @@ -98,7 +83,7 @@ public: /// /// Gets the pointer to the string. /// - FORCE_INLINE const T* Get() const + FORCE_INLINE constexpr const T* Get() const { return _data; } @@ -202,7 +187,7 @@ public: /// /// Initializes a new instance of the class. /// - StringView() + constexpr StringView() { _data = nullptr; _length = 0; @@ -218,7 +203,7 @@ public: /// Initializes a new instance of the class. /// /// The reference to the static string. - StringView(const StringView& str) + constexpr StringView(const StringView& str) { _data = str._data; _length = str._length; @@ -239,7 +224,7 @@ public: /// /// The characters sequence. /// The characters sequence length (excluding null-terminator character). - StringView(const Char* str, int32 length) + constexpr StringView(const Char* str, int32 length) { _data = str; _length = length; @@ -259,6 +244,21 @@ public: return *this; } + /// + /// Assigns the static string. + /// + /// The other object. + /// The reference to this object. + FORCE_INLINE constexpr StringView& operator=(const StringView& other) + { + if (this != &other) + { + _data = other._data; + _length = other._length; + } + return *this; + } + /// /// Lexicographically test whether this string is equivalent to the other given string (case sensitive). /// @@ -394,7 +394,7 @@ public: /// /// Initializes a new instance of the class. /// - StringAnsiView() + constexpr StringAnsiView() { _data = nullptr; _length = 0; @@ -410,7 +410,7 @@ public: /// Initializes a new instance of the class. /// /// The reference to the static string. - StringAnsiView(const StringAnsiView& str) + constexpr StringAnsiView(const StringAnsiView& str) { _data = str._data; _length = str._length; @@ -431,7 +431,7 @@ public: /// /// The characters sequence. /// The characters sequence length (excluding null-terminator character). - StringAnsiView(const char* str, int32 length) + constexpr StringAnsiView(const char* str, int32 length) { _data = str; _length = length; @@ -451,6 +451,21 @@ public: return *this; } + /// + /// Assigns the static string. + /// + /// The other object. + /// The reference to this object. + FORCE_INLINE constexpr StringAnsiView& operator=(const StringAnsiView& other) + { + if (this != &other) + { + _data = other._data; + _length = other._length; + } + return *this; + } + /// /// Lexicographically test whether this string is equivalent to the other given string (case sensitive). /// diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index bb809d4e0..84b0573d8 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -3,6 +3,7 @@ #include "Foliage.h" #include "FoliageType.h" #include "FoliageCluster.h" +#include "Engine/Core/Log.h" #include "Engine/Core/Random.h" #include "Engine/Engine/Engine.h" #include "Engine/Graphics/RenderTask.h" diff --git a/Source/Engine/Graphics/Textures/TextureBase.cpp b/Source/Engine/Graphics/Textures/TextureBase.cpp index d21d61f2f..af5d96394 100644 --- a/Source/Engine/Graphics/Textures/TextureBase.cpp +++ b/Source/Engine/Graphics/Textures/TextureBase.cpp @@ -2,16 +2,15 @@ #include "TextureBase.h" #include "TextureData.h" +#include "Engine/Core/Math/Color32.h" #include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/Textures/GPUTexture.h" #include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/PixelFormatExtensions.h" #include "Engine/Debug/Exceptions/ArgumentOutOfRangeException.h" #include "Engine/Debug/Exceptions/InvalidOperationException.h" -#include "Engine/Core/Math/Color32.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Content/Factories/BinaryAssetFactory.h" -#include REGISTER_BINARY_ASSET_ABSTRACT(TextureBase, "FlaxEngine.TextureBase"); diff --git a/Source/Engine/Level/Actors/SkyLight.cpp b/Source/Engine/Level/Actors/SkyLight.cpp index da7efbf31..94ccf6c29 100644 --- a/Source/Engine/Level/Actors/SkyLight.cpp +++ b/Source/Engine/Level/Actors/SkyLight.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "SkyLight.h" +#include "Engine/Core/Log.h" #include "Engine/Platform/FileSystem.h" #include "Engine/Graphics/RenderView.h" #include "Engine/Graphics/RenderTask.h" diff --git a/Source/Engine/Level/Scene/SceneCSGData.cpp b/Source/Engine/Level/Scene/SceneCSGData.cpp index 2ac5121b5..2e4ecc3b3 100644 --- a/Source/Engine/Level/Scene/SceneCSGData.cpp +++ b/Source/Engine/Level/Scene/SceneCSGData.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "SceneCSGData.h" +#include "Engine/Core/Log.h" #include "Engine/CSG/CSGBuilder.h" #include "Engine/Serialization/Serialization.h" #include "Engine/Level/Scene/Scene.h" diff --git a/Source/Engine/Navigation/NavMesh.cpp b/Source/Engine/Navigation/NavMesh.cpp index fe7756049..32ba8c61f 100644 --- a/Source/Engine/Navigation/NavMesh.cpp +++ b/Source/Engine/Navigation/NavMesh.cpp @@ -5,6 +5,7 @@ #include "Engine/Level/Scene/Scene.h" #include "Engine/Serialization/Serialization.h" #if COMPILE_WITH_ASSETS_IMPORTER +#include "Engine/Core/Log.h" #include "Engine/ContentImporters/AssetsImportingManager.h" #include "Engine/Serialization/MemoryWriteStream.h" #if USE_EDITOR diff --git a/Source/Engine/Physics/Actors/WheeledVehicle.cpp b/Source/Engine/Physics/Actors/WheeledVehicle.cpp index 558e4f98c..0fe53f570 100644 --- a/Source/Engine/Physics/Actors/WheeledVehicle.cpp +++ b/Source/Engine/Physics/Actors/WheeledVehicle.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "WheeledVehicle.h" +#include "Engine/Core/Log.h" #include "Engine/Physics/Physics.h" #include "Engine/Physics/Utilities.h" #include "Engine/Level/Scene/Scene.h" diff --git a/Source/Engine/Scripting/ManagedCLR/MClass.Mono.cpp b/Source/Engine/Scripting/ManagedCLR/MClass.Mono.cpp index 21551afc0..b02580ab8 100644 --- a/Source/Engine/Scripting/ManagedCLR/MClass.Mono.cpp +++ b/Source/Engine/Scripting/ManagedCLR/MClass.Mono.cpp @@ -80,11 +80,6 @@ MClass::~MClass() _events.ClearDelete(); } -String MClass::ToString() const -{ - return String(_fullname); -} - MonoClass* MClass::GetNative() const { return _monoClass; diff --git a/Source/Engine/Scripting/ManagedCLR/MClass.h b/Source/Engine/Scripting/ManagedCLR/MClass.h index a8d083d7b..e4d0ba74c 100644 --- a/Source/Engine/Scripting/ManagedCLR/MClass.h +++ b/Source/Engine/Scripting/ManagedCLR/MClass.h @@ -73,12 +73,6 @@ public: return _fullname; } - /// - /// Gets the class full name as string. - /// - /// The class full name. - String ToString() const; - #if USE_MONO /// diff --git a/Source/Engine/Scripting/Scripting.cpp b/Source/Engine/Scripting/Scripting.cpp index 2888497c7..55a60f799 100644 --- a/Source/Engine/Scripting/Scripting.cpp +++ b/Source/Engine/Scripting/Scripting.cpp @@ -639,9 +639,7 @@ MClass* Scripting::FindClass(MonoClass* monoClass) { if (monoClass == nullptr) return nullptr; - PROFILE_CPU(); - auto& modules = BinaryModule::GetModules(); for (auto module : modules) { @@ -653,7 +651,6 @@ MClass* Scripting::FindClass(MonoClass* monoClass) return result; } } - return nullptr; } @@ -661,9 +658,7 @@ MClass* Scripting::FindClass(const StringAnsiView& fullname) { if (fullname.IsEmpty()) return nullptr; - PROFILE_CPU(); - auto& modules = BinaryModule::GetModules(); for (auto module : modules) { @@ -675,7 +670,6 @@ MClass* Scripting::FindClass(const StringAnsiView& fullname) return result; } } - return nullptr; } @@ -683,9 +677,7 @@ MonoClass* Scripting::FindClassNative(const StringAnsiView& fullname) { if (fullname.IsEmpty()) return nullptr; - PROFILE_CPU(); - auto& modules = BinaryModule::GetModules(); for (auto module : modules) { @@ -697,7 +689,6 @@ MonoClass* Scripting::FindClassNative(const StringAnsiView& fullname) return result->GetNative(); } } - return nullptr; } @@ -705,9 +696,7 @@ ScriptingTypeHandle Scripting::FindScriptingType(const StringAnsiView& fullname) { if (fullname.IsEmpty()) return ScriptingTypeHandle(); - PROFILE_CPU(); - auto& modules = BinaryModule::GetModules(); for (auto module : modules) { @@ -717,7 +706,6 @@ ScriptingTypeHandle Scripting::FindScriptingType(const StringAnsiView& fullname) return ScriptingTypeHandle(module, typeIndex); } } - return ScriptingTypeHandle(); } diff --git a/Source/Engine/Scripting/Scripting.h b/Source/Engine/Scripting/Scripting.h index 98fddfc7f..fcdd3746c 100644 --- a/Source/Engine/Scripting/Scripting.h +++ b/Source/Engine/Scripting/Scripting.h @@ -91,21 +91,21 @@ public: /// /// Finds the class with given fully qualified name within whole assembly. /// - /// The full name of the type eg: System.Int64.MaxInt. + /// The full name of the type eg: System.Int64. /// The MClass object or null if missing. static MClass* FindClass(const StringAnsiView& fullname); /// /// Finds the native class with given fully qualified name within whole assembly. /// - /// The full name of the type eg: System.Int64.MaxInt. + /// The full name of the type eg: System.Int64. /// The MClass object or null if missing. static MonoClass* FindClassNative(const StringAnsiView& fullname); /// /// Finds the scripting type of the given fullname by searching loaded scripting assemblies. /// - /// The full name of the type eg: System.Int64.MaxInt. + /// The full name of the type eg: System.Int64. /// The scripting type or invalid type if missing. static ScriptingTypeHandle FindScriptingType(const StringAnsiView& fullname); diff --git a/Source/Engine/Scripting/ScriptingObject.cpp b/Source/Engine/Scripting/ScriptingObject.cpp index 5208b1560..a66b8a103 100644 --- a/Source/Engine/Scripting/ScriptingObject.cpp +++ b/Source/Engine/Scripting/ScriptingObject.cpp @@ -259,7 +259,7 @@ void ScriptingObject::OnDeleteObject() String ScriptingObject::ToString() const { - return _type ? _type.GetType().ManagedClass->ToString() : String::Empty; + return _type ? String(_type.GetType().ManagedClass->GetFullName()) : String::Empty; } ManagedScriptingObject::ManagedScriptingObject(const SpawnParams& params) diff --git a/Source/Engine/ShadowsOfMordor/Builder.DoWork.cpp b/Source/Engine/ShadowsOfMordor/Builder.DoWork.cpp index fcacc9de8..74e46a03f 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.DoWork.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.DoWork.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "Builder.h" +#include "Engine/Core/Log.h" #include "Engine/Core/Types/TimeSpan.h" #include "Engine/Core/Math/Math.h" #include "Engine/Level/Actors/BoxBrush.h" diff --git a/Source/Engine/ShadowsOfMordor/Builder.Entries.cpp b/Source/Engine/ShadowsOfMordor/Builder.Entries.cpp index b14937338..445555b69 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Entries.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Entries.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "Builder.h" +#include "Engine/Core/Log.h" #include "Engine/Core/Math/Math.h" #include "Engine/Level/Actors/BoxBrush.h" #include "Engine/Level/Actors/StaticModel.h" diff --git a/Source/Engine/ShadowsOfMordor/Builder.Hemispheres.cpp b/Source/Engine/ShadowsOfMordor/Builder.Hemispheres.cpp index f04d790a8..edad95799 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.Hemispheres.cpp +++ b/Source/Engine/ShadowsOfMordor/Builder.Hemispheres.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "Builder.h" +#include "Engine/Core/Log.h" #include "Engine/Core/Math/Math.h" #include "Engine/Level/SceneQuery.h" #include "Engine/Level/Actors/BoxBrush.h" diff --git a/Source/Engine/ShadowsOfMordor/Builder.h b/Source/Engine/ShadowsOfMordor/Builder.h index c8c84f5bf..6d07a0587 100644 --- a/Source/Engine/ShadowsOfMordor/Builder.h +++ b/Source/Engine/ShadowsOfMordor/Builder.h @@ -10,6 +10,7 @@ #if COMPILE_WITH_GI_BAKING #include "Engine/Graphics/RenderTask.h" +#include "Engine/Core/Singleton.h" // Forward declarations #if COMPILE_WITH_ASSETS_IMPORTER diff --git a/Source/Engine/Terrain/Terrain.cpp b/Source/Engine/Terrain/Terrain.cpp index 2ff053432..8aff910f3 100644 --- a/Source/Engine/Terrain/Terrain.cpp +++ b/Source/Engine/Terrain/Terrain.cpp @@ -2,6 +2,7 @@ #include "Terrain.h" #include "TerrainPatch.h" +#include "Engine/Core/Log.h" #include "Engine/Core/Math/Ray.h" #include "Engine/Level/Scene/SceneRendering.h" #include "Engine/Serialization/Serialization.h" From b510e3fcc59fd5a116954aef5ed430709bc487fe Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 17 Jun 2021 14:17:04 +0200 Subject: [PATCH 184/198] Add support for loading C# dictionaries in native json reader --- .../ExtendedDefaultContractResolver.cs | 27 +++++++++ Source/Engine/Serialization/Serialization.h | 55 ++++++++++++++----- 2 files changed, 67 insertions(+), 15 deletions(-) diff --git a/Source/Engine/Serialization/JsonCustomSerializers/ExtendedDefaultContractResolver.cs b/Source/Engine/Serialization/JsonCustomSerializers/ExtendedDefaultContractResolver.cs index cc07597e8..2018015a9 100644 --- a/Source/Engine/Serialization/JsonCustomSerializers/ExtendedDefaultContractResolver.cs +++ b/Source/Engine/Serialization/JsonCustomSerializers/ExtendedDefaultContractResolver.cs @@ -47,6 +47,33 @@ namespace FlaxEngine.Json.JsonCustomSerializers return contract; } + /// + protected override JsonDictionaryContract CreateDictionaryContract(Type objectType) + { + var contract = base.CreateDictionaryContract(objectType); + + // Override contract to save enums keys as integer + if (contract.DictionaryKeyType?.IsEnum ?? false) + { + var enumType = contract.DictionaryKeyType; + contract.DictionaryKeyResolver = name => + { + try + { + var e = Enum.Parse(enumType, name); + name = Convert.ToInt32(e).ToString(); + } + catch + { + // Ignore errors + } + return name; + }; + } + + return contract; + } + protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) { var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); diff --git a/Source/Engine/Serialization/Serialization.h b/Source/Engine/Serialization/Serialization.h index 4ccbe533c..41517da58 100644 --- a/Source/Engine/Serialization/Serialization.h +++ b/Source/Engine/Serialization/Serialization.h @@ -18,6 +18,18 @@ struct VariantType; namespace Serialization { + inline int32 DeserializeInt(ISerializable::DeserializeStream& stream) + { + int32 result = 0; + if (stream.IsInt()) + result = stream.GetInt(); + else if (stream.IsFloat()) + result = (int32)stream.GetFloat(); + else if (stream.IsString()) + StringUtils::Parse(stream.GetString(), &result); + return result; + } + // In-build types inline bool ShouldSerialize(const bool& v, const void* otherObj) @@ -43,7 +55,7 @@ namespace Serialization } inline void Deserialize(ISerializable::DeserializeStream& stream, int8& v, ISerializeModifier* modifier) { - v = stream.GetInt(); + v = (int8)DeserializeInt(stream); } inline bool ShouldSerialize(const char& v, const void* otherObj) @@ -82,7 +94,7 @@ namespace Serialization } inline void Deserialize(ISerializable::DeserializeStream& stream, int16& v, ISerializeModifier* modifier) { - v = stream.GetInt(); + v = (int16)DeserializeInt(stream); } inline bool ShouldSerialize(const int32& v, const void* otherObj) @@ -95,7 +107,7 @@ namespace Serialization } inline void Deserialize(ISerializable::DeserializeStream& stream, int32& v, ISerializeModifier* modifier) { - v = stream.GetInt(); + v = DeserializeInt(stream); } inline bool ShouldSerialize(const int64& v, const void* otherObj) @@ -204,7 +216,7 @@ namespace Serialization template inline typename TEnableIf::Value>::Type Deserialize(ISerializable::DeserializeStream& stream, T& v, ISerializeModifier* modifier) { - v = (T)stream.GetInt(); + v = (T)(stream.IsInt() ? stream.GetInt() : 0); } // Common types @@ -577,21 +589,34 @@ namespace Serialization template inline void Deserialize(ISerializable::DeserializeStream& stream, Dictionary& v, ISerializeModifier* modifier) { - if (!stream.IsArray()) - return; - const auto& streamArray = stream.GetArray(); v.Clear(); - v.EnsureCapacity(streamArray.Size() * 3); - for (rapidjson::SizeType i = 0; i < streamArray.Size(); i++) + if (stream.IsArray()) { - auto& streamItem = streamArray[i]; - const auto mKey = SERIALIZE_FIND_MEMBER(streamItem, "Key"); - const auto mValue = SERIALIZE_FIND_MEMBER(streamItem, "Value"); - if (mKey != streamItem.MemberEnd() && mValue != streamItem.MemberEnd()) + const auto& streamArray = stream.GetArray(); + const int32 size = streamArray.Size(); + v.EnsureCapacity(size * 3); + for (int32 i = 0; i < size; i++) + { + auto& streamItem = streamArray[i]; + const auto mKey = SERIALIZE_FIND_MEMBER(streamItem, "Key"); + const auto mValue = SERIALIZE_FIND_MEMBER(streamItem, "Value"); + if (mKey != streamItem.MemberEnd() && mValue != streamItem.MemberEnd()) + { + KeyType key; + Deserialize(mKey->value, key, modifier); + Deserialize(mValue->value, v[key], modifier); + } + } + } + else if (stream.IsObject()) + { + const int32 size = stream.MemberCount(); + v.EnsureCapacity(size * 3); + for (auto i = stream.MemberBegin(); i != stream.MemberEnd(); ++i) { KeyType key; - Deserialize(mKey->value, key, modifier); - Deserialize(mValue->value, v[key], modifier); + Deserialize(i->name, key, modifier); + Deserialize(i->value, v[key], modifier); } } } From 1fedad5dfe7c77e9201006e542e9685779d36acb Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 17 Jun 2021 14:17:36 +0200 Subject: [PATCH 185/198] Add missing changes --- Source/Engine/Level/Scene/Lightmap.cpp | 1 + Source/Engine/UI/UIControl.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Level/Scene/Lightmap.cpp b/Source/Engine/Level/Scene/Lightmap.cpp index 88fa88b5a..2559005a9 100644 --- a/Source/Engine/Level/Scene/Lightmap.cpp +++ b/Source/Engine/Level/Scene/Lightmap.cpp @@ -2,6 +2,7 @@ #include "Lightmap.h" #include "Scene.h" +#include "Engine/Core/Log.h" #include "Engine/Platform/FileSystem.h" #include "Engine/Content/Content.h" #include "Engine/Level/Level.h" diff --git a/Source/Engine/UI/UIControl.cpp b/Source/Engine/UI/UIControl.cpp index b9fef02f8..6afdd77b3 100644 --- a/Source/Engine/UI/UIControl.cpp +++ b/Source/Engine/UI/UIControl.cpp @@ -118,7 +118,7 @@ void UIControl::Deserialize(DeserializeStream& stream, ISerializeModifier* modif const auto controlMember = stream.FindMember("Control"); if (controlMember != stream.MemberEnd()) { - const StringAnsiView controlType(controlMember->value.GetString(), controlMember->value.GetStringLength()); + const StringAnsiView controlType(controlMember->value.GetStringAnsiView()); const auto type = Scripting::FindClass(controlType); if (type != nullptr) { From 64501a86459c957a4c8574225a363bb3e693514d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 17 Jun 2021 14:22:08 +0200 Subject: [PATCH 186/198] Fix enum deserialization from string --- Source/Engine/Serialization/Serialization.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Engine/Serialization/Serialization.h b/Source/Engine/Serialization/Serialization.h index 41517da58..9fc49d8b7 100644 --- a/Source/Engine/Serialization/Serialization.h +++ b/Source/Engine/Serialization/Serialization.h @@ -216,7 +216,7 @@ namespace Serialization template inline typename TEnableIf::Value>::Type Deserialize(ISerializable::DeserializeStream& stream, T& v, ISerializeModifier* modifier) { - v = (T)(stream.IsInt() ? stream.GetInt() : 0); + v = (T)DeserializeInt(stream); } // Common types From 4744fa05ef716e7157848494df4fa3d2ff6ea68c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 17 Jun 2021 15:33:34 +0200 Subject: [PATCH 187/198] Refactor Streaming with new settings and textures streaming configuration --- .../Content/Create/SettingsCreateEntry.cs | 6 + .../Content/Import/TextureImportEntry.cs | 11 +- Source/Editor/Content/PreviewsCache.cpp | 17 +- .../Editor/Managed/ManagedEditor.Internal.cpp | 3 + .../Editor/Modules/ContentDatabaseModule.cs | 1 + .../SceneAnimations/SceneAnimation.cpp | 1 + Source/Engine/Content/Assets/MaterialBase.cpp | 1 + .../Content/Assets/MaterialInstance.cpp | 1 + .../Content/Upgraders/TextureAssetUpgrader.h | 5 - .../Engine/ContentImporters/ImportTexture.cpp | 6 +- Source/Engine/Core/Config/GameSettings.cpp | 5 + Source/Engine/Core/Config/GameSettings.cs | 10 + Source/Engine/Core/Config/GameSettings.h | 1 + Source/Engine/Core/Types/StringView.h | 10 +- .../Graphics/Textures/StreamingTexture.cpp | 37 +++ .../Graphics/Textures/StreamingTexture.h | 7 +- .../Engine/Graphics/Textures/TextureBase.cpp | 64 +++-- Source/Engine/Graphics/Textures/TextureBase.h | 45 +--- Source/Engine/Graphics/Textures/Types.h | 55 ++++- Source/Engine/Physics/CollisionData.cpp | 1 + Source/Engine/Streaming/Config.h | 16 -- .../Handlers/AudioStreamingHandler.cpp | 67 ----- .../Handlers/AudioStreamingHandler.h | 20 -- .../Handlers/ModelsStreamingHandler.cpp | 50 ---- .../Handlers/ModelsStreamingHandler.h | 19 -- .../SkinnedModelsStreamingHandler.cpp | 50 ---- .../Handlers/SkinnedModelsStreamingHandler.h | 19 -- .../Handlers/TexturesStreamingHandler.cpp | 71 ------ .../Handlers/TexturesStreamingHandler.h | 19 -- Source/Engine/Streaming/IStreamingHandler.h | 16 +- .../Engine/Streaming/StreamableResource.cpp | 8 +- Source/Engine/Streaming/StreamableResource.h | 11 +- .../{StreamingManager.cpp => Streaming.cpp} | 28 ++- Source/Engine/Streaming/Streaming.h | 25 ++ Source/Engine/Streaming/StreamingGroup.cpp | 5 +- Source/Engine/Streaming/StreamingHandlers.cpp | 230 ++++++++++++++++++ Source/Engine/Streaming/StreamingHandlers.h | 54 ++++ Source/Engine/Streaming/StreamingManager.h | 18 -- Source/Engine/Streaming/StreamingSettings.h | 32 +++ Source/Engine/Streaming/TextureGroup.h | 56 +++++ .../Engine/Tools/TextureTool/TextureTool.cpp | 5 + Source/Engine/Tools/TextureTool/TextureTool.h | 5 + 42 files changed, 631 insertions(+), 480 deletions(-) delete mode 100644 Source/Engine/Streaming/Config.h delete mode 100644 Source/Engine/Streaming/Handlers/AudioStreamingHandler.cpp delete mode 100644 Source/Engine/Streaming/Handlers/AudioStreamingHandler.h delete mode 100644 Source/Engine/Streaming/Handlers/ModelsStreamingHandler.cpp delete mode 100644 Source/Engine/Streaming/Handlers/ModelsStreamingHandler.h delete mode 100644 Source/Engine/Streaming/Handlers/SkinnedModelsStreamingHandler.cpp delete mode 100644 Source/Engine/Streaming/Handlers/SkinnedModelsStreamingHandler.h delete mode 100644 Source/Engine/Streaming/Handlers/TexturesStreamingHandler.cpp delete mode 100644 Source/Engine/Streaming/Handlers/TexturesStreamingHandler.h rename Source/Engine/Streaming/{StreamingManager.cpp => Streaming.cpp} (88%) create mode 100644 Source/Engine/Streaming/Streaming.h create mode 100644 Source/Engine/Streaming/StreamingHandlers.cpp create mode 100644 Source/Engine/Streaming/StreamingHandlers.h delete mode 100644 Source/Engine/Streaming/StreamingManager.h create mode 100644 Source/Engine/Streaming/StreamingSettings.h create mode 100644 Source/Engine/Streaming/TextureGroup.h diff --git a/Source/Editor/Content/Create/SettingsCreateEntry.cs b/Source/Editor/Content/Create/SettingsCreateEntry.cs index 974de76cc..8445425b0 100644 --- a/Source/Editor/Content/Create/SettingsCreateEntry.cs +++ b/Source/Editor/Content/Create/SettingsCreateEntry.cs @@ -68,6 +68,11 @@ namespace FlaxEditor.Content.Create /// InputSettings, + /// + /// The streaming settings. + /// + StreamingSettings, + /// /// The Windows settings. /// @@ -116,6 +121,7 @@ namespace FlaxEditor.Content.Create typeof(LocalizationSettings), typeof(BuildSettings), typeof(InputSettings), + typeof(StreamingSettings), typeof(WindowsPlatformSettings), typeof(UWPPlatformSettings), typeof(LinuxPlatformSettings), diff --git a/Source/Editor/Content/Import/TextureImportEntry.cs b/Source/Editor/Content/Import/TextureImportEntry.cs index db1e3ae8c..9c435c120 100644 --- a/Source/Editor/Content/Import/TextureImportEntry.cs +++ b/Source/Editor/Content/Import/TextureImportEntry.cs @@ -283,6 +283,12 @@ namespace FlaxEditor.Content.Import [EditorOrder(250), VisibleIf("PreserveAlphaCoverage"), DefaultValue(0.5f), Tooltip("The reference value for the alpha coverage preserving.")] public float PreserveAlphaCoverageReference { get; set; } = 0.5f; + /// + /// Texture group for streaming (negative if unused). See Streaming Settings. + /// + [EditorOrder(300), Tooltip("Texture group for streaming (negative if unused). See Streaming Settings.")] + public int TextureGroup = -1; + /// /// The sprites. Used to keep created sprites on sprite atlas reimport. /// @@ -305,6 +311,7 @@ namespace FlaxEditor.Content.Import public float PreserveAlphaCoverageReference; public float Scale; public int MaxSize; + public int TextureGroup; public Int2 Size; public Rectangle[] SpriteAreas; public string[] SpriteNames; @@ -327,7 +334,8 @@ namespace FlaxEditor.Content.Import PreserveAlphaCoverageReference = PreserveAlphaCoverageReference, Scale = Scale, Size = Size, - MaxSize = (int)MaxSize + MaxSize = (int)MaxSize, + TextureGroup = TextureGroup, }; if (Sprites != null && Sprites.Count > 0) { @@ -362,6 +370,7 @@ namespace FlaxEditor.Content.Import PreserveAlphaCoverageReference = options.PreserveAlphaCoverageReference; Scale = options.Scale; MaxSize = ConvertMaxSize(options.MaxSize); + TextureGroup = options.TextureGroup; Size = options.Size; if (options.SpriteAreas != null) { diff --git a/Source/Editor/Content/PreviewsCache.cpp b/Source/Editor/Content/PreviewsCache.cpp index c89b00004..e05f16600 100644 --- a/Source/Editor/Content/PreviewsCache.cpp +++ b/Source/Editor/Content/PreviewsCache.cpp @@ -241,16 +241,13 @@ CreateAssetResult PreviewsCache::create(CreateAssetContext& context) IMPORT_SETUP(PreviewsCache, 4); // Create texture header (custom data) - TextureHeader header; - header.Width = ASSETS_ICONS_ATLAS_SIZE; - header.Height = ASSETS_ICONS_ATLAS_SIZE; - header.Format = ASSETS_ICONS_ATLAS_FORMAT; - header.IsSRGB = false; - header.IsCubeMap = false; - header.MipLevels = 1; - header.NeverStream = true; - header.Type = TextureFormatType::Unknown; - context.Data.CustomData.Copy(&header); + TextureHeader textureHeader; + textureHeader.Width = ASSETS_ICONS_ATLAS_SIZE; + textureHeader.Height = ASSETS_ICONS_ATLAS_SIZE; + textureHeader.Format = ASSETS_ICONS_ATLAS_FORMAT; + textureHeader.MipLevels = 1; + textureHeader.NeverStream = true; + context.Data.CustomData.Copy(&textureHeader); // Create blank image (chunk 0) uint64 imageSize = CalculateTextureMemoryUsage(ASSETS_ICONS_ATLAS_FORMAT, ASSETS_ICONS_ATLAS_SIZE, ASSETS_ICONS_ATLAS_SIZE, 1); diff --git a/Source/Editor/Managed/ManagedEditor.Internal.cpp b/Source/Editor/Managed/ManagedEditor.Internal.cpp index a312b38c4..2ef78052e 100644 --- a/Source/Editor/Managed/ManagedEditor.Internal.cpp +++ b/Source/Editor/Managed/ManagedEditor.Internal.cpp @@ -66,6 +66,7 @@ struct InternalTextureOptions float PreserveAlphaCoverageReference; float Scale; int32 MaxSize; + int32 TextureGroup; int32 SizeX; int32 SizeY; MonoArray* SpriteAreas; @@ -86,6 +87,7 @@ struct InternalTextureOptions to->PreserveAlphaCoverage = from->PreserveAlphaCoverage; to->PreserveAlphaCoverageReference = from->PreserveAlphaCoverageReference; to->MaxSize = from->MaxSize; + to->TextureGroup = from->TextureGroup; to->SizeX = from->SizeX; to->SizeY = from->SizeY; to->Sprites.Clear(); @@ -118,6 +120,7 @@ struct InternalTextureOptions to->PreserveAlphaCoverageReference = from->PreserveAlphaCoverageReference; to->Scale = from->Scale; to->MaxSize = from->MaxSize; + to->TextureGroup = from->TextureGroup; to->SizeX = from->SizeX; to->SizeY = from->SizeY; if (from->Sprites.HasItems()) diff --git a/Source/Editor/Modules/ContentDatabaseModule.cs b/Source/Editor/Modules/ContentDatabaseModule.cs index ca4577d55..12e7abb96 100644 --- a/Source/Editor/Modules/ContentDatabaseModule.cs +++ b/Source/Editor/Modules/ContentDatabaseModule.cs @@ -940,6 +940,7 @@ namespace FlaxEditor.Modules Proxy.Add(new SettingsProxy(typeof(AudioSettings), Editor.Instance.Icons.AudioSettings128)); Proxy.Add(new SettingsProxy(typeof(BuildSettings), Editor.Instance.Icons.BuildSettings128)); Proxy.Add(new SettingsProxy(typeof(InputSettings), Editor.Instance.Icons.InputSettings128)); + Proxy.Add(new SettingsProxy(typeof(StreamingSettings), Editor.Instance.Icons.BuildSettings128)); Proxy.Add(new SettingsProxy(typeof(WindowsPlatformSettings), Editor.Instance.Icons.WindowsSettings128)); Proxy.Add(new SettingsProxy(typeof(UWPPlatformSettings), Editor.Instance.Icons.UWPSettings128)); Proxy.Add(new SettingsProxy(typeof(LinuxPlatformSettings), Editor.Instance.Icons.LinuxSettings128)); diff --git a/Source/Engine/Animations/SceneAnimations/SceneAnimation.cpp b/Source/Engine/Animations/SceneAnimations/SceneAnimation.cpp index f669c72c2..e3246705a 100644 --- a/Source/Engine/Animations/SceneAnimations/SceneAnimation.cpp +++ b/Source/Engine/Animations/SceneAnimations/SceneAnimation.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "SceneAnimation.h" +#include "Engine/Core/Log.h" #include "Engine/Content/Factories/BinaryAssetFactory.h" #include "Engine/Content/Assets/MaterialBase.h" #include "Engine/Content/Content.h" diff --git a/Source/Engine/Content/Assets/MaterialBase.cpp b/Source/Engine/Content/Assets/MaterialBase.cpp index 3b2a039db..f879f0512 100644 --- a/Source/Engine/Content/Assets/MaterialBase.cpp +++ b/Source/Engine/Content/Assets/MaterialBase.cpp @@ -2,6 +2,7 @@ #include "MaterialBase.h" #include "MaterialInstance.h" +#include "Engine/Core/Log.h" #include "Engine/Core/Types/Variant.h" #include "Engine/Content/Content.h" #include "Engine/Content/Factories/BinaryAssetFactory.h" diff --git a/Source/Engine/Content/Assets/MaterialInstance.cpp b/Source/Engine/Content/Assets/MaterialInstance.cpp index 5ea8c9351..d432a5804 100644 --- a/Source/Engine/Content/Assets/MaterialInstance.cpp +++ b/Source/Engine/Content/Assets/MaterialInstance.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "MaterialInstance.h" +#include "Engine/Core/Log.h" #include "Engine/Core/Types/Variant.h" #include "Engine/Content/Content.h" #include "Engine/Content/Upgraders/MaterialInstanceUpgrader.h" diff --git a/Source/Engine/Content/Upgraders/TextureAssetUpgrader.h b/Source/Engine/Content/Upgraders/TextureAssetUpgrader.h index a598538f9..ef25ad07c 100644 --- a/Source/Engine/Content/Upgraders/TextureAssetUpgrader.h +++ b/Source/Engine/Content/Upgraders/TextureAssetUpgrader.h @@ -480,7 +480,6 @@ private: newHeader.IsSRGB = textureHeader.IsSRGB; newHeader.NeverStream = textureHeader.NeverStream; newHeader.Type = textureHeader.Type; - newHeader.IsCubeMap = false; context.Output.CustomData.Copy(&newHeader); // Convert import options @@ -535,7 +534,6 @@ private: newHeader.MipLevels = textureHeader.MipLevels; newHeader.Format = PixelFormatOldToNew(textureHeader.Format); newHeader.IsSRGB = textureHeader.IsSRGB; - newHeader.NeverStream = false; newHeader.Type = TextureFormatType::ColorRGB; newHeader.IsCubeMap = true; context.Output.CustomData.Copy(&newHeader); @@ -617,7 +615,6 @@ private: newHeader.IsSRGB = textureHeader.IsSRGB; newHeader.NeverStream = textureHeader.NeverStream; newHeader.Type = textureHeader.Type; - newHeader.IsCubeMap = false; context.Output.CustomData.Copy(&newHeader); // Copy sprite atlas data from old header stream to chunk (chunk 15th) @@ -696,7 +693,6 @@ private: newHeader.IsSRGB = textureHeader.IsSRGB; newHeader.NeverStream = textureHeader.NeverStream; newHeader.Type = textureHeader.Type; - newHeader.IsCubeMap = false; auto data = (IESProfile::CustomDataLayout*)newHeader.CustomData; data->Brightness = brightness; data->TextureMultiplier = multiplier; @@ -765,7 +761,6 @@ private: newHeader.IsSRGB = textureHeader.IsSRGB; newHeader.NeverStream = textureHeader.NeverStream; newHeader.Type = textureHeader.Type; - newHeader.IsCubeMap = false; context.Output.CustomData.Copy(&newHeader); // Copy asset previews IDs mapping data from old chunk to new one diff --git a/Source/Engine/ContentImporters/ImportTexture.cpp b/Source/Engine/ContentImporters/ImportTexture.cpp index 7daa62d9d..99c752a31 100644 --- a/Source/Engine/ContentImporters/ImportTexture.cpp +++ b/Source/Engine/ContentImporters/ImportTexture.cpp @@ -168,6 +168,7 @@ CreateAssetResult ImportTexture::Create(CreateAssetContext& context, const Textu textureHeader.MipLevels = textureData.GetMipLevels(); textureHeader.IsSRGB = PixelFormatExtensions::IsSRGB(textureHeader.Format); textureHeader.IsCubeMap = isCubeMap; + textureHeader.TextureGroup = options.TextureGroup; ASSERT(textureHeader.MipLevels <= GPU_MAX_TEXTURE_MIP_LEVELS); // Save header @@ -306,6 +307,7 @@ CreateAssetResult ImportTexture::Create(CreateAssetContext& context, const Textu textureHeader.MipLevels = textureData.Mips.Count(); textureHeader.IsSRGB = PixelFormatExtensions::IsSRGB(textureHeader.Format); textureHeader.IsCubeMap = isCubeMap; + textureHeader.TextureGroup = options.TextureGroup; ASSERT(textureHeader.MipLevels <= GPU_MAX_TEXTURE_MIP_LEVELS); // Save header @@ -542,12 +544,10 @@ CreateAssetResult ImportTexture::ImportIES(class CreateAssetContext& context) textureHeader.Width = loader.GetWidth(); textureHeader.Height = loader.GetHeight(); textureHeader.MipLevels = 1; - textureHeader.NeverStream = false; textureHeader.Type = TextureFormatType::Unknown; textureHeader.Format = PixelFormat::R16_Float; - textureHeader.IsSRGB = false; - textureHeader.IsCubeMap = false; auto data = (IESProfile::CustomDataLayout*)textureHeader.CustomData; + static_assert(sizeof(IESProfile::CustomDataLayout) <= sizeof(textureHeader.CustomData), "Invalid Custom Data size in Texture Header."); data->Brightness = loader.GetBrightness(); data->TextureMultiplier = multiplier; ASSERT(textureHeader.MipLevels <= GPU_MAX_TEXTURE_MIP_LEVELS); diff --git a/Source/Engine/Core/Config/GameSettings.cpp b/Source/Engine/Core/Config/GameSettings.cpp index 5b333b227..26d9175b0 100644 --- a/Source/Engine/Core/Config/GameSettings.cpp +++ b/Source/Engine/Core/Config/GameSettings.cpp @@ -19,6 +19,7 @@ #include "Engine/Content/AssetReference.h" #include "Engine/Engine/EngineService.h" #include "Engine/Engine/Globals.h" +#include "Engine/Streaming/StreamingSettings.h" class GameSettingsService : public EngineService { @@ -42,6 +43,7 @@ IMPLEMENT_SETTINGS_GETTER(TimeSettings, Time); IMPLEMENT_SETTINGS_GETTER(AudioSettings, Audio); IMPLEMENT_SETTINGS_GETTER(PhysicsSettings, Physics); IMPLEMENT_SETTINGS_GETTER(InputSettings, Input); +IMPLEMENT_SETTINGS_GETTER(StreamingSettings, Streaming); #if !USE_EDITOR #if PLATFORM_WINDOWS @@ -130,6 +132,7 @@ bool GameSettings::Load() PRELOAD_SETTINGS(Navigation); PRELOAD_SETTINGS(Localization); PRELOAD_SETTINGS(GameCooking); + PRELOAD_SETTINGS(Streaming); #undef PRELOAD_SETTINGS // Apply the game settings to the engine @@ -157,6 +160,7 @@ void GameSettings::Apply() APPLY_SETTINGS(AudioSettings); APPLY_SETTINGS(LayersAndTagsSettings); APPLY_SETTINGS(PhysicsSettings); + APPLY_SETTINGS(StreamingSettings); APPLY_SETTINGS(InputSettings); APPLY_SETTINGS(GraphicsSettings); APPLY_SETTINGS(NavigationSettings); @@ -202,6 +206,7 @@ void GameSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* mo DESERIALIZE(Navigation); DESERIALIZE(Localization); DESERIALIZE(GameCooking); + DESERIALIZE(Streaming); // Per-platform settings containers DESERIALIZE(WindowsPlatform); diff --git a/Source/Engine/Core/Config/GameSettings.cs b/Source/Engine/Core/Config/GameSettings.cs index ed96ef4e6..b9ba4e110 100644 --- a/Source/Engine/Core/Config/GameSettings.cs +++ b/Source/Engine/Core/Config/GameSettings.cs @@ -91,6 +91,12 @@ namespace FlaxEditor.Content.Settings [EditorOrder(1050), EditorDisplay("Other Settings"), AssetReference(typeof(BuildSettings), true), Tooltip("Reference to Build Settings asset")] public JsonAsset GameCooking; + /// + /// Reference to asset. + /// + [EditorOrder(1060), EditorDisplay("Other Settings"), AssetReference(typeof(StreamingSettings), true), Tooltip("Reference to Streaming Settings asset")] + public JsonAsset Streaming; + /// /// The custom settings to use with a game. Can be specified by the user to define game-specific options and be used by the external plugins (used as key-value pair). /// @@ -229,6 +235,8 @@ namespace FlaxEditor.Content.Settings return LoadAsset(gameSettings.Localization) as T; if (type == typeof(BuildSettings)) return LoadAsset(gameSettings.GameCooking) as T; + if (type == typeof(StreamingSettings)) + return LoadAsset(gameSettings.Streaming) as T; if (type == typeof(InputSettings)) return LoadAsset(gameSettings.Input) as T; if (type == typeof(AudioSettings)) @@ -333,6 +341,8 @@ namespace FlaxEditor.Content.Settings return SaveAsset(gameSettings, ref gameSettings.Localization, obj); if (type == typeof(BuildSettings)) return SaveAsset(gameSettings, ref gameSettings.GameCooking, obj); + if (type == typeof(StreamingSettings)) + return SaveAsset(gameSettings, ref gameSettings.Streaming, obj); if (type == typeof(InputSettings)) return SaveAsset(gameSettings, ref gameSettings.Input, obj); if (type == typeof(WindowsPlatformSettings)) diff --git a/Source/Engine/Core/Config/GameSettings.h b/Source/Engine/Core/Config/GameSettings.h index 8016bdbe4..0737272a3 100644 --- a/Source/Engine/Core/Config/GameSettings.h +++ b/Source/Engine/Core/Config/GameSettings.h @@ -70,6 +70,7 @@ public: Guid Navigation; Guid Localization; Guid GameCooking; + Guid Streaming; // Per-platform settings containers Guid WindowsPlatform; diff --git a/Source/Engine/Core/Types/StringView.h b/Source/Engine/Core/Types/StringView.h index bb987b3b4..b630c429c 100644 --- a/Source/Engine/Core/Types/StringView.h +++ b/Source/Engine/Core/Types/StringView.h @@ -17,6 +17,12 @@ protected: const T* _data; int32 _length; + constexpr StringViewBase() + { + _data = nullptr; + _length = 0; + } + public: /// @@ -189,8 +195,6 @@ public: /// constexpr StringView() { - _data = nullptr; - _length = 0; } /// @@ -396,8 +400,6 @@ public: /// constexpr StringAnsiView() { - _data = nullptr; - _length = 0; } /// diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp index d5a1e9339..f4bd6c082 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp +++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp @@ -9,6 +9,38 @@ #include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/Async/Tasks/GPUUploadTextureMipTask.h" +TextureHeader_Deprecated::TextureHeader_Deprecated() +{ + Platform::MemoryClear(this, sizeof(*this)); +} + +TextureHeader::TextureHeader() +{ + Platform::MemoryClear(this, sizeof(*this)); + TextureGroup = -1; +} + +TextureHeader::TextureHeader(TextureHeader_Deprecated& old) +{ + Platform::MemoryClear(this, sizeof(*this)); + Width = old.Width;; + Height = old.Height; + MipLevels = old.MipLevels; + Format = old.Format; + Type = old.Type; + if (old.IsCubeMap) + IsCubeMap = 1; + if (old.IsSRGB) + IsSRGB = 1; + if (old.NeverStream) + NeverStream = 1; + TextureGroup = -1; + Platform::MemoryCopy(CustomData, old.CustomData, sizeof(CustomData)); +} + +static_assert(sizeof(TextureHeader_Deprecated) == 10 * sizeof(int32), "Invalid TextureHeader size."); +static_assert(sizeof(TextureHeader) == 36, "Invalid TextureHeader size."); + StreamingTexture::StreamingTexture(ITextureOwner* parent, const String& name) : StreamableResource(StreamingGroups::Instance()->Textures()) , _owner(parent) @@ -112,6 +144,11 @@ String StreamingTexture::ToString() const return _texture->ToString(); } +int32 StreamingTexture::GetMaxResidency() const +{ + return _header.MipLevels; +} + int32 StreamingTexture::GetCurrentResidency() const { return _texture->ResidentMipLevels(); diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.h b/Source/Engine/Graphics/Textures/StreamingTexture.h index 1dda71ba4..3aa8fee28 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.h +++ b/Source/Engine/Graphics/Textures/StreamingTexture.h @@ -7,7 +7,7 @@ #include "Types.h" /// -/// GPU texture object which can change it's resolution (quality) at runtime +/// GPU texture object which can change it's resolution (quality) at runtime. /// class FLAXENGINE_API StreamingTexture : public Object, public StreamableResource { @@ -180,10 +180,7 @@ public: String ToString() const override; // [StreamableResource] - int32 GetMaxResidency() const override - { - return _header.MipLevels; - } + int32 GetMaxResidency() const override; int32 GetCurrentResidency() const override; int32 GetAllocatedResidency() const override; bool CanBeUpdated() const override; diff --git a/Source/Engine/Graphics/Textures/TextureBase.cpp b/Source/Engine/Graphics/Textures/TextureBase.cpp index af5d96394..a4aeccbcb 100644 --- a/Source/Engine/Graphics/Textures/TextureBase.cpp +++ b/Source/Engine/Graphics/Textures/TextureBase.cpp @@ -29,27 +29,27 @@ Vector2 TextureBase::Size() const int32 TextureBase::GetArraySize() const { - return StreamingTexture()->TotalArraySize(); + return _texture.TotalArraySize(); } int32 TextureBase::GetMipLevels() const { - return StreamingTexture()->TotalMipLevels(); + return _texture.TotalMipLevels(); } int32 TextureBase::GetResidentMipLevels() const { - return GetTexture()->ResidentMipLevels(); + return _texture.GetTexture()->ResidentMipLevels(); } uint64 TextureBase::GetCurrentMemoryUsage() const { - return GetTexture()->GetMemoryUsage(); + return _texture.GetTexture()->GetMemoryUsage(); } uint64 TextureBase::GetTotalMemoryUsage() const { - return StreamingTexture()->GetTotalMemoryUsage(); + return _texture.GetTotalMemoryUsage(); } BytesContainer TextureBase::GetMipData(int32 mipIndex, int32& rowPitch, int32& slicePitch) @@ -166,16 +166,15 @@ bool TextureBase::Init(InitData* initData) _customData = initData; // Create texture - TextureHeader header; - header.Format = initData->Format; - header.Width = initData->Width; - header.Height = initData->Height; - header.IsCubeMap = initData->ArraySize == 6; - header.MipLevels = initData->Mips.Count(); - header.IsSRGB = false; - header.Type = TextureFormatType::ColorRGBA; - header.NeverStream = true; - if (_texture.Create(header)) + TextureHeader textureHeader; + textureHeader.Format = initData->Format; + textureHeader.Width = initData->Width; + textureHeader.Height = initData->Height; + textureHeader.IsCubeMap = initData->ArraySize == 6; + textureHeader.MipLevels = initData->Mips.Count(); + textureHeader.Type = TextureFormatType::ColorRGBA; + textureHeader.NeverStream = true; + if (_texture.Create(textureHeader)) { LOG(Warning, "Cannot initialize texture."); return true; @@ -289,6 +288,41 @@ bool TextureBase::GetMipDataCustomPitch(int32 mipIndex, uint32& rowPitch, uint32 return result; } +bool TextureBase::init(AssetInitData& initData) +{ + if (IsVirtual()) + return false; + if (initData.SerializedVersion != TexturesSerializedVersion) + { + LOG(Error, "Invalid serialized texture version."); + return true; + } + + // Get texture header for asset custom data (fast access) + TextureHeader textureHeader; + if (initData.CustomData.Length() == sizeof(TextureHeader)) + { + Platform::MemoryCopy(&textureHeader, initData.CustomData.Get(), sizeof(textureHeader)); + } + else if (initData.CustomData.Length() == sizeof(TextureHeader_Deprecated)) + { + textureHeader = TextureHeader(*(TextureHeader_Deprecated*)initData.CustomData.Get()); + } + else + { + LOG(Error, "Missing texture header."); + return true; + } + + return _texture.Create(textureHeader); +} + +Asset::LoadResult TextureBase::load() +{ + // Loading textures is very fast xD + return LoadResult::Ok; +} + bool TextureBase::InitData::GenerateMip(int32 mipIndex, bool linear) { // Validate input diff --git a/Source/Engine/Graphics/Textures/TextureBase.h b/Source/Engine/Graphics/Textures/TextureBase.h index 7839f940a..bdc551397 100644 --- a/Source/Engine/Graphics/Textures/TextureBase.h +++ b/Source/Engine/Graphics/Textures/TextureBase.h @@ -4,7 +4,6 @@ #include "Engine/Content/BinaryAsset.h" #include "StreamingTexture.h" -#include "Engine/Core/Log.h" class TextureData; @@ -15,12 +14,8 @@ class TextureData; API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API TextureBase : public BinaryAsset, public ITextureOwner { DECLARE_ASSET_HEADER(TextureBase); -public: - static const uint32 TexturesSerializedVersion = 4; -public: - /// /// The texture init data (external source). /// @@ -180,43 +175,7 @@ public: protected: // [BinaryAsset] - bool init(AssetInitData& initData) override - { - // Skip for virtual assets - if (IsVirtual()) - return false; - - // Validate - if (initData.SerializedVersion != 4) - { - LOG(Error, "Invalid serialized texture version."); - return true; - } - if (initData.CustomData.Length() != sizeof(TextureHeader)) - { - LOG(Error, "Missing texture header."); - return true; - } - - // Load header - TextureHeader header; - Platform::MemoryCopy(&header, initData.CustomData.Get(), sizeof(TextureHeader)); - - // Create texture - if (_texture.Create(header)) - { - LOG(Error, "Cannot initialize texture."); - return true; - } - - return false; - } - - LoadResult load() override - { - // Loading textures is very fast xD - return LoadResult::Ok; - } - + bool init(AssetInitData& initData) override; + LoadResult load() override; void unload(bool isReloading) override; }; diff --git a/Source/Engine/Graphics/Textures/Types.h b/Source/Engine/Graphics/Textures/Types.h index e20af2b6a..5f2a60203 100644 --- a/Source/Engine/Graphics/Textures/Types.h +++ b/Source/Engine/Graphics/Textures/Types.h @@ -12,25 +12,53 @@ DECLARE_ENUM_EX_7(TextureFormatType, byte, 0, Unknown, ColorRGB, ColorRGBA, NormalMap, GrayScale, HdrRGBA, HdrRGB); /// -/// Texture header structure +/// Old texture header structure (was not fully initialized to zero). +/// +struct TextureHeader_Deprecated +{ + int32 Width; + int32 Height; + int32 MipLevels; + PixelFormat Format; + TextureFormatType Type; + bool IsCubeMap; + bool NeverStream; + bool IsSRGB; + byte CustomData[17]; + + TextureHeader_Deprecated(); +}; + +/// +/// Texture header structure. /// struct FLAXENGINE_API TextureHeader { /// - /// Top mip width in pixels + /// Width in pixels /// int32 Width; /// - /// Top mip height in pixels + /// Height in pixels /// int32 Height; + /// + /// Depth in pixels + /// + int32 Depth; + /// /// Amount of mip levels /// int32 MipLevels; + /// + /// Texture group for streaming (negative if unused). + /// + int32 TextureGroup; + /// /// Texture pixels format /// @@ -44,22 +72,23 @@ struct FLAXENGINE_API TextureHeader /// /// True if texture is a cubemap (has 6 array slices per mip). /// - bool IsCubeMap; - - /// - /// True if disable dynamic texture streaming - /// - bool NeverStream; + byte IsCubeMap : 1; /// /// True if texture contains sRGB colors data /// - bool IsSRGB; + byte IsSRGB : 1; + + /// + /// True if disable dynamic texture streaming + /// + byte NeverStream : 1; /// /// The custom data to be used per texture storage layer (faster access). /// - byte CustomData[17]; -}; + byte CustomData[10]; -static_assert(sizeof(TextureHeader) == 10 * sizeof(int32), "Invalid TextureHeader size."); + TextureHeader(); + TextureHeader(TextureHeader_Deprecated& old); +}; diff --git a/Source/Engine/Physics/CollisionData.cpp b/Source/Engine/Physics/CollisionData.cpp index a8621da09..eeb0a43c1 100644 --- a/Source/Engine/Physics/CollisionData.cpp +++ b/Source/Engine/Physics/CollisionData.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "CollisionData.h" +#include "Engine/Core/Log.h" #include "Engine/Content/Content.h" #include "Engine/Content/Assets/Model.h" #include "Engine/Content/Factories/BinaryAssetFactory.h" diff --git a/Source/Engine/Streaming/Config.h b/Source/Engine/Streaming/Config.h deleted file mode 100644 index eba4e78a9..000000000 --- a/Source/Engine/Streaming/Config.h +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#pragma once - -// Resources streaming types -typedef float StreamingQuality; - -#define MIN_STREAMING_QUALITY 0.0f -#define MAX_STREAMING_QUALITY 1.0f - -/// -/// Enables/disables dynamic resources streaming feature -/// -#define ENABLE_RESOURCES_DYNAMIC_STREAMING 1 - -class StreamableResource; diff --git a/Source/Engine/Streaming/Handlers/AudioStreamingHandler.cpp b/Source/Engine/Streaming/Handlers/AudioStreamingHandler.cpp deleted file mode 100644 index ad5797f92..000000000 --- a/Source/Engine/Streaming/Handlers/AudioStreamingHandler.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#include "AudioStreamingHandler.h" -#include "Engine/Audio/AudioClip.h" -#include "Engine/Audio/Audio.h" -#include "Engine/Audio/AudioSource.h" - -StreamingQuality AudioStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now) -{ - // Audio clips don't use quality but only residency - return MAX_STREAMING_QUALITY; -} - -int32 AudioStreamingHandler::CalculateResidency(StreamableResource* resource, StreamingQuality quality) -{ - ASSERT(resource); - auto clip = static_cast(resource); - const int32 chunksCount = clip->Buffers.Count(); - bool chunksMask[ASSET_FILE_DATA_CHUNKS]; - Platform::MemoryClear(chunksMask, sizeof(chunksMask)); - - // Find audio chunks required for streaming - clip->StreamingQueue.Clear(); - for (int32 sourceIndex = 0; sourceIndex < Audio::Sources.Count(); sourceIndex++) - { - // TODO: collect refs to audio clip from sources and use faster iteration (but do it thread-safe) - - const auto src = Audio::Sources[sourceIndex]; - if (src->Clip == clip && src->GetState() == AudioSource::States::Playing) - { - // Stream the current and the next chunk if could be used in a while - const int32 chunk = src->_streamingFirstChunk; - ASSERT(Math::IsInRange(chunk, 0, chunksCount)); - chunksMask[chunk] = true; - - const float StreamingDstSec = 2.0f; - if (chunk + 1 < chunksCount && src->GetTime() + StreamingDstSec >= clip->GetBufferStartTime(src->_streamingFirstChunk)) - { - chunksMask[chunk + 1] = true; - } - } - } - - // Try to enqueue chunks to modify (load or unload) - for (int32 i = 0; i < chunksCount; i++) - { - if (chunksMask[i] != (clip->Buffers[i] != 0)) - { - clip->StreamingQueue.Add(i); - } - } - - return clip->StreamingQueue.Count(); -} - -int32 AudioStreamingHandler::CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) -{ - // No smoothing or slowdown in residency change - return targetResidency; -} - -bool AudioStreamingHandler::RequiresStreaming(StreamableResource* resource, int32 currentResidency, int32 targetResidency) -{ - // Audio clips use streaming queue buffer to detect streaming request start - const auto clip = static_cast(resource); - return clip->StreamingQueue.HasItems(); -} diff --git a/Source/Engine/Streaming/Handlers/AudioStreamingHandler.h b/Source/Engine/Streaming/Handlers/AudioStreamingHandler.h deleted file mode 100644 index c88481e22..000000000 --- a/Source/Engine/Streaming/Handlers/AudioStreamingHandler.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#pragma once - -#include "Engine/Streaming/IStreamingHandler.h" - -/// -/// Implementation of IStreamingHandler for audio resources. -/// -/// -class FLAXENGINE_API AudioStreamingHandler : public IStreamingHandler -{ -public: - - // [IStreamingHandler] - StreamingQuality CalculateTargetQuality(StreamableResource* resource, DateTime now) override; - int32 CalculateResidency(StreamableResource* resource, StreamingQuality quality) override; - int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; - bool RequiresStreaming(StreamableResource* resource, int32 currentResidency, int32 targetResidency) override; -}; diff --git a/Source/Engine/Streaming/Handlers/ModelsStreamingHandler.cpp b/Source/Engine/Streaming/Handlers/ModelsStreamingHandler.cpp deleted file mode 100644 index 8c99f5155..000000000 --- a/Source/Engine/Streaming/Handlers/ModelsStreamingHandler.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#include "ModelsStreamingHandler.h" -#include "Engine/Content/Assets/Model.h" - -StreamingQuality ModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now) -{ - ASSERT(resource); - - // TODO: calculate a proper quality levels for models based on render time and streaming enable/disable options - - return MAX_STREAMING_QUALITY; -} - -int32 ModelsStreamingHandler::CalculateResidency(StreamableResource* resource, StreamingQuality quality) -{ - ASSERT(resource); - auto& model = *(Model*)resource; - auto lodCount = model.GetLODsCount(); - - if (quality < ZeroTolerance) - return 0; - - int32 lods = Math::CeilToInt(quality * (StreamingQuality)lodCount); - - ASSERT(model.IsValidLODIndex(lods - 1)); - return lods; -} - -int32 ModelsStreamingHandler::CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) -{ - ASSERT(resource); - auto& model = *(Model*)resource; - - // Always load only single LOD at once - int32 residency = targetResidency; - int32 currentResidency = model.GetCurrentResidency(); - if (currentResidency < targetResidency) - { - // Up - residency = currentResidency + 1; - } - else if (currentResidency > targetResidency) - { - // Down - residency = currentResidency - 1; - } - - return residency; -} diff --git a/Source/Engine/Streaming/Handlers/ModelsStreamingHandler.h b/Source/Engine/Streaming/Handlers/ModelsStreamingHandler.h deleted file mode 100644 index 02161f3ae..000000000 --- a/Source/Engine/Streaming/Handlers/ModelsStreamingHandler.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#pragma once - -#include "Engine/Streaming/IStreamingHandler.h" - -/// -/// Implementation of IStreamingHandler for streamable models. -/// -/// -class FLAXENGINE_API ModelsStreamingHandler : public IStreamingHandler -{ -public: - - // [IStreamingHandler] - StreamingQuality CalculateTargetQuality(StreamableResource* resource, DateTime now) override; - int32 CalculateResidency(StreamableResource* resource, StreamingQuality quality) override; - int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; -}; diff --git a/Source/Engine/Streaming/Handlers/SkinnedModelsStreamingHandler.cpp b/Source/Engine/Streaming/Handlers/SkinnedModelsStreamingHandler.cpp deleted file mode 100644 index 3e4586dc2..000000000 --- a/Source/Engine/Streaming/Handlers/SkinnedModelsStreamingHandler.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#include "SkinnedModelsStreamingHandler.h" -#include "Engine/Content/Assets/SkinnedModel.h" - -StreamingQuality SkinnedModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now) -{ - ASSERT(resource); - - // TODO: calculate a proper quality levels for models based on render time and streaming enable/disable options - - return MAX_STREAMING_QUALITY; -} - -int32 SkinnedModelsStreamingHandler::CalculateResidency(StreamableResource* resource, StreamingQuality quality) -{ - ASSERT(resource); - auto& model = *(SkinnedModel*)resource; - auto lodCount = model.GetLODsCount(); - - if (quality < ZeroTolerance) - return 0; - - int32 lods = Math::CeilToInt(quality * lodCount); - - ASSERT(model.IsValidLODIndex(lods - 1)); - return lods; -} - -int32 SkinnedModelsStreamingHandler::CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) -{ - ASSERT(resource); - auto& model = *(SkinnedModel*)resource; - - // Always load only single LOD at once - int32 residency = targetResidency; - int32 currentResidency = model.GetCurrentResidency(); - if (currentResidency < targetResidency) - { - // Up - residency = currentResidency + 1; - } - else if (currentResidency > targetResidency) - { - // Down - residency = currentResidency - 1; - } - - return residency; -} diff --git a/Source/Engine/Streaming/Handlers/SkinnedModelsStreamingHandler.h b/Source/Engine/Streaming/Handlers/SkinnedModelsStreamingHandler.h deleted file mode 100644 index 1ef929ec9..000000000 --- a/Source/Engine/Streaming/Handlers/SkinnedModelsStreamingHandler.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#pragma once - -#include "Engine/Streaming/IStreamingHandler.h" - -/// -/// Implementation of IStreamingHandler for streamable skinned models. -/// -/// -class FLAXENGINE_API SkinnedModelsStreamingHandler : public IStreamingHandler -{ -public: - - // [IStreamingHandler] - StreamingQuality CalculateTargetQuality(StreamableResource* resource, DateTime now) override; - int32 CalculateResidency(StreamableResource* resource, StreamingQuality quality) override; - int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; -}; diff --git a/Source/Engine/Streaming/Handlers/TexturesStreamingHandler.cpp b/Source/Engine/Streaming/Handlers/TexturesStreamingHandler.cpp deleted file mode 100644 index d308124a8..000000000 --- a/Source/Engine/Streaming/Handlers/TexturesStreamingHandler.cpp +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#include "TexturesStreamingHandler.h" -#include "Engine/Core/Math/Math.h" -#include "Engine/Graphics/Textures/StreamingTexture.h" - -StreamingQuality TexturesStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now) -{ - ASSERT(resource); - auto& texture = *(StreamingTexture*)resource; - - // TODO: calculate a proper quality levels for the textures - - //return 0.5f; - - return MAX_STREAMING_QUALITY; -} - -int32 TexturesStreamingHandler::CalculateResidency(StreamableResource* resource, StreamingQuality quality) -{ - ASSERT(resource); - auto& texture = *(StreamingTexture*)resource; - ASSERT(texture.IsInitialized()); - const auto totalMipLevels = texture.TotalMipLevels(); - - if (quality < ZeroTolerance) - { - return 0; - } - - int32 mipLevels = Math::CeilToInt(quality * totalMipLevels); - if (mipLevels > 0 && mipLevels < 3 && totalMipLevels > 1 && texture.IsBlockCompressed()) - { - // Block compressed textures require minimum size of 4 (3 mips or more) - mipLevels = 3; - } - - ASSERT(Math::IsInRange(mipLevels, 0, totalMipLevels)); - return mipLevels; -} - -int32 TexturesStreamingHandler::CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) -{ - ASSERT(resource); - auto& texture = *(StreamingTexture*)resource; - ASSERT(texture.IsInitialized()); - - int32 residency = targetResidency; - int32 currentResidency = texture.GetCurrentResidency(); - - // Check if go up or down - if (currentResidency < targetResidency) - { - // Up - residency = Math::Min(currentResidency + 2, targetResidency); - - // Stream first a few mips very fast (they are very small) - const int32 QuickStartMipsCount = 6; - if (currentResidency == 0) - { - residency = Math::Min(QuickStartMipsCount, targetResidency); - } - } - else if (currentResidency > targetResidency) - { - // Down at once - residency = targetResidency; - } - - return residency; -} diff --git a/Source/Engine/Streaming/Handlers/TexturesStreamingHandler.h b/Source/Engine/Streaming/Handlers/TexturesStreamingHandler.h deleted file mode 100644 index bb0b3a526..000000000 --- a/Source/Engine/Streaming/Handlers/TexturesStreamingHandler.h +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#pragma once - -#include "Engine/Streaming/IStreamingHandler.h" - -/// -/// Implementation of IStreamingHandler for streamable textures. -/// -/// -class FLAXENGINE_API TexturesStreamingHandler : public IStreamingHandler -{ -public: - - // [IStreamingHandler] - StreamingQuality CalculateTargetQuality(StreamableResource* resource, DateTime now) override; - int32 CalculateResidency(StreamableResource* resource, StreamingQuality quality) override; - int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; -}; diff --git a/Source/Engine/Streaming/IStreamingHandler.h b/Source/Engine/Streaming/IStreamingHandler.h index fd221996d..3e9674141 100644 --- a/Source/Engine/Streaming/IStreamingHandler.h +++ b/Source/Engine/Streaming/IStreamingHandler.h @@ -2,13 +2,13 @@ #pragma once -#include "Config.h" -#include "Engine/Core/Types/DateTime.h" +#include "Engine/Core/Types/BaseTypes.h" class StreamingGroup; +class StreamableResource; /// -/// Base interface for all streamable resource handlers +/// Base interface for all streamable resource handlers that implement resource streaming policy. /// class FLAXENGINE_API IStreamingHandler { @@ -19,20 +19,20 @@ public: public: /// - /// Calculates target quality level for the given resource. + /// Calculates target quality level (0-1) for the given resource. /// /// The resource. /// The current time. - /// Target Quality - virtual StreamingQuality CalculateTargetQuality(StreamableResource* resource, DateTime now) = 0; + /// Target quality (0-1). + virtual float CalculateTargetQuality(StreamableResource* resource, DateTime now) = 0; /// /// Calculates the residency level for a given resource and quality level. /// /// The resource. - /// The quality level. + /// The quality level (0-1). /// Residency level - virtual int32 CalculateResidency(StreamableResource* resource, StreamingQuality quality) = 0; + virtual int32 CalculateResidency(StreamableResource* resource, float quality) = 0; /// /// Calculates the residency level to stream for a given resource and target residency. diff --git a/Source/Engine/Streaming/StreamableResource.cpp b/Source/Engine/Streaming/StreamableResource.cpp index 3d6c825fd..af758e786 100644 --- a/Source/Engine/Streaming/StreamableResource.cpp +++ b/Source/Engine/Streaming/StreamableResource.cpp @@ -1,13 +1,13 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "StreamableResource.h" -#include "StreamingManager.h" +#include "Streaming.h" StreamableResource::StreamableResource(StreamingGroup* group) : _group(group) , _isDynamic(true) , _isStreaming(false) - , _streamingQuality(MAX_STREAMING_QUALITY) + , _streamingQuality(1.0f) { ASSERT(_group != nullptr); } @@ -23,7 +23,7 @@ void StreamableResource::startStreaming(bool isDynamic) if (_isStreaming == false) { - StreamingManager::Resources.Add(this); + Streaming::Resources.Add(this); _isStreaming = true; } } @@ -32,7 +32,7 @@ void StreamableResource::stopStreaming() { if (_isStreaming == true) { - StreamingManager::Resources.Remove(this); + Streaming::Resources.Remove(this); Streaming = StreamingCache(); _isStreaming = false; } diff --git a/Source/Engine/Streaming/StreamableResource.h b/Source/Engine/Streaming/StreamableResource.h index 257b02c57..8b8854bfe 100644 --- a/Source/Engine/Streaming/StreamableResource.h +++ b/Source/Engine/Streaming/StreamableResource.h @@ -4,7 +4,6 @@ #include "Engine/Core/Types/DateTime.h" #include "Engine/Core/Collections/SamplesBuffer.h" -#include "Config.h" class StreamingGroup; class Task; @@ -18,7 +17,7 @@ protected: StreamingGroup* _group; bool _isDynamic, _isStreaming; - StreamingQuality _streamingQuality; + float _streamingQuality; StreamableResource(StreamingGroup* group); ~StreamableResource(); @@ -38,17 +37,13 @@ public: /// FORCE_INLINE bool IsDynamic() const { -#if ENABLE_RESOURCES_DYNAMIC_STREAMING return _isDynamic; -#else - return false; -#endif } /// /// Gets resource streaming quality level /// - FORCE_INLINE StreamingQuality GetStreamingQuality() const + FORCE_INLINE float GetStreamingQuality() const { return _streamingQuality; } @@ -114,7 +109,7 @@ public: DateTime LastUpdate = 0; int32 TargetResidency = 0; DateTime TargetResidencyChange = 0; - SamplesBuffer QualitySamples; + SamplesBuffer QualitySamples; }; StreamingCache Streaming; diff --git a/Source/Engine/Streaming/StreamingManager.cpp b/Source/Engine/Streaming/Streaming.cpp similarity index 88% rename from Source/Engine/Streaming/StreamingManager.cpp rename to Source/Engine/Streaming/Streaming.cpp index 3b557e85f..1ce53cf75 100644 --- a/Source/Engine/Streaming/StreamingManager.cpp +++ b/Source/Engine/Streaming/Streaming.cpp @@ -1,13 +1,15 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. -#include "StreamingManager.h" +#include "Streaming.h" #include "StreamableResource.h" #include "StreamingGroup.h" +#include "StreamingSettings.h" #include "Engine/Engine/EngineService.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Threading/Threading.h" #include "Engine/Threading/Task.h" #include "Engine/Graphics/GPUDevice.h" +#include "Engine/Serialization/Serialization.h" namespace StreamingManagerImpl { @@ -21,7 +23,6 @@ using namespace StreamingManagerImpl; class StreamingManagerService : public EngineService { public: - StreamingManagerService() : EngineService(TEXT("Streaming Manager"), 100) { @@ -32,7 +33,18 @@ public: StreamingManagerService StreamingManagerServiceInstance; -StreamableResourcesCollection StreamingManager::Resources; +StreamableResourcesCollection Streaming::Resources; +Array> Streaming::TextureGroups; + +void StreamingSettings::Apply() +{ + Streaming::TextureGroups = TextureGroups; +} + +void StreamingSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) +{ + DESERIALIZE(TextureGroups); +} void UpdateResource(StreamableResource* resource, DateTime now) { @@ -43,18 +55,18 @@ void UpdateResource(StreamableResource* resource, DateTime now) auto handler = group->GetHandler(); // Calculate target quality for that asset - StreamingQuality targetQuality = MAX_STREAMING_QUALITY; + float targetQuality = 1.0f; if (resource->IsDynamic()) { targetQuality = handler->CalculateTargetQuality(resource, now); // TODO: here we should apply resources group master scale (based on game settings quality level and memory level) - targetQuality = Math::Clamp(targetQuality, MIN_STREAMING_QUALITY, MAX_STREAMING_QUALITY); + targetQuality = Math::Saturate(targetQuality); } // Update quality smoothing resource->Streaming.QualitySamples.Add(targetQuality); targetQuality = resource->Streaming.QualitySamples.Maximum(); - targetQuality = Math::Clamp(targetQuality, MIN_STREAMING_QUALITY, MAX_STREAMING_QUALITY); + targetQuality = Math::Saturate(targetQuality); // Calculate target residency level (discrete value) auto maxResidency = resource->GetMaxResidency(); @@ -132,7 +144,7 @@ void StreamingManagerService::Update() // Check if skip update auto now = DateTime::NowUTC(); auto delta = now - LastUpdateTime; - int32 resourcesCount = StreamingManager::Resources.ResourcesCount(); + int32 resourcesCount = Streaming::Resources.ResourcesCount(); if (resourcesCount == 0 || delta < ManagerUpdatesInterval || GPUDevice::Instance->GetState() != GPUDevice::DeviceState::Ready) return; LastUpdateTime = now; @@ -154,7 +166,7 @@ void StreamingManagerService::Update() LastUpdateResourcesIndex = 0; // Peek resource - const auto resource = StreamingManager::Resources[LastUpdateResourcesIndex]; + const auto resource = Streaming::Resources[LastUpdateResourcesIndex]; // Try to update it if (now - resource->Streaming.LastUpdate >= ResourceUpdatesInterval && resource->CanBeUpdated()) diff --git a/Source/Engine/Streaming/Streaming.h b/Source/Engine/Streaming/Streaming.h new file mode 100644 index 000000000..35766639a --- /dev/null +++ b/Source/Engine/Streaming/Streaming.h @@ -0,0 +1,25 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Scripting/ScriptingType.h" +#include "TextureGroup.h" +#include "StreamableResourcesCollection.h" + +/// +/// The content streaming service. +/// +API_CLASS(Static) class FLAXENGINE_API Streaming +{ +DECLARE_SCRIPTING_TYPE_NO_SPAWN(Streaming); + + /// + /// List with all resources + /// + static StreamableResourcesCollection Resources; + + /// + /// Textures streaming configuration (per-group). + /// + API_FIELD() static Array> TextureGroups; +}; diff --git a/Source/Engine/Streaming/StreamingGroup.cpp b/Source/Engine/Streaming/StreamingGroup.cpp index 01c3a6744..c0a3a928d 100644 --- a/Source/Engine/Streaming/StreamingGroup.cpp +++ b/Source/Engine/Streaming/StreamingGroup.cpp @@ -1,10 +1,7 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #include "StreamingGroup.h" -#include "Handlers/TexturesStreamingHandler.h" -#include "Handlers/ModelsStreamingHandler.h" -#include "Handlers/SkinnedModelsStreamingHandler.h" -#include "Handlers/AudioStreamingHandler.h" +#include "StreamingHandlers.h" StreamingGroup::StreamingGroup(Type type, IStreamingHandler* handler) : _type(type) diff --git a/Source/Engine/Streaming/StreamingHandlers.cpp b/Source/Engine/Streaming/StreamingHandlers.cpp new file mode 100644 index 000000000..bf43d317a --- /dev/null +++ b/Source/Engine/Streaming/StreamingHandlers.cpp @@ -0,0 +1,230 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#include "StreamingHandlers.h" +#include "Streaming.h" +#include "Engine/Core/Math/Math.h" +#include "Engine/Graphics/Textures/StreamingTexture.h" +#include "Engine/Content/Assets/Model.h" +#include "Engine/Content/Assets/SkinnedModel.h" +#include "Engine/Audio/AudioClip.h" +#include "Engine/Audio/Audio.h" +#include "Engine/Audio/AudioSource.h" + +float TexturesStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now) +{ + ASSERT(resource); + auto& texture = *(StreamingTexture*)resource; + const TextureHeader& header = *texture.GetHeader(); + if (header.TextureGroup < 0 || header.TextureGroup >= Streaming::TextureGroups.Count()) + { + // Full texture load by default + return 1.0f; + } + + // Quality based on texture group settings + const TextureGroup& group = Streaming::TextureGroups[header.TextureGroup]; + return group.QualityScale; +} + +int32 TexturesStreamingHandler::CalculateResidency(StreamableResource* resource, float quality) +{ + if (quality < ZeroTolerance) + return 0; + ASSERT(resource); + auto& texture = *(StreamingTexture*)resource; + ASSERT(texture.IsInitialized()); + const TextureHeader& header = *texture.GetHeader(); + + const int32 totalMipLevels = texture.TotalMipLevels(); + int32 mipLevels = Math::CeilToInt(quality * (float)totalMipLevels); + + if (header.TextureGroup >= 0 && header.TextureGroup < Streaming::TextureGroups.Count()) + { + const TextureGroup& group = Streaming::TextureGroups[header.TextureGroup]; + mipLevels = Math::Clamp(mipLevels + group.MipLevelsBias, group.MipLevelsMin, group.MipLevelsMax); +#if USE_EDITOR + // Simulate per-platform limit in Editor + int32 max; + if (group.MipLevelsMaxPerPlatform.TryGet(PLATFORM_TYPE, max)) + mipLevels = Math::Min(mipLevels, max); +#endif + } + + if (mipLevels > 0 && mipLevels < 3 && totalMipLevels > 1 && texture.IsBlockCompressed()) + { + // Block compressed textures require minimum size of 4 (3 mips or more) + mipLevels = 3; + } + + return Math::Clamp(mipLevels, 0, totalMipLevels); +} + +int32 TexturesStreamingHandler::CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) +{ + ASSERT(resource); + auto& texture = *(StreamingTexture*)resource; + ASSERT(texture.IsInitialized()); + int32 residency = targetResidency; + int32 currentResidency = texture.GetCurrentResidency(); + if (currentResidency < targetResidency) + { + // Up + residency = Math::Min(currentResidency + 2, targetResidency); + + // Stream first a few mips very fast (they are very small) + const int32 QuickStartMipsCount = 6; + if (currentResidency == 0) + { + residency = Math::Min(QuickStartMipsCount, targetResidency); + } + } + else if (currentResidency > targetResidency) + { + // Down at once + residency = targetResidency; + } + return residency; +} + +float ModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now) +{ + // TODO: calculate a proper quality levels for models based on render time and streaming enable/disable options + return 1.0f; +} + +int32 ModelsStreamingHandler::CalculateResidency(StreamableResource* resource, float quality) +{ + if (quality < ZeroTolerance) + return 0; + ASSERT(resource); + auto& model = *(Model*)resource; + + const int32 lodCount = model.GetLODsCount(); + int32 lods = Math::CeilToInt(quality * (float)lodCount); + ASSERT(model.IsValidLODIndex(lods - 1)); + return lods; +} + +int32 ModelsStreamingHandler::CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) +{ + ASSERT(resource); + auto& model = *(Model*)resource; + + // Always load only single LOD at once + int32 residency = targetResidency; + int32 currentResidency = model.GetCurrentResidency(); + if (currentResidency < targetResidency) + { + // Up + residency = currentResidency + 1; + } + else if (currentResidency > targetResidency) + { + // Down + residency = currentResidency - 1; + } + + return residency; +} + +float SkinnedModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now) +{ + // TODO: calculate a proper quality levels for models based on render time and streaming enable/disable options + return 1.0f; +} + +int32 SkinnedModelsStreamingHandler::CalculateResidency(StreamableResource* resource, float quality) +{ + if (quality < ZeroTolerance) + return 0; + ASSERT(resource); + auto& model = *(SkinnedModel*)resource; + + const int32 lodCount = model.GetLODsCount(); + int32 lods = Math::CeilToInt(quality * (float)lodCount); + ASSERT(model.IsValidLODIndex(lods - 1)); + return lods; +} + +int32 SkinnedModelsStreamingHandler::CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) +{ + ASSERT(resource); + auto& model = *(SkinnedModel*)resource; + + // Always load only single LOD at once + int32 residency = targetResidency; + int32 currentResidency = model.GetCurrentResidency(); + if (currentResidency < targetResidency) + { + // Up + residency = currentResidency + 1; + } + else if (currentResidency > targetResidency) + { + // Down + residency = currentResidency - 1; + } + + return residency; +} + +float AudioStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now) +{ + // Audio clips don't use quality but only residency + return 1.0f; +} + +int32 AudioStreamingHandler::CalculateResidency(StreamableResource* resource, float quality) +{ + ASSERT(resource); + auto clip = static_cast(resource); + const int32 chunksCount = clip->Buffers.Count(); + bool chunksMask[ASSET_FILE_DATA_CHUNKS]; + Platform::MemoryClear(chunksMask, sizeof(chunksMask)); + + // Find audio chunks required for streaming + clip->StreamingQueue.Clear(); + for (int32 sourceIndex = 0; sourceIndex < Audio::Sources.Count(); sourceIndex++) + { + // TODO: collect refs to audio clip from sources and use faster iteration (but do it thread-safe) + + const auto src = Audio::Sources[sourceIndex]; + if (src->Clip == clip && src->GetState() == AudioSource::States::Playing) + { + // Stream the current and the next chunk if could be used in a while + const int32 chunk = src->_streamingFirstChunk; + ASSERT(Math::IsInRange(chunk, 0, chunksCount)); + chunksMask[chunk] = true; + + const float StreamingDstSec = 2.0f; + if (chunk + 1 < chunksCount && src->GetTime() + StreamingDstSec >= clip->GetBufferStartTime(src->_streamingFirstChunk)) + { + chunksMask[chunk + 1] = true; + } + } + } + + // Try to enqueue chunks to modify (load or unload) + for (int32 i = 0; i < chunksCount; i++) + { + if (chunksMask[i] != (clip->Buffers[i] != 0)) + { + clip->StreamingQueue.Add(i); + } + } + + return clip->StreamingQueue.Count(); +} + +int32 AudioStreamingHandler::CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) +{ + // No smoothing or slowdown in residency change + return targetResidency; +} + +bool AudioStreamingHandler::RequiresStreaming(StreamableResource* resource, int32 currentResidency, int32 targetResidency) +{ + // Audio clips use streaming queue buffer to detect streaming request start + const auto clip = static_cast(resource); + return clip->StreamingQueue.HasItems(); +} diff --git a/Source/Engine/Streaming/StreamingHandlers.h b/Source/Engine/Streaming/StreamingHandlers.h new file mode 100644 index 000000000..b6080d616 --- /dev/null +++ b/Source/Engine/Streaming/StreamingHandlers.h @@ -0,0 +1,54 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Streaming/IStreamingHandler.h" + +/// +/// Implementation of IStreamingHandler for streamable textures. +/// +class FLAXENGINE_API TexturesStreamingHandler : public IStreamingHandler +{ +public: + // [IStreamingHandler] + float CalculateTargetQuality(StreamableResource* resource, DateTime now) override; + int32 CalculateResidency(StreamableResource* resource, float quality) override; + int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; +}; + +/// +/// Implementation of IStreamingHandler for streamable models. +/// +class FLAXENGINE_API ModelsStreamingHandler : public IStreamingHandler +{ +public: + // [IStreamingHandler] + float CalculateTargetQuality(StreamableResource* resource, DateTime now) override; + int32 CalculateResidency(StreamableResource* resource, float quality) override; + int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; +}; + +/// +/// Implementation of IStreamingHandler for streamable skinned models. +/// +class FLAXENGINE_API SkinnedModelsStreamingHandler : public IStreamingHandler +{ +public: + // [IStreamingHandler] + float CalculateTargetQuality(StreamableResource* resource, DateTime now) override; + int32 CalculateResidency(StreamableResource* resource, float quality) override; + int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; +}; + +/// +/// Implementation of IStreamingHandler for audio clips. +/// +class FLAXENGINE_API AudioStreamingHandler : public IStreamingHandler +{ +public: + // [IStreamingHandler] + float CalculateTargetQuality(StreamableResource* resource, DateTime now) override; + int32 CalculateResidency(StreamableResource* resource, float quality) override; + int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; + bool RequiresStreaming(StreamableResource* resource, int32 currentResidency, int32 targetResidency) override; +}; diff --git a/Source/Engine/Streaming/StreamingManager.h b/Source/Engine/Streaming/StreamingManager.h deleted file mode 100644 index 732ed3f96..000000000 --- a/Source/Engine/Streaming/StreamingManager.h +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#pragma once - -#include "StreamableResourcesCollection.h" - -/// -/// Main class for dynamic resources streaming service -/// -class FLAXENGINE_API StreamingManager -{ -public: - - /// - /// List with all resources - /// - static StreamableResourcesCollection Resources; -}; diff --git a/Source/Engine/Streaming/StreamingSettings.h b/Source/Engine/Streaming/StreamingSettings.h new file mode 100644 index 000000000..7302696a0 --- /dev/null +++ b/Source/Engine/Streaming/StreamingSettings.h @@ -0,0 +1,32 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Core/Config/Settings.h" +#include "TextureGroup.h" + +/// +/// Content streaming settings. +/// +API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings", NoConstructor) class FLAXENGINE_API StreamingSettings : public SettingsBase +{ +DECLARE_SCRIPTING_TYPE_MINIMAL(StreamingSettings); +public: + + /// + /// Textures streaming configuration (per-group). + /// + API_FIELD(Attributes="EditorOrder(100), EditorDisplay(\"Textures\")") + Array> TextureGroups; + +public: + + /// + /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. + /// + static StreamingSettings* Get(); + + // [SettingsBase] + void Apply() override; + void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override; +}; diff --git a/Source/Engine/Streaming/TextureGroup.h b/Source/Engine/Streaming/TextureGroup.h new file mode 100644 index 000000000..dfd6dba07 --- /dev/null +++ b/Source/Engine/Streaming/TextureGroup.h @@ -0,0 +1,56 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Core/Types/String.h" +#include "Engine/Core/ISerializable.h" +#if USE_EDITOR +#include "Engine/Core/Collections/Dictionary.h" +#endif + +/// +/// Settings container for a group of textures. Defines the data streaming options and resource quality. +/// +API_STRUCT() struct TextureGroup : ISerializable +{ +API_AUTO_SERIALIZATION(); +DECLARE_SCRIPTING_TYPE_MINIMAL(TextureGroup); + + /// + /// The name of the group. + /// + API_FIELD(Attributes="EditorOrder(10)") + String Name; + + /// + /// The quality scale factor applied to textures in this group. Can be used to increase or decrease textures resolution. In range 0-1 where 0 means lowest quality, 1 means full quality. + /// + API_FIELD(Attributes="EditorOrder(20), Limit(0, 1)") + float QualityScale = 1.0f; + + /// + /// The minimum amount of loaded mip levels for textures in this group. Defines the amount of the mips that should be always loaded. Higher values decrease streaming usage and keep more mips loaded. + /// + API_FIELD(Attributes="EditorOrder(30), Limit(0, 14)") + int32 MipLevelsMin = 0; + + /// + /// The maximum amount of loaded mip levels for textures in this group. Defines the maximum amount of the mips that can be loaded. Overriden per-platform. Lower values reduce textures quality and improve performance. + /// + API_FIELD(Attributes="EditorOrder(40), Limit(1, 14)") + int32 MipLevelsMax = 14; + + /// + /// The loaded mip levels bias for textures in this group. Can be used to increase or decrease quality of the streaming for textures in this group (eg. bump up the quality during cinematic sequence). + /// + API_FIELD(Attributes="EditorOrder(50), Limit(-14, 14)") + int32 MipLevelsBias = 0; + +#if USE_EDITOR + /// + /// The per-platform maximum amount of mip levels for textures in this group. Can be used to strip textures quality when cooking game for a target platform. + /// + API_FIELD(Attributes="EditorOrder(50)") + Dictionary MipLevelsMaxPerPlatform; +#endif +}; diff --git a/Source/Engine/Tools/TextureTool/TextureTool.cpp b/Source/Engine/Tools/TextureTool/TextureTool.cpp index bd8970c59..e607a862e 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.cpp @@ -35,6 +35,7 @@ TextureTool::Options::Options() Resize = false; PreserveAlphaCoverage = false; PreserveAlphaCoverageReference = 0.5f; + TextureGroup = -1; Scale = 1.0f; SizeX = 1024; SizeY = 1024; @@ -97,6 +98,9 @@ void TextureTool::Options::Serialize(SerializeStream& stream, const void* otherO stream.JKEY("PreserveAlphaCoverageReference"); stream.Float(PreserveAlphaCoverageReference); + stream.JKEY("TextureGroup"); + stream.Int(TextureGroup); + stream.JKEY("Scale"); stream.Float(Scale); @@ -145,6 +149,7 @@ void TextureTool::Options::Deserialize(DeserializeStream& stream, ISerializeModi Resize = JsonTools::GetBool(stream, "Resize", Resize); PreserveAlphaCoverage = JsonTools::GetBool(stream, "PreserveAlphaCoverage", PreserveAlphaCoverage); PreserveAlphaCoverageReference = JsonTools::GetFloat(stream, "PreserveAlphaCoverageReference", PreserveAlphaCoverageReference); + TextureGroup = JsonTools::GetInt(stream, "TextureGroup", TextureGroup); Scale = JsonTools::GetFloat(stream, "Scale", Scale); SizeX = JsonTools::GetInt(stream, "SizeX", SizeX); SizeY = JsonTools::GetInt(stream, "SizeY", SizeY); diff --git a/Source/Engine/Tools/TextureTool/TextureTool.h b/Source/Engine/Tools/TextureTool/TextureTool.h index 6337406ae..a9f957f00 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.h +++ b/Source/Engine/Tools/TextureTool/TextureTool.h @@ -78,6 +78,11 @@ public: /// float PreserveAlphaCoverageReference; + /// + /// Texture group for streaming (negative if unused). See Streaming Settings. + /// + int32 TextureGroup; + /// /// The import texture scale. /// From 289515a78bf2cbf040cdb071ded0ed4726419981 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 17 Jun 2021 16:01:01 +0200 Subject: [PATCH 188/198] Add support for stripping texture resolution for target platform when cooking game --- Flax.flaxproj | 2 +- Source/Editor/Cooker/Steps/CookAssetsStep.cpp | 249 ++++++++++++------ Source/Editor/Cooker/Steps/CookAssetsStep.h | 8 + .../Engine/Graphics/Textures/TextureBase.cpp | 45 ++++ Source/Engine/Graphics/Textures/TextureData.h | 8 +- Source/FlaxEngine.Gen.cs | 4 +- Source/FlaxEngine.Gen.h | 6 +- 7 files changed, 234 insertions(+), 88 deletions(-) diff --git a/Flax.flaxproj b/Flax.flaxproj index a7178c5cd..e135c2741 100644 --- a/Flax.flaxproj +++ b/Flax.flaxproj @@ -3,7 +3,7 @@ "Version": { "Major": 1, "Minor": 1, - "Build": 6220 + "Build": 6221 }, "Company": "Flax", "Copyright": "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.", diff --git a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp index 5f097f638..703b4179a 100644 --- a/Source/Editor/Cooker/Steps/CookAssetsStep.cpp +++ b/Source/Editor/Cooker/Steps/CookAssetsStep.cpp @@ -25,6 +25,7 @@ #include "Engine/Core/Config/PlatformSettings.h" #include "Engine/Core/Config/GameSettings.h" #include "Engine/Core/Config/BuildSettings.h" +#include "Engine/Streaming/StreamingSettings.h" #include "Engine/ShadersCompilation/ShadersCompilation.h" #include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/Textures/TextureData.h" @@ -44,6 +45,35 @@ Dictionary CookAssetsStep::AssetProcessors; +bool CookAssetsStep::CacheEntry::IsValid(bool withDependencies) +{ + AssetInfo assetInfo; + if (Content::GetAssetInfo(ID, assetInfo)) + { + if (TypeName == assetInfo.TypeName) + { + if (FileSystem::GetFileLastEditTime(assetInfo.Path) <= FileModified) + { + bool isValid = true; + if (withDependencies) + { + for (auto& f : FileDependencies) + { + if (FileSystem::GetFileLastEditTime(f.First) > f.Second) + { + isValid = false; + break; + } + } + } + if (isValid) + return true; + } + } + } + return false; +} + CookAssetsStep::CacheEntry& CookAssetsStep::CacheData::CreateEntry(const JsonAssetBase* asset, String& cachedFilePath) { ASSERT(asset->DataTypeName.HasChars()); @@ -68,7 +98,6 @@ CookAssetsStep::CacheEntry& CookAssetsStep::CacheData::CreateEntry(const Asset* void CookAssetsStep::CacheData::InvalidateShaders() { LOG(Info, "Invalidating cached shader assets."); - for (auto e = Entries.Begin(); e.IsNotEnd(); ++e) { auto& typeName = e->Value.TypeName; @@ -83,6 +112,23 @@ void CookAssetsStep::CacheData::InvalidateShaders() } } +void CookAssetsStep::CacheData::InvalidateTextures() +{ + LOG(Info, "Invalidating cached texture assets."); + for (auto e = Entries.Begin(); e.IsNotEnd(); ++e) + { + auto& typeName = e->Value.TypeName; + if ( + typeName == Texture::TypeName || + typeName == CubeTexture::TypeName || + typeName == SpriteAtlas::TypeName + ) + { + Entries.Remove(e); + } + } +} + void CookAssetsStep::CacheData::Load(CookingData& data) { HeaderFilePath = data.CacheDirectory / String::Format(TEXT("CookedHeader_{0}.bin"), FLAXENGINE_VERSION_BUILD); @@ -159,17 +205,17 @@ void CookAssetsStep::CacheData::Load(CookingData& data) Entries.Clear(); } + const auto buildSettings = BuildSettings::Get(); + const auto gameSettings = GameSettings::Get(); + // Invalidate shaders and assets with shaders if need to rebuild them bool invalidateShaders = false; - const auto buildSettings = BuildSettings::Get(); - const bool shadersNoOptimize = buildSettings->ShadersNoOptimize; - const bool shadersGenerateDebugData = buildSettings->ShadersGenerateDebugData; - if (shadersNoOptimize != Settings.Global.ShadersNoOptimize) + if (buildSettings->ShadersNoOptimize != Settings.Global.ShadersNoOptimize) { LOG(Info, "ShadersNoOptimize option has been modified."); invalidateShaders = true; } - if (shadersGenerateDebugData != Settings.Global.ShadersGenerateDebugData) + if (buildSettings->ShadersGenerateDebugData != Settings.Global.ShadersGenerateDebugData) { LOG(Info, "ShadersGenerateDebugData option has been modified."); invalidateShaders = true; @@ -218,6 +264,12 @@ void CookAssetsStep::CacheData::Load(CookingData& data) #endif if (invalidateShaders) InvalidateShaders(); + + // Invalidate textures if streaming settings gets modified + if (Settings.Global.StreamingSettingsAssetId != gameSettings->Streaming || (Entries.ContainsKey(gameSettings->Streaming) && !Entries[gameSettings->Streaming].IsValid())) + { + InvalidateTextures(); + } } void CookAssetsStep::CacheData::Save() @@ -541,91 +593,127 @@ bool ProcessParticleEmitter(CookAssetsStep::AssetCookData& data) bool ProcessTextureBase(CookAssetsStep::AssetCookData& data) { const auto asset = static_cast(data.Asset); - - // Check if target platform doesn't support the texture format + const auto& assetHeader = asset->StreamingTexture()->GetHeader(); const auto format = asset->Format(); const auto targetFormat = data.Data.Tools->GetTextureFormat(data.Data, asset, format); + const auto streamingSettings = StreamingSettings::Get(); + int32 mipLevelsMax = GPU_MAX_TEXTURE_MIP_LEVELS; + if (assetHeader->TextureGroup >= 0 && assetHeader->TextureGroup < streamingSettings->TextureGroups.Count()) + { + auto& group = streamingSettings->TextureGroups[assetHeader->TextureGroup]; + mipLevelsMax = group.MipLevelsMax; + group.MipLevelsMaxPerPlatform.TryGet(data.Data.Tools->GetPlatform(), mipLevelsMax); + } + + // Faster path if don't need to modify texture for the target platform + if (format == targetFormat && assetHeader->MipLevels <= mipLevelsMax) + { + return CookAssetsStep::ProcessDefaultAsset(data); + } + + // Extract texture data from the asset + TextureData textureDataSrc; + auto assetLock = asset->LockData(); + if (asset->GetTextureData(textureDataSrc, false)) + { + LOG(Error, "Failed to load data from texture {0}", asset->ToString()); + return true; + } + + TextureData* textureData = &textureDataSrc; + TextureData textureDataTmp1; + if (format != targetFormat) { - // Extract texture data from the asset - TextureData textureData; - auto assetLock = asset->LockData(); - if (asset->GetTextureData(textureData, false)) - { - LOG(Error, "Failed to load data from texture {0}", asset->ToString()); - return true; - } - // Convert texture data to the target format - TextureData targetTextureData; - if (TextureTool::Convert(targetTextureData, textureData, targetFormat)) + if (TextureTool::Convert(textureDataTmp1, *textureData, targetFormat)) { LOG(Error, "Failed to convert texture {0} from format {1} to {2}", asset->ToString(), (int32)format, (int32)targetFormat); return true; } - - // Adjust texture header - auto& header = *(TextureHeader*)data.InitData.CustomData.Get(); - header.Width = targetTextureData.Width; - header.Height = targetTextureData.Height; - header.Format = targetTextureData.Format; - header.MipLevels = targetTextureData.GetMipLevels(); - - // Serialize texture data into the asset chunks - for (int32 mipIndex = 0; mipIndex < targetTextureData.GetMipLevels(); mipIndex++) - { - auto chunk = New(); - data.InitData.Header.Chunks[mipIndex] = chunk; - - // Calculate the texture data storage layout - uint32 rowPitch, slicePitch; - const int32 mipWidth = Math::Max(1, targetTextureData.Width >> mipIndex); - const int32 mipHeight = Math::Max(1, targetTextureData.Height >> mipIndex); - RenderTools::ComputePitch(targetTextureData.Format, mipWidth, mipHeight, rowPitch, slicePitch); - chunk->Data.Allocate(slicePitch * targetTextureData.GetArraySize()); - - // Copy array slices into mip data (sequential) - for (int32 arrayIndex = 0; arrayIndex < targetTextureData.Items.Count(); arrayIndex++) - { - auto& mipData = targetTextureData.Items[arrayIndex].Mips[mipIndex]; - byte* src = mipData.Data.Get(); - byte* dst = chunk->Data.Get() + (slicePitch * arrayIndex); - - // Faster path if source and destination data layout matches - if (rowPitch == mipData.RowPitch && slicePitch == mipData.DepthPitch) - { - Platform::MemoryCopy(dst, src, slicePitch); - } - else - { - const auto copyRowSize = Math::Min(mipData.RowPitch, rowPitch); - for (uint32 line = 0; line < mipData.Lines; line++) - { - Platform::MemoryCopy(dst, src, copyRowSize); - src += mipData.RowPitch; - dst += rowPitch; - } - } - } - } - - // Clone any custom asset chunks (eg. sprite atlas data, mips are in 0-13 chunks) - for (int32 i = 14; i < ASSET_FILE_DATA_CHUNKS; i++) - { - const auto chunk = asset->GetChunk(i); - if (chunk != nullptr && chunk->IsMissing() && chunk->ExistsInFile()) - { - if (asset->Storage->LoadAssetChunk(chunk)) - return true; - data.InitData.Header.Chunks[i] = chunk->Clone(); - } - } - - return false; + textureData = &textureDataSrc; } - // Fallback to the default asset processing - return CookAssetsStep::ProcessDefaultAsset(data); + if (assetHeader->MipLevels > mipLevelsMax) + { + // Reduce texture quality + const int32 mipLevelsToStrip = assetHeader->MipLevels - mipLevelsMax; + textureData->Width = Math::Max(1, textureData->Width >> mipLevelsToStrip); + textureData->Height = Math::Max(1, textureData->Height >> mipLevelsToStrip); + textureData->Depth = Math::Max(1, textureData->Depth >> mipLevelsToStrip); + for (int32 arrayIndex = 0; arrayIndex < textureData->Items.Count(); arrayIndex++) + { + auto& item = textureData->Items[arrayIndex]; + Array> oldMips(MoveTemp(item.Mips)); + item.Mips.Resize(mipLevelsMax); + for (int32 mipIndex = 0; mipIndex < mipLevelsMax; mipIndex++) + { + auto& dstMip = item.Mips[mipIndex]; + auto& srcMip = oldMips[mipIndex + mipLevelsToStrip]; + dstMip = MoveTemp(srcMip); + } + } + } + + // Adjust texture header + auto& header = *(TextureHeader*)data.InitData.CustomData.Get(); + header.Width = textureData->Width; + header.Height = textureData->Height; + header.Depth = textureData->Depth; + header.Format = textureData->Format; + header.MipLevels = textureData->GetMipLevels(); + + // Serialize texture data into the asset chunks + for (int32 mipIndex = 0; mipIndex < textureData->GetMipLevels(); mipIndex++) + { + auto chunk = New(); + data.InitData.Header.Chunks[mipIndex] = chunk; + + // Calculate the texture data storage layout + uint32 rowPitch, slicePitch; + const int32 mipWidth = Math::Max(1, textureData->Width >> mipIndex); + const int32 mipHeight = Math::Max(1, textureData->Height >> mipIndex); + RenderTools::ComputePitch(textureData->Format, mipWidth, mipHeight, rowPitch, slicePitch); + chunk->Data.Allocate(slicePitch * textureData->GetArraySize()); + + // Copy array slices into mip data (sequential) + for (int32 arrayIndex = 0; arrayIndex < textureData->Items.Count(); arrayIndex++) + { + auto& mipData = textureData->Items[arrayIndex].Mips[mipIndex]; + byte* src = mipData.Data.Get(); + byte* dst = chunk->Data.Get() + (slicePitch * arrayIndex); + + // Faster path if source and destination data layout matches + if (rowPitch == mipData.RowPitch && slicePitch == mipData.DepthPitch) + { + Platform::MemoryCopy(dst, src, slicePitch); + } + else + { + const auto copyRowSize = Math::Min(mipData.RowPitch, rowPitch); + for (uint32 line = 0; line < mipData.Lines; line++) + { + Platform::MemoryCopy(dst, src, copyRowSize); + src += mipData.RowPitch; + dst += rowPitch; + } + } + } + } + + // Clone any custom asset chunks (eg. sprite atlas data, mips are in 0-13 chunks) + for (int32 i = 14; i < ASSET_FILE_DATA_CHUNKS; i++) + { + const auto chunk = asset->GetChunk(i); + if (chunk != nullptr && chunk->IsMissing() && chunk->ExistsInFile()) + { + if (asset->Storage->LoadAssetChunk(chunk)) + return true; + data.InitData.Header.Chunks[i] = chunk->Clone(); + } + } + + return false; } CookAssetsStep::CookAssetsStep() @@ -938,6 +1026,7 @@ bool CookAssetsStep::Perform(CookingData& data) { cache.Settings.Global.ShadersNoOptimize = buildSettings->ShadersNoOptimize; cache.Settings.Global.ShadersGenerateDebugData = buildSettings->ShadersGenerateDebugData; + cache.Settings.Global.StreamingSettingsAssetId = gameSettings->Streaming; } // Note: this step converts all the assets (even the json) into the binary files (FlaxStorage format). diff --git a/Source/Editor/Cooker/Steps/CookAssetsStep.h b/Source/Editor/Cooker/Steps/CookAssetsStep.h index 8896e1e99..f134d3dd9 100644 --- a/Source/Editor/Cooker/Steps/CookAssetsStep.h +++ b/Source/Editor/Cooker/Steps/CookAssetsStep.h @@ -49,6 +49,8 @@ public: /// The list of files on which this entry depends on. Cached date is the last edit time used to discard cache result on modification. /// FileDependenciesList FileDependencies; + + bool IsValid(bool withDependencies = false); }; /// @@ -93,6 +95,7 @@ public: { bool ShadersNoOptimize; bool ShadersGenerateDebugData; + Guid StreamingSettingsAssetId; } Global; } Settings; @@ -134,6 +137,11 @@ public: /// void InvalidateShaders(); + /// + /// Removes all cached entries for assets that contain a texture. This forces rebuild for them. + /// + void InvalidateTextures(); + /// /// Loads the cache for the given cooking data. /// diff --git a/Source/Engine/Graphics/Textures/TextureBase.cpp b/Source/Engine/Graphics/Textures/TextureBase.cpp index a4aeccbcb..59acc488e 100644 --- a/Source/Engine/Graphics/Textures/TextureBase.cpp +++ b/Source/Engine/Graphics/Textures/TextureBase.cpp @@ -12,6 +12,51 @@ #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Content/Factories/BinaryAssetFactory.h" +TextureMipData::TextureMipData() + : RowPitch(0) + , DepthPitch(0) + , Lines(0) +{ +} + +TextureMipData::TextureMipData(const TextureMipData& other) + : RowPitch(other.RowPitch) + , DepthPitch(other.DepthPitch) + , Lines(other.Lines) + , Data(other.Data) +{ +} + +TextureMipData::TextureMipData(TextureMipData&& other) noexcept + : RowPitch(other.RowPitch) + , DepthPitch(other.DepthPitch) + , Lines(other.Lines) + , Data(std::move(other.Data)) +{ +} + +TextureMipData& TextureMipData::operator=(const TextureMipData& other) +{ + if (this == &other) + return *this; + RowPitch = other.RowPitch; + DepthPitch = other.DepthPitch; + Lines = other.Lines; + Data = other.Data; + return *this; +} + +TextureMipData& TextureMipData::operator=(TextureMipData&& other) noexcept +{ + if (this == &other) + return *this; + RowPitch = other.RowPitch; + DepthPitch = other.DepthPitch; + Lines = other.Lines; + Data = MoveTemp(other.Data); + return *this; +} + REGISTER_BINARY_ASSET_ABSTRACT(TextureBase, "FlaxEngine.TextureBase"); TextureBase::TextureBase(const SpawnParams& params, const AssetInfo* info) diff --git a/Source/Engine/Graphics/Textures/TextureData.h b/Source/Engine/Graphics/Textures/TextureData.h index e5e1eba57..dcae204c8 100644 --- a/Source/Engine/Graphics/Textures/TextureData.h +++ b/Source/Engine/Graphics/Textures/TextureData.h @@ -19,6 +19,12 @@ public: uint32 Lines; BytesContainer Data; + TextureMipData(); + TextureMipData(const TextureMipData& other); + TextureMipData(TextureMipData&& other) noexcept; + TextureMipData& operator=(const TextureMipData& other); + TextureMipData& operator=(TextureMipData&& other) noexcept; + template T& Get(int32 x, int32 y) { @@ -120,7 +126,6 @@ public: /// /// Gets amount of textures in the array /// - /// Array size int32 GetArraySize() const { return Items.Count(); @@ -129,7 +134,6 @@ public: /// /// Gets amount of mip maps in the textures /// - /// Amount of mip levels int32 GetMipLevels() const { return Items.HasItems() ? Items[0].Mips.Count() : 0; diff --git a/Source/FlaxEngine.Gen.cs b/Source/FlaxEngine.Gen.cs index 5206c98f4..2fe4bf65a 100644 --- a/Source/FlaxEngine.Gen.cs +++ b/Source/FlaxEngine.Gen.cs @@ -13,5 +13,5 @@ using System.Runtime.InteropServices; [assembly: AssemblyCulture("")] [assembly: ComVisible(false)] [assembly: Guid("b8442186-4a70-7c85-704a-857c262d00f6")] -[assembly: AssemblyVersion("1.1.6220")] -[assembly: AssemblyFileVersion("1.1.6220")] +[assembly: AssemblyVersion("1.1.6221")] +[assembly: AssemblyFileVersion("1.1.6221")] diff --git a/Source/FlaxEngine.Gen.h b/Source/FlaxEngine.Gen.h index 0e083b5ab..add6b8f11 100644 --- a/Source/FlaxEngine.Gen.h +++ b/Source/FlaxEngine.Gen.h @@ -3,11 +3,11 @@ #pragma once #define FLAXENGINE_NAME "FlaxEngine" -#define FLAXENGINE_VERSION Version(1, 1, 6220) -#define FLAXENGINE_VERSION_TEXT "1.1.6220" +#define FLAXENGINE_VERSION Version(1, 1, 6221) +#define FLAXENGINE_VERSION_TEXT "1.1.6221" #define FLAXENGINE_VERSION_MAJOR 1 #define FLAXENGINE_VERSION_MINOR 1 -#define FLAXENGINE_VERSION_BUILD 6220 +#define FLAXENGINE_VERSION_BUILD 6221 #define FLAXENGINE_COMPANY "Flax" #define FLAXENGINE_COPYRIGHT "Copyright (c) 2012-2021 Wojciech Figat. All rights reserved." From 2d88ed17d402b9be566842117e9e93385c126b5a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 18 Jun 2021 09:43:35 +0200 Subject: [PATCH 189/198] Add dedicated editor for Texture Group picking --- .../Content/Import/TextureImportEntry.cs | 1 + .../Dedicated/TextureGroupEditor.cs | 51 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 Source/Editor/CustomEditors/Dedicated/TextureGroupEditor.cs diff --git a/Source/Editor/Content/Import/TextureImportEntry.cs b/Source/Editor/Content/Import/TextureImportEntry.cs index 9c435c120..0985f7e5a 100644 --- a/Source/Editor/Content/Import/TextureImportEntry.cs +++ b/Source/Editor/Content/Import/TextureImportEntry.cs @@ -286,6 +286,7 @@ namespace FlaxEditor.Content.Import /// /// Texture group for streaming (negative if unused). See Streaming Settings. /// + [CustomEditor(typeof(CustomEditors.Dedicated.TextureGroupEditor))] [EditorOrder(300), Tooltip("Texture group for streaming (negative if unused). See Streaming Settings.")] public int TextureGroup = -1; diff --git a/Source/Editor/CustomEditors/Dedicated/TextureGroupEditor.cs b/Source/Editor/CustomEditors/Dedicated/TextureGroupEditor.cs new file mode 100644 index 000000000..1aff3583b --- /dev/null +++ b/Source/Editor/CustomEditors/Dedicated/TextureGroupEditor.cs @@ -0,0 +1,51 @@ +// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. + +using FlaxEditor.Content.Settings; +using FlaxEditor.CustomEditors.Elements; +using FlaxEditor.GUI; +using FlaxEngine; + +namespace FlaxEditor.CustomEditors.Dedicated +{ + /// + /// Custom editor for index. + /// + internal class TextureGroupEditor : CustomEditor + { + private ComboBoxElement _element; + + /// + public override DisplayStyle Style => DisplayStyle.Inline; + + /// + public override void Initialize(LayoutElementsContainer layout) + { + _element = layout.ComboBox(); + _element.ComboBox.SelectedIndexChanged += OnSelectedIndexChanged; + _element.ComboBox.AddItem("None"); + var groups = GameSettings.Load(); + if (groups?.TextureGroups != null) + { + for (int i = 0; i < groups.TextureGroups.Length; i++) + { + _element.ComboBox.AddItem(groups.TextureGroups[i].Name); + } + } + } + + private void OnSelectedIndexChanged(ComboBox comboBox) + { + var value = comboBox.HasSelection ? comboBox.SelectedIndex - 1 : -1; + SetValue(value); + } + + /// + public override void Refresh() + { + base.Refresh(); + + var value = (int)Values[0]; + _element.ComboBox.SelectedIndex = value + 1; + } + } +} From 3b31fd7c712e1e460cf67aadd66427e10180926d Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 18 Jun 2021 10:49:04 +0200 Subject: [PATCH 190/198] Add support for editing texture group in editor (without reimporting) --- Source/Editor/Windows/Assets/TextureWindow.cs | 101 ++++++++++++------ Source/Engine/Audio/AudioClip.cpp | 4 +- Source/Engine/Content/Asset.cpp | 14 +++ Source/Engine/Content/Asset.h | 14 +-- Source/Engine/Content/Assets/Model.cpp | 4 +- Source/Engine/Content/Assets/SkinnedModel.cpp | 4 +- Source/Engine/Content/Assets/Texture.cpp | 10 ++ Source/Engine/Content/Assets/Texture.h | 11 +- .../Graphics/Textures/StreamingTexture.cpp | 2 +- .../Graphics/Textures/StreamingTexture.h | 2 +- .../Engine/Graphics/Textures/TextureBase.cpp | 27 +++-- Source/Engine/Graphics/Textures/TextureBase.h | 15 ++- .../Engine/Streaming/StreamableResource.cpp | 6 +- Source/Engine/Streaming/StreamableResource.h | 5 +- Source/Engine/Streaming/Streaming.cpp | 1 - Source/Engine/Streaming/StreamingGroup.h | 12 --- 16 files changed, 141 insertions(+), 91 deletions(-) diff --git a/Source/Editor/Windows/Assets/TextureWindow.cs b/Source/Editor/Windows/Assets/TextureWindow.cs index 481c48f47..0ae8abe6d 100644 --- a/Source/Editor/Windows/Assets/TextureWindow.cs +++ b/Source/Editor/Windows/Assets/TextureWindow.cs @@ -4,7 +4,10 @@ using System.Xml; using FlaxEditor.Content; using FlaxEditor.Content.Import; using FlaxEditor.CustomEditors; +using FlaxEditor.CustomEditors.Dedicated; using FlaxEditor.CustomEditors.Editors; +using FlaxEditor.GUI; +using FlaxEditor.Scripting; using FlaxEditor.Viewport.Previews; using FlaxEngine; using FlaxEngine.GUI; @@ -18,47 +21,57 @@ namespace FlaxEditor.Windows.Assets /// public sealed class TextureWindow : AssetEditorWindowBase { + private sealed class ProxyEditor : GenericEditor + { + public override void Initialize(LayoutElementsContainer layout) + { + var window = ((PropertiesProxy)Values[0])._window; + var texture = window?.Asset; + if (texture == null || !texture.IsLoaded) + { + layout.Label("Loading...", TextAlignment.Center); + return; + } + + // Texture info + var general = layout.Group("General"); + general.Label("Format: " + texture.Format); + general.Label(string.Format("Size: {0}x{1}", texture.Width, texture.Height)); + general.Label("Mip levels: " + texture.MipLevels); + general.Label("Memory usage: " + Utilities.Utils.FormatBytesCount(texture.TotalMemoryUsage)); + + // Texture properties + var properties = layout.Group("Properties"); + var textureGroup = new CustomValueContainer(new ScriptType(typeof(int)), texture.TextureGroup, + (instance, index) => texture.TextureGroup, + (instance, index, value) => + { + texture.TextureGroup = (int)value; + window.MarkAsEdited(); + }); + properties.Property("Texture Group", textureGroup, new TextureGroupEditor(), "The texture group used by this texture."); + + // Import settings + base.Initialize(layout); + + // Reimport + layout.Space(10); + var reimportButton = layout.Button("Reimport"); + reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport(); + } + } + /// /// The texture properties proxy object. /// [CustomEditor(typeof(ProxyEditor))] private sealed class PropertiesProxy { - private TextureWindow _window; + internal TextureWindow _window; [EditorOrder(1000), EditorDisplay("Import Settings", EditorDisplayAttribute.InlineStyle)] public TextureImportSettings ImportSettings = new TextureImportSettings(); - public sealed class ProxyEditor : GenericEditor - { - public override void Initialize(LayoutElementsContainer layout) - { - var window = ((PropertiesProxy)Values[0])._window; - if (window == null) - { - layout.Label("Loading...", TextAlignment.Center); - return; - } - - // Texture properties - { - var texture = window.Asset; - - var group = layout.Group("General"); - group.Label("Format: " + texture.Format); - group.Label(string.Format("Size: {0}x{1}", texture.Width, texture.Height)); - group.Label("Mip levels: " + texture.MipLevels); - group.Label("Memory usage: " + Utilities.Utils.FormatBytesCount(texture.TotalMemoryUsage)); - } - - base.Initialize(layout); - - layout.Space(10); - var reimportButton = layout.Button("Reimport"); - reimportButton.Button.Clicked += () => ((PropertiesProxy)Values[0]).Reimport(); - } - } - /// /// Gathers parameters from the specified texture. /// @@ -110,7 +123,7 @@ namespace FlaxEditor.Windows.Assets private readonly SplitPanel _split; private readonly TexturePreview _preview; private readonly CustomEditorPresenter _propertiesEditor; - + private readonly ToolStripButton _saveButton; private readonly PropertiesProxy _properties; private bool _isWaitingForLoad; @@ -140,6 +153,7 @@ namespace FlaxEditor.Windows.Assets _propertiesEditor.Select(_properties); // Toolstrip + _saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save"); _toolstrip.AddButton(Editor.Icons.Import64, () => Editor.ContentImporting.Reimport((BinaryAssetItem)Item)).LinkTooltip("Reimport"); _toolstrip.AddSeparator(); _toolstrip.AddButton(Editor.Icons.CenterView64, _preview.CenterView).LinkTooltip("Center view"); @@ -173,6 +187,14 @@ namespace FlaxEditor.Windows.Assets _isWaitingForLoad = true; } + /// + protected override void UpdateToolstrip() + { + _saveButton.Enabled = IsEdited; + + base.UpdateToolstrip(); + } + /// protected override void OnClose() { @@ -182,6 +204,21 @@ namespace FlaxEditor.Windows.Assets base.OnClose(); } + /// + public override void Save() + { + if (!IsEdited) + return; + + if (Asset.Save()) + { + Editor.LogError("Cannot save asset."); + return; + } + + ClearEditedFlag(); + } + /// public override void Update(float deltaTime) { diff --git a/Source/Engine/Audio/AudioClip.cpp b/Source/Engine/Audio/AudioClip.cpp index 3213850d3..7c1945803 100644 --- a/Source/Engine/Audio/AudioClip.cpp +++ b/Source/Engine/Audio/AudioClip.cpp @@ -401,7 +401,7 @@ Asset::LoadResult AudioClip::load() if (AudioHeader.Streamable) { // Do nothing because data streaming starts when any AudioSource requests the data - startStreaming(false); + StartStreaming(false); return LoadResult::Ok; } @@ -450,7 +450,7 @@ Asset::LoadResult AudioClip::load() void AudioClip::unload(bool isReloading) { - stopStreaming(); + StopStreaming(); StreamingQueue.Clear(); if (Buffers.HasItems()) { diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index e8536f58d..5436cb6b9 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -111,6 +111,11 @@ Asset::Asset(const SpawnParams& params, const AssetInfo* info) { } +int32 Asset::GetReferencesCount() const +{ + return (int32)Platform::AtomicRead(const_cast(&_refCount)); +} + String Asset::ToString() const { return String::Format(TEXT("{0}, {1}, {2}"), GetTypeName(), GetID(), GetPath()); @@ -435,6 +440,15 @@ void Asset::startLoading() _loadingTask->Start(); } +void Asset::releaseStorage() +{ +} + +bool Asset::IsInternalType() const +{ + return false; +} + bool Asset::onLoad(LoadAssetTask* task) { // It may fail when task is cancelled and new one is created later (don't crash but just end with an error) diff --git a/Source/Engine/Content/Asset.h b/Source/Engine/Content/Asset.h index a831ee773..3d4ca2789 100644 --- a/Source/Engine/Content/Asset.h +++ b/Source/Engine/Content/Asset.h @@ -82,10 +82,7 @@ public: /// /// Gets asset's reference count. Asset will be automatically unloaded when this reaches zero. /// - API_PROPERTY() int32 GetReferencesCount() const - { - return (int32)Platform::AtomicRead(const_cast(&_refCount)); - } + API_PROPERTY() int32 GetReferencesCount() const; /// /// Adds reference to that asset. @@ -213,9 +210,7 @@ protected: /// /// Releases the storage file/container handle to prevent issues when renaming or moving the asset. /// - virtual void releaseStorage() - { - } + virtual void releaseStorage(); /// /// Loads asset @@ -231,10 +226,7 @@ protected: protected: - virtual bool IsInternalType() const - { - return false; - } + virtual bool IsInternalType() const; bool onLoad(LoadAssetTask* task); void onLoaded(); diff --git a/Source/Engine/Content/Assets/Model.cpp b/Source/Engine/Content/Assets/Model.cpp index dd9c484e1..d337c62a2 100644 --- a/Source/Engine/Content/Assets/Model.cpp +++ b/Source/Engine/Content/Assets/Model.cpp @@ -570,7 +570,7 @@ bool Model::Init(const Span& meshesCountPerLod) } // Dispose previous data and disable streaming (will start data uploading tasks manually) - stopStreaming(); + StopStreaming(); // Setup MaterialSlots.Resize(1); @@ -827,7 +827,7 @@ Asset::LoadResult Model::load() #endif // Request resource streaming - startStreaming(true); + StartStreaming(true); return LoadResult::Ok; } diff --git a/Source/Engine/Content/Assets/SkinnedModel.cpp b/Source/Engine/Content/Assets/SkinnedModel.cpp index b98ba2e57..3860aeda8 100644 --- a/Source/Engine/Content/Assets/SkinnedModel.cpp +++ b/Source/Engine/Content/Assets/SkinnedModel.cpp @@ -670,7 +670,7 @@ bool SkinnedModel::Init(const Span& meshesCountPerLod) } // Dispose previous data and disable streaming (will start data uploading tasks manually) - stopStreaming(); + StopStreaming(); // Setup MaterialSlots.Resize(1); @@ -973,7 +973,7 @@ Asset::LoadResult SkinnedModel::load() } // Request resource streaming - startStreaming(true); + StartStreaming(true); return LoadResult::Ok; } diff --git a/Source/Engine/Content/Assets/Texture.cpp b/Source/Engine/Content/Assets/Texture.cpp index 23dbff0e2..08100d4b5 100644 --- a/Source/Engine/Content/Assets/Texture.cpp +++ b/Source/Engine/Content/Assets/Texture.cpp @@ -17,6 +17,16 @@ Texture::Texture(const SpawnParams& params, const AssetInfo* info) { } +TextureFormatType Texture::GetFormatType() const +{ + return _texture.GetFormatType(); +} + +bool Texture::IsNormalMap() const +{ + return _texture.GetFormatType() == TextureFormatType::NormalMap; +} + #if USE_EDITOR bool Texture::Save(const StringView& path, const InitData* customData) diff --git a/Source/Engine/Content/Assets/Texture.h b/Source/Engine/Content/Assets/Texture.h index afefc59d2..2b0a6c127 100644 --- a/Source/Engine/Content/Assets/Texture.h +++ b/Source/Engine/Content/Assets/Texture.h @@ -10,23 +10,16 @@ API_CLASS(NoSpawn) class FLAXENGINE_API Texture : public TextureBase { DECLARE_BINARY_ASSET_HEADER(Texture, TexturesSerializedVersion); -public: /// /// Gets the texture format type. /// - FORCE_INLINE TextureFormatType GetFormatType() const - { - return _texture.GetFormatType(); - } + TextureFormatType GetFormatType() const; /// /// Returns true if texture is a normal map. /// - API_PROPERTY() FORCE_INLINE bool IsNormalMap() const - { - return GetFormatType() == TextureFormatType::NormalMap; - } + API_PROPERTY() bool IsNormalMap() const; public: diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp index f4bd6c082..54063f536 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp +++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp @@ -117,7 +117,7 @@ bool StreamingTexture::Create(const TextureHeader& header) #else bool isDynamic = false; #endif - startStreaming(isDynamic); + StartStreaming(isDynamic); return false; } diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.h b/Source/Engine/Graphics/Textures/StreamingTexture.h index 3aa8fee28..2317c52db 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.h +++ b/Source/Engine/Graphics/Textures/StreamingTexture.h @@ -11,6 +11,7 @@ /// class FLAXENGINE_API StreamingTexture : public Object, public StreamableResource { + friend class TextureBase; friend class StreamTextureMipTask; friend class StreamTextureResizeTask; protected: @@ -60,7 +61,6 @@ public: /// /// Gets total texture width (in texels) /// - /// Texture width FORCE_INLINE int32 TotalWidth() const { return _header.Width; diff --git a/Source/Engine/Graphics/Textures/TextureBase.cpp b/Source/Engine/Graphics/Textures/TextureBase.cpp index 59acc488e..d9aa392cf 100644 --- a/Source/Engine/Graphics/Textures/TextureBase.cpp +++ b/Source/Engine/Graphics/Textures/TextureBase.cpp @@ -97,6 +97,20 @@ uint64 TextureBase::GetTotalMemoryUsage() const return _texture.GetTotalMemoryUsage(); } +int32 TextureBase::GetTextureGroup() const +{ + return _texture._header.TextureGroup; +} + +void TextureBase::SetTextureGroup(int32 textureGroup) +{ + if (_texture._header.TextureGroup != textureGroup) + { + _texture._header.TextureGroup = textureGroup; + _texture.RequestStreamingUpdate(); + } +} + BytesContainer TextureBase::GetMipData(int32 mipIndex, int32& rowPitch, int32& slicePitch) { BytesContainer result; @@ -114,7 +128,6 @@ BytesContainer TextureBase::GetMipData(int32 mipIndex, int32& rowPitch, int32& s } else { - // Wait for the asset header to be loaded if (WaitForLoaded()) return result; @@ -127,7 +140,7 @@ BytesContainer TextureBase::GetMipData(int32 mipIndex, int32& rowPitch, int32& s slicePitch = slicePitch1; // Ensure to have chunk loaded - if (LoadChunk(calculateChunkIndex(mipIndex))) + if (LoadChunk(CalculateChunkIndex(mipIndex))) return result; } @@ -261,7 +274,7 @@ bool TextureBase::Init(void* ptr) return Init(initData); } -int32 TextureBase::calculateChunkIndex(int32 mipIndex) const +int32 TextureBase::CalculateChunkIndex(int32 mipIndex) const { // Mips are in 0-13 chunks return mipIndex; @@ -287,7 +300,7 @@ Task* TextureBase::RequestMipDataAsync(int32 mipIndex) if (_customData) return nullptr; - auto chunkIndex = calculateChunkIndex(mipIndex); + auto chunkIndex = CalculateChunkIndex(mipIndex); return (Task*)_parent->RequestChunkDataAsync(chunkIndex); } @@ -304,7 +317,7 @@ void TextureBase::GetMipData(int32 mipIndex, BytesContainer& data) const return; } - auto chunkIndex = calculateChunkIndex(mipIndex); + auto chunkIndex = CalculateChunkIndex(mipIndex); _parent->GetChunkData(chunkIndex, data); } @@ -316,7 +329,7 @@ void TextureBase::GetMipDataWithLoading(int32 mipIndex, BytesContainer& data) co return; } - const auto chunkIndex = calculateChunkIndex(mipIndex); + const auto chunkIndex = CalculateChunkIndex(mipIndex); _parent->LoadChunk(chunkIndex); _parent->GetChunkData(chunkIndex, data); } @@ -399,8 +412,6 @@ bool TextureBase::InitData::GenerateMip(int32 mipIndex, bool linear) // Allocate data const int32 dstMipWidth = Math::Max(1, Width >> mipIndex); const int32 dstMipHeight = Math::Max(1, Height >> mipIndex); - const int32 srcMipWidth = Math::Max(1, Width >> (mipIndex - 1)); - const int32 srcMipHeight = Math::Max(1, Height >> (mipIndex - 1)); const int32 pixelStride = PixelFormatExtensions::SizeInBytes(Format); dstMip.RowPitch = dstMipWidth * pixelStride; dstMip.SlicePitch = dstMip.RowPitch * dstMipHeight; diff --git a/Source/Engine/Graphics/Textures/TextureBase.h b/Source/Engine/Graphics/Textures/TextureBase.h index bdc551397..2bb0042df 100644 --- a/Source/Engine/Graphics/Textures/TextureBase.h +++ b/Source/Engine/Graphics/Textures/TextureBase.h @@ -50,9 +50,6 @@ protected: StreamingTexture _texture; InitData* _customData; - -private: - BinaryAsset* _parent; public: @@ -126,6 +123,16 @@ public: /// Gets the total memory usage that texture may have in use (if loaded to the maximum quality). Exact value may differ due to memory alignment and resource allocation policy. /// API_PROPERTY() uint64 GetTotalMemoryUsage() const; + + /// + /// Gets the index of the texture group used by this texture. + /// + API_PROPERTY() int32 GetTextureGroup() const; + + /// + /// Sets the index of the texture group used by this texture. + /// + API_PROPERTY() void SetTextureGroup(int32 textureGroup); public: @@ -155,7 +162,7 @@ public: protected: - virtual int32 calculateChunkIndex(int32 mipIndex) const; + virtual int32 CalculateChunkIndex(int32 mipIndex) const; private: diff --git a/Source/Engine/Streaming/StreamableResource.cpp b/Source/Engine/Streaming/StreamableResource.cpp index af758e786..685faff03 100644 --- a/Source/Engine/Streaming/StreamableResource.cpp +++ b/Source/Engine/Streaming/StreamableResource.cpp @@ -14,10 +14,10 @@ StreamableResource::StreamableResource(StreamingGroup* group) StreamableResource::~StreamableResource() { - stopStreaming(); + StopStreaming(); } -void StreamableResource::startStreaming(bool isDynamic) +void StreamableResource::StartStreaming(bool isDynamic) { _isDynamic = isDynamic; @@ -28,7 +28,7 @@ void StreamableResource::startStreaming(bool isDynamic) } } -void StreamableResource::stopStreaming() +void StreamableResource::StopStreaming() { if (_isStreaming == true) { diff --git a/Source/Engine/Streaming/StreamableResource.h b/Source/Engine/Streaming/StreamableResource.h index 8b8854bfe..a27833149 100644 --- a/Source/Engine/Streaming/StreamableResource.h +++ b/Source/Engine/Streaming/StreamableResource.h @@ -105,7 +105,6 @@ public: struct StreamingCache { - //float MinDstSinceLastUpdate = MAX_float; DateTime LastUpdate = 0; int32 TargetResidency = 0; DateTime TargetResidencyChange = 0; @@ -124,6 +123,6 @@ public: protected: - void startStreaming(bool isDynamic); - void stopStreaming(); + void StartStreaming(bool isDynamic); + void StopStreaming(); }; diff --git a/Source/Engine/Streaming/Streaming.cpp b/Source/Engine/Streaming/Streaming.cpp index 1ce53cf75..a2e010105 100644 --- a/Source/Engine/Streaming/Streaming.cpp +++ b/Source/Engine/Streaming/Streaming.cpp @@ -59,7 +59,6 @@ void UpdateResource(StreamableResource* resource, DateTime now) if (resource->IsDynamic()) { targetQuality = handler->CalculateTargetQuality(resource, now); - // TODO: here we should apply resources group master scale (based on game settings quality level and memory level) targetQuality = Math::Saturate(targetQuality); } diff --git a/Source/Engine/Streaming/StreamingGroup.h b/Source/Engine/Streaming/StreamingGroup.h index 8f7cec045..8e3a78555 100644 --- a/Source/Engine/Streaming/StreamingGroup.h +++ b/Source/Engine/Streaming/StreamingGroup.h @@ -14,9 +14,6 @@ class FLAXENGINE_API StreamingGroup { public: - /// - /// Declares the Group type - /// DECLARE_ENUM_4(Type, Custom, Textures, Models, Audio); protected: @@ -38,7 +35,6 @@ public: /// /// Gets the group type. /// - /// Type FORCE_INLINE Type GetType() const { return _type; @@ -47,7 +43,6 @@ public: /// /// Gets the group type name. /// - /// Typename FORCE_INLINE const Char* GetTypename() const { return ToString(_type); @@ -56,7 +51,6 @@ public: /// /// Gets the group streaming handler used by this group. /// - /// Handler FORCE_INLINE IStreamingHandler* GetHandler() const { return _handler; @@ -88,7 +82,6 @@ public: /// /// Gets textures group. /// - /// Group FORCE_INLINE StreamingGroup* Textures() const { return _textures; @@ -97,7 +90,6 @@ public: /// /// Gets models group. /// - /// Group FORCE_INLINE StreamingGroup* Models() const { return _models; @@ -106,7 +98,6 @@ public: /// /// Gets skinned models group. /// - /// Group FORCE_INLINE StreamingGroup* SkinnedModels() const { return _skinnedModels; @@ -115,7 +106,6 @@ public: /// /// Gets audio group. /// - /// Group FORCE_INLINE StreamingGroup* Audio() const { return _audio; @@ -126,7 +116,6 @@ public: /// /// Gets all the groups. /// - /// Groups. FORCE_INLINE const Array& Groups() const { return _groups; @@ -135,7 +124,6 @@ public: /// /// Gets all the handlers. /// - /// Groups. FORCE_INLINE const Array& Handlers() const { return _handlers; From a7f018a4da1ef10d9fe13c3391fbe9fc38553fc4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 18 Jun 2021 11:07:21 +0200 Subject: [PATCH 191/198] Fix Linux game build --- Source/Engine/Engine/Linux/LinuxGame.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/Engine/Linux/LinuxGame.cpp b/Source/Engine/Engine/Linux/LinuxGame.cpp index 3fe7c6a92..48b4044d9 100644 --- a/Source/Engine/Engine/Linux/LinuxGame.cpp +++ b/Source/Engine/Engine/Linux/LinuxGame.cpp @@ -5,6 +5,7 @@ #include "LinuxGame.h" #include "Engine/Platform/Window.h" #include "Engine/Platform/FileSystem.h" +#include "Engine/Core/Log.h" #include "Engine/Core/Config/PlatformSettings.h" #include "Engine/Engine/CommandLine.h" #include "Engine/Engine/Globals.h" From 47da35587896aaa20c9932784fdfceda5398388a Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 18 Jun 2021 11:57:08 +0200 Subject: [PATCH 192/198] Add api to force refresh streaming --- .../Engine/Streaming/StreamableResource.cpp | 39 ------- .../Streaming/StreamableResourcesCollection.h | 103 ------------------ Source/Engine/Streaming/Streaming.cpp | 57 +++++++++- Source/Engine/Streaming/Streaming.h | 12 +- 4 files changed, 58 insertions(+), 153 deletions(-) delete mode 100644 Source/Engine/Streaming/StreamableResource.cpp delete mode 100644 Source/Engine/Streaming/StreamableResourcesCollection.h diff --git a/Source/Engine/Streaming/StreamableResource.cpp b/Source/Engine/Streaming/StreamableResource.cpp deleted file mode 100644 index 685faff03..000000000 --- a/Source/Engine/Streaming/StreamableResource.cpp +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#include "StreamableResource.h" -#include "Streaming.h" - -StreamableResource::StreamableResource(StreamingGroup* group) - : _group(group) - , _isDynamic(true) - , _isStreaming(false) - , _streamingQuality(1.0f) -{ - ASSERT(_group != nullptr); -} - -StreamableResource::~StreamableResource() -{ - StopStreaming(); -} - -void StreamableResource::StartStreaming(bool isDynamic) -{ - _isDynamic = isDynamic; - - if (_isStreaming == false) - { - Streaming::Resources.Add(this); - _isStreaming = true; - } -} - -void StreamableResource::StopStreaming() -{ - if (_isStreaming == true) - { - Streaming::Resources.Remove(this); - Streaming = StreamingCache(); - _isStreaming = false; - } -} diff --git a/Source/Engine/Streaming/StreamableResourcesCollection.h b/Source/Engine/Streaming/StreamableResourcesCollection.h deleted file mode 100644 index a8231726d..000000000 --- a/Source/Engine/Streaming/StreamableResourcesCollection.h +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. - -#pragma once - -#include "Engine/Core/Delegate.h" -#include "Engine/Core/Collections/Array.h" -#include "Engine/Platform/CriticalSection.h" - -class StreamableResource; - -/// -/// Container for collection of Streamable Resources. -/// -class StreamableResourcesCollection -{ -private: - - CriticalSection _locker; - Array _resources; - -public: - - /// - /// Initializes a new instance of the class. - /// - StreamableResourcesCollection() - : _resources(4096) - { - } - -public: - - /// - /// Event called on new resource added to the collection. - /// - Delegate Added; - - /// - /// Event called on new resource added from the collection. - /// - Delegate Removed; - -public: - - /// - /// Gets the amount of registered resources. - /// - /// The resources count. - int32 ResourcesCount() const - { - _locker.Lock(); - const int32 result = _resources.Count(); - _locker.Unlock(); - return result; - } - - /// - /// Gets the resource at the given index. - /// - /// The index. - /// The resource at the given index. - StreamableResource* operator[](int32 index) const - { - _locker.Lock(); - StreamableResource* result = _resources.At(index); - _locker.Unlock(); - return result; - } - -public: - - /// - /// Adds the resource to the collection. - /// - /// The resource to add. - void Add(StreamableResource* resource) - { - ASSERT(resource); - - _locker.Lock(); - ASSERT(_resources.Contains(resource) == false); - _resources.Add(resource); - _locker.Unlock(); - - Added(resource); - } - - /// - /// Removes resource from the collection. - /// - /// The resource to remove. - void Remove(StreamableResource* resource) - { - ASSERT(resource); - - _locker.Lock(); - ASSERT(_resources.Contains(resource) == true); - _resources.Remove(resource); - _locker.Unlock(); - - Removed(resource); - } -}; diff --git a/Source/Engine/Streaming/Streaming.cpp b/Source/Engine/Streaming/Streaming.cpp index a2e010105..8b879b3dd 100644 --- a/Source/Engine/Streaming/Streaming.cpp +++ b/Source/Engine/Streaming/Streaming.cpp @@ -14,8 +14,9 @@ namespace StreamingManagerImpl { DateTime LastUpdateTime(0); - CriticalSection UpdateLocker; int32 LastUpdateResourcesIndex = 0; + CriticalSection ResourcesLock; + Array Resources; } using namespace StreamingManagerImpl; @@ -33,7 +34,6 @@ public: StreamingManagerService StreamingManagerServiceInstance; -StreamableResourcesCollection Streaming::Resources; Array> Streaming::TextureGroups; void StreamingSettings::Apply() @@ -46,6 +46,44 @@ void StreamingSettings::Deserialize(DeserializeStream& stream, ISerializeModifie DESERIALIZE(TextureGroups); } +StreamableResource::StreamableResource(StreamingGroup* group) + : _group(group) + , _isDynamic(true) + , _isStreaming(false) + , _streamingQuality(1.0f) +{ + ASSERT(_group != nullptr); +} + +StreamableResource::~StreamableResource() +{ + StopStreaming(); +} + +void StreamableResource::StartStreaming(bool isDynamic) +{ + _isDynamic = isDynamic; + if (!_isStreaming) + { + _isStreaming = true; + ResourcesLock.Lock(); + Resources.Add(this); + ResourcesLock.Unlock(); + } +} + +void StreamableResource::StopStreaming() +{ + if (_isStreaming) + { + ResourcesLock.Lock(); + Resources.Remove(this); + ResourcesLock.Unlock(); + Streaming = StreamingCache(); + _isStreaming = false; + } +} + void UpdateResource(StreamableResource* resource, DateTime now) { ASSERT(resource && resource->CanBeUpdated()); @@ -143,7 +181,8 @@ void StreamingManagerService::Update() // Check if skip update auto now = DateTime::NowUTC(); auto delta = now - LastUpdateTime; - int32 resourcesCount = Streaming::Resources.ResourcesCount(); + ScopeLock lock(ResourcesLock); + const int32 resourcesCount = Resources.Count(); if (resourcesCount == 0 || delta < ManagerUpdatesInterval || GPUDevice::Instance->GetState() != GPUDevice::DeviceState::Ready) return; LastUpdateTime = now; @@ -151,7 +190,6 @@ void StreamingManagerService::Update() PROFILE_CPU(); // Start update - ScopeLock lock(UpdateLocker); int32 resourcesUpdates = Math::Min(MaxResourcesPerUpdate, resourcesCount); // Update high priority queue and then rest of the resources @@ -165,7 +203,7 @@ void StreamingManagerService::Update() LastUpdateResourcesIndex = 0; // Peek resource - const auto resource = Streaming::Resources[LastUpdateResourcesIndex]; + const auto resource = Resources[LastUpdateResourcesIndex]; // Try to update it if (now - resource->Streaming.LastUpdate >= ResourceUpdatesInterval && resource->CanBeUpdated()) @@ -177,3 +215,12 @@ void StreamingManagerService::Update() // TODO: add StreamingManager stats, update time per frame, updates per frame, etc. } + +void Streaming::RequestStreamingUpdate() +{ + PROFILE_CPU(); + ResourcesLock.Lock(); + for (auto e : Resources) + e->RequestStreamingUpdate(); + ResourcesLock.Unlock(); +} diff --git a/Source/Engine/Streaming/Streaming.h b/Source/Engine/Streaming/Streaming.h index 35766639a..2afb404f6 100644 --- a/Source/Engine/Streaming/Streaming.h +++ b/Source/Engine/Streaming/Streaming.h @@ -2,9 +2,9 @@ #pragma once +#include "Engine/Core/Collections/Array.h" #include "Engine/Scripting/ScriptingType.h" #include "TextureGroup.h" -#include "StreamableResourcesCollection.h" /// /// The content streaming service. @@ -13,13 +13,13 @@ API_CLASS(Static) class FLAXENGINE_API Streaming { DECLARE_SCRIPTING_TYPE_NO_SPAWN(Streaming); - /// - /// List with all resources - /// - static StreamableResourcesCollection Resources; - /// /// Textures streaming configuration (per-group). /// API_FIELD() static Array> TextureGroups; + + /// + /// Requests the streaming update for all the loaded resources. Use it to refresh content streaming after changing configuration. + /// + API_FUNCTION() static void RequestStreamingUpdate(); }; From dc38a552185d260d441dbd4ef33e25382ea7ca1c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 18 Jun 2021 11:57:22 +0200 Subject: [PATCH 193/198] Add option to open all selected assets in content window --- Source/Editor/Windows/ContentWindow.ContextMenu.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Editor/Windows/ContentWindow.ContextMenu.cs b/Source/Editor/Windows/ContentWindow.ContextMenu.cs index 079df66a5..cded93cc0 100644 --- a/Source/Editor/Windows/ContentWindow.ContextMenu.cs +++ b/Source/Editor/Windows/ContentWindow.ContextMenu.cs @@ -65,6 +65,13 @@ namespace FlaxEditor.Windows b = cm.AddButton("Open", () => Open(item)); b.Enabled = proxy != null || isFolder; + if (_view.SelectedCount > 1) + b = cm.AddButton("Open (all selected)", () => + { + foreach (var e in _view.Selection) + Open(e); + }); + cm.AddButton("Show in explorer", () => FileSystem.ShowFileExplorer(System.IO.Path.GetDirectoryName(item.Path))); if (item.HasDefaultThumbnail == false) From 41f0554484b6c5e26e020a8a739ed198bbcad74e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 18 Jun 2021 11:57:51 +0200 Subject: [PATCH 194/198] Add texture groups preview to Graphics Quality window in editor --- Source/Editor/Windows/GraphicsQualityWindow.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Windows/GraphicsQualityWindow.cs b/Source/Editor/Windows/GraphicsQualityWindow.cs index 33e5a3ff4..3880a4391 100644 --- a/Source/Editor/Windows/GraphicsQualityWindow.cs +++ b/Source/Editor/Windows/GraphicsQualityWindow.cs @@ -1,5 +1,6 @@ // Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. +using System.Collections.Generic; using System.ComponentModel; using FlaxEditor.CustomEditors; using FlaxEngine; @@ -9,7 +10,7 @@ using FlaxEngine.Json; namespace FlaxEditor.Windows { /// - /// Window used to show and edit current graphics rendering settings via . + /// Window used to show and edit current graphics rendering settings via and . /// /// public class GraphicsQualityWindow : EditorWindow @@ -86,6 +87,18 @@ namespace FlaxEditor.Windows get => Graphics.AllowCSMBlending; set => Graphics.AllowCSMBlending = value; } + + [NoSerialize] + [EditorOrder(2000), EditorDisplay("Textures", EditorDisplayAttribute.InlineStyle), Tooltip("Textures streaming configuration.")] + public TextureGroup[] TextureGroups + { + get => Streaming.TextureGroups; + set + { + Streaming.TextureGroups = value; + Streaming.RequestStreamingUpdate(); + } + } } /// From f582ca5051ec0fbedd52f4efe46842f970979d8c Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 18 Jun 2021 14:41:44 +0200 Subject: [PATCH 195/198] Add dynamic textures streaming based on visibility --- Source/Engine/Graphics/GPUContext.cpp | 1 + Source/Engine/Graphics/GPUContext.h | 8 ++--- Source/Engine/Graphics/GPUDevice.cpp | 2 ++ Source/Engine/Graphics/GPUResource.h | 8 +++++ Source/Engine/Graphics/Textures/GPUTexture.h | 3 +- .../DirectX/DX11/GPUContextDX11.cpp | 8 ++--- .../DirectX/DX12/GPUContextDX12.cpp | 6 ++-- .../Vulkan/GPUContextVulkan.cpp | 8 ++--- Source/Engine/Streaming/IStreamingHandler.h | 5 ++-- Source/Engine/Streaming/Streaming.cpp | 9 +++--- Source/Engine/Streaming/StreamingHandlers.cpp | 29 ++++++++++++------- Source/Engine/Streaming/StreamingHandlers.h | 8 ++--- Source/Engine/Streaming/TextureGroup.h | 14 ++++++++- 13 files changed, 70 insertions(+), 39 deletions(-) diff --git a/Source/Engine/Graphics/GPUContext.cpp b/Source/Engine/Graphics/GPUContext.cpp index cff2c4766..a1955fb4c 100644 --- a/Source/Engine/Graphics/GPUContext.cpp +++ b/Source/Engine/Graphics/GPUContext.cpp @@ -13,6 +13,7 @@ GPUContext::GPUContext(GPUDevice* device) void GPUContext::FrameBegin() { + _lastRenderTime = Platform::GetTimeSeconds(); } void GPUContext::FrameEnd() diff --git a/Source/Engine/Graphics/GPUContext.h b/Source/Engine/Graphics/GPUContext.h index ed2417c96..5e1219fa2 100644 --- a/Source/Engine/Graphics/GPUContext.h +++ b/Source/Engine/Graphics/GPUContext.h @@ -121,18 +121,14 @@ private: protected: - /// - /// Initializes a new instance of the class. - /// - /// The graphics device. + double _lastRenderTime = -1; GPUContext(GPUDevice* device); public: /// - /// Gets the graphics device handle + /// Gets the graphics device. /// - /// Graphics device FORCE_INLINE GPUDevice* GetDevice() const { return _device; diff --git a/Source/Engine/Graphics/GPUDevice.cpp b/Source/Engine/Graphics/GPUDevice.cpp index 5ea4914ba..e3f6542b5 100644 --- a/Source/Engine/Graphics/GPUDevice.cpp +++ b/Source/Engine/Graphics/GPUDevice.cpp @@ -191,6 +191,8 @@ void GPUResource::OnDeleteObject() PersistentScriptingObject::OnDeleteObject(); } +double GPUResourceView::DummyLastRenderTime = -1; + struct GPUDevice::PrivateData { AssetReference QuadShader; diff --git a/Source/Engine/Graphics/GPUResource.h b/Source/Engine/Graphics/GPUResource.h index 92fa9c769..4f7d09c63 100644 --- a/Source/Engine/Graphics/GPUResource.h +++ b/Source/Engine/Graphics/GPUResource.h @@ -65,6 +65,9 @@ public: public: + // Points to the cache used by the resource for the resource visibility/usage detection. Written during rendering when resource is used. + double LastRenderTime = -1; + /// /// Action fired when resource GPU state gets released. All objects and async tasks using this resource should release references to this object nor use its data. /// @@ -215,14 +218,19 @@ API_CLASS(Abstract, NoSpawn, Attributes="HideInEditor") class FLAXENGINE_API GPU { DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUResourceView); protected: + static double DummyLastRenderTime; explicit GPUResourceView(const SpawnParams& params) : PersistentScriptingObject(params) + , LastRenderTime(&DummyLastRenderTime) { } public: + // Points to the cache used by the resource for the resource visibility/usage detection. Written during rendering when resource view is used. + double* LastRenderTime; + /// /// Gets the native pointer to the underlying view. It's a platform-specific handle. /// diff --git a/Source/Engine/Graphics/Textures/GPUTexture.h b/Source/Engine/Graphics/Textures/GPUTexture.h index 8b5385f9e..82171182b 100644 --- a/Source/Engine/Graphics/Textures/GPUTexture.h +++ b/Source/Engine/Graphics/Textures/GPUTexture.h @@ -31,11 +31,12 @@ protected: { } - void Init(GPUResource* parent, PixelFormat format, MSAALevel msaa) + FORCE_INLINE void Init(GPUResource* parent, PixelFormat format, MSAALevel msaa) { _parent = parent; _format = format; _msaa = msaa; + LastRenderTime = &parent->LastRenderTime; } public: diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp index 9c9af3145..b283c7a5f 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp @@ -327,26 +327,26 @@ void GPUContextDX11::BindCB(int32 slot, GPUConstantBuffer* cb) void GPUContextDX11::BindSR(int32 slot, GPUResourceView* view) { ASSERT(slot >= 0 && slot < GPU_MAX_SR_BINDED); - auto handle = view ? ((IShaderResourceDX11*)view->GetNativePtr())->SRV() : nullptr; - if (_srHandles[slot] != handle) { _srDirtyFlag = true; _srHandles[slot] = handle; + if (view) + *view->LastRenderTime = _lastRenderTime; } } void GPUContextDX11::BindUA(int32 slot, GPUResourceView* view) { ASSERT(slot >= 0 && slot < GPU_MAX_UA_BINDED); - auto handle = view ? ((IShaderResourceDX11*)view->GetNativePtr())->UAV() : nullptr; - if (_uaHandles[slot] != handle) { _uaDirtyFlag = true; _uaHandles[slot] = handle; + if (view) + *view->LastRenderTime = _lastRenderTime; } } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp index 0d3309219..ffa0a5550 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/GPUContextDX12.cpp @@ -809,9 +809,7 @@ void GPUContextDX12::ResetCB() void GPUContextDX12::BindCB(int32 slot, GPUConstantBuffer* cb) { ASSERT(slot >= 0 && slot < GPU_MAX_CB_BINDED); - auto cbDX12 = static_cast(cb); - if (_cbHandles[slot] != cbDX12) { _cbDirtyFlag = true; @@ -828,6 +826,8 @@ void GPUContextDX12::BindSR(int32 slot, GPUResourceView* view) _srMaskDirtyGraphics |= 1 << slot; _srMaskDirtyCompute |= 1 << slot; _srHandles[slot] = handle; + if (view) + *view->LastRenderTime = _lastRenderTime; } } @@ -835,6 +835,8 @@ void GPUContextDX12::BindUA(int32 slot, GPUResourceView* view) { ASSERT(slot >= 0 && slot < GPU_MAX_UA_BINDED); _uaHandles[slot] = view ? (IShaderResourceDX12*)view->GetNativePtr() : nullptr; + if (view) + *view->LastRenderTime = _lastRenderTime; } void GPUContextDX12::BindVB(const Span& vertexBuffers, const uint32* vertexBuffersOffsets) diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp index 63b5503a8..6e73ec157 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUContextVulkan.cpp @@ -948,26 +948,26 @@ void GPUContextVulkan::BindCB(int32 slot, GPUConstantBuffer* cb) void GPUContextVulkan::BindSR(int32 slot, GPUResourceView* view) { ASSERT(slot >= 0 && slot < GPU_MAX_SR_BINDED); - const auto handle = view ? (DescriptorOwnerResourceVulkan*)view->GetNativePtr() : nullptr; - if (_srHandles[slot] != handle) { _srDirtyFlag = true; _srHandles[slot] = handle; + if (view) + *view->LastRenderTime = _lastRenderTime; } } void GPUContextVulkan::BindUA(int32 slot, GPUResourceView* view) { ASSERT(slot >= 0 && slot < GPU_MAX_UA_BINDED); - const auto handle = view ? (DescriptorOwnerResourceVulkan*)view->GetNativePtr() : nullptr; - if (_uaHandles[slot] != handle) { _uaDirtyFlag = true; _uaHandles[slot] = handle; + if (view) + *view->LastRenderTime = _lastRenderTime; } } diff --git a/Source/Engine/Streaming/IStreamingHandler.h b/Source/Engine/Streaming/IStreamingHandler.h index 3e9674141..299c530b4 100644 --- a/Source/Engine/Streaming/IStreamingHandler.h +++ b/Source/Engine/Streaming/IStreamingHandler.h @@ -22,9 +22,10 @@ public: /// Calculates target quality level (0-1) for the given resource. /// /// The resource. - /// The current time. + /// The current time and date. + /// The current platform time (seconds). /// Target quality (0-1). - virtual float CalculateTargetQuality(StreamableResource* resource, DateTime now) = 0; + virtual float CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) = 0; /// /// Calculates the residency level for a given resource and quality level. diff --git a/Source/Engine/Streaming/Streaming.cpp b/Source/Engine/Streaming/Streaming.cpp index 8b879b3dd..395a890b9 100644 --- a/Source/Engine/Streaming/Streaming.cpp +++ b/Source/Engine/Streaming/Streaming.cpp @@ -84,7 +84,7 @@ void StreamableResource::StopStreaming() } } -void UpdateResource(StreamableResource* resource, DateTime now) +void UpdateResource(StreamableResource* resource, DateTime now, double currentTime) { ASSERT(resource && resource->CanBeUpdated()); @@ -96,7 +96,7 @@ void UpdateResource(StreamableResource* resource, DateTime now) float targetQuality = 1.0f; if (resource->IsDynamic()) { - targetQuality = handler->CalculateTargetQuality(resource, now); + targetQuality = handler->CalculateTargetQuality(resource, now, currentTime); targetQuality = Math::Saturate(targetQuality); } @@ -175,7 +175,7 @@ void StreamingManagerService::Update() // Configuration // TODO: use game settings static TimeSpan ManagerUpdatesInterval = TimeSpan::FromMilliseconds(30); - static TimeSpan ResourceUpdatesInterval = TimeSpan::FromMilliseconds(200); + static TimeSpan ResourceUpdatesInterval = TimeSpan::FromMilliseconds(100); static int32 MaxResourcesPerUpdate = 50; // Check if skip update @@ -191,6 +191,7 @@ void StreamingManagerService::Update() // Start update int32 resourcesUpdates = Math::Min(MaxResourcesPerUpdate, resourcesCount); + double currentTime = Platform::GetTimeSeconds(); // Update high priority queue and then rest of the resources // Note: resources in the update queue are updated always, while others only between specified intervals @@ -208,7 +209,7 @@ void StreamingManagerService::Update() // Try to update it if (now - resource->Streaming.LastUpdate >= ResourceUpdatesInterval && resource->CanBeUpdated()) { - UpdateResource(resource, now); + UpdateResource(resource, now, currentTime); resourcesUpdates--; } } diff --git a/Source/Engine/Streaming/StreamingHandlers.cpp b/Source/Engine/Streaming/StreamingHandlers.cpp index bf43d317a..2fb4cd840 100644 --- a/Source/Engine/Streaming/StreamingHandlers.cpp +++ b/Source/Engine/Streaming/StreamingHandlers.cpp @@ -4,26 +4,33 @@ #include "Streaming.h" #include "Engine/Core/Math/Math.h" #include "Engine/Graphics/Textures/StreamingTexture.h" +#include "Engine/Graphics/Textures/GPUTexture.h" #include "Engine/Content/Assets/Model.h" #include "Engine/Content/Assets/SkinnedModel.h" #include "Engine/Audio/AudioClip.h" #include "Engine/Audio/Audio.h" #include "Engine/Audio/AudioSource.h" -float TexturesStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now) +float TexturesStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) { ASSERT(resource); auto& texture = *(StreamingTexture*)resource; const TextureHeader& header = *texture.GetHeader(); - if (header.TextureGroup < 0 || header.TextureGroup >= Streaming::TextureGroups.Count()) + float result = 1.0f; + if (header.TextureGroup >= 0 && header.TextureGroup < Streaming::TextureGroups.Count()) { - // Full texture load by default - return 1.0f; - } + // Quality based on texture group settings + const TextureGroup& group = Streaming::TextureGroups[header.TextureGroup]; + result = group.Quality; - // Quality based on texture group settings - const TextureGroup& group = Streaming::TextureGroups[header.TextureGroup]; - return group.QualityScale; + // Drop quality if invisible + const double lastRenderTime = texture.GetTexture()->LastRenderTime; + if (lastRenderTime < 0 || group.TimeToInvisible <= (float)(currentTime - lastRenderTime)) + { + result *= group.QualityIfInvisible; + } + } + return result; } int32 TexturesStreamingHandler::CalculateResidency(StreamableResource* resource, float quality) @@ -86,7 +93,7 @@ int32 TexturesStreamingHandler::CalculateRequestedResidency(StreamableResource* return residency; } -float ModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now) +float ModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) { // TODO: calculate a proper quality levels for models based on render time and streaming enable/disable options return 1.0f; @@ -127,7 +134,7 @@ int32 ModelsStreamingHandler::CalculateRequestedResidency(StreamableResource* re return residency; } -float SkinnedModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now) +float SkinnedModelsStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) { // TODO: calculate a proper quality levels for models based on render time and streaming enable/disable options return 1.0f; @@ -168,7 +175,7 @@ int32 SkinnedModelsStreamingHandler::CalculateRequestedResidency(StreamableResou return residency; } -float AudioStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now) +float AudioStreamingHandler::CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) { // Audio clips don't use quality but only residency return 1.0f; diff --git a/Source/Engine/Streaming/StreamingHandlers.h b/Source/Engine/Streaming/StreamingHandlers.h index b6080d616..0c20dcfda 100644 --- a/Source/Engine/Streaming/StreamingHandlers.h +++ b/Source/Engine/Streaming/StreamingHandlers.h @@ -11,7 +11,7 @@ class FLAXENGINE_API TexturesStreamingHandler : public IStreamingHandler { public: // [IStreamingHandler] - float CalculateTargetQuality(StreamableResource* resource, DateTime now) override; + float CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) override; int32 CalculateResidency(StreamableResource* resource, float quality) override; int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; }; @@ -23,7 +23,7 @@ class FLAXENGINE_API ModelsStreamingHandler : public IStreamingHandler { public: // [IStreamingHandler] - float CalculateTargetQuality(StreamableResource* resource, DateTime now) override; + float CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) override; int32 CalculateResidency(StreamableResource* resource, float quality) override; int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; }; @@ -35,7 +35,7 @@ class FLAXENGINE_API SkinnedModelsStreamingHandler : public IStreamingHandler { public: // [IStreamingHandler] - float CalculateTargetQuality(StreamableResource* resource, DateTime now) override; + float CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) override; int32 CalculateResidency(StreamableResource* resource, float quality) override; int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; }; @@ -47,7 +47,7 @@ class FLAXENGINE_API AudioStreamingHandler : public IStreamingHandler { public: // [IStreamingHandler] - float CalculateTargetQuality(StreamableResource* resource, DateTime now) override; + float CalculateTargetQuality(StreamableResource* resource, DateTime now, double currentTime) override; int32 CalculateResidency(StreamableResource* resource, float quality) override; int32 CalculateRequestedResidency(StreamableResource* resource, int32 targetResidency) override; bool RequiresStreaming(StreamableResource* resource, int32 currentResidency, int32 targetResidency) override; diff --git a/Source/Engine/Streaming/TextureGroup.h b/Source/Engine/Streaming/TextureGroup.h index dfd6dba07..87ad7dc4d 100644 --- a/Source/Engine/Streaming/TextureGroup.h +++ b/Source/Engine/Streaming/TextureGroup.h @@ -26,7 +26,19 @@ DECLARE_SCRIPTING_TYPE_MINIMAL(TextureGroup); /// The quality scale factor applied to textures in this group. Can be used to increase or decrease textures resolution. In range 0-1 where 0 means lowest quality, 1 means full quality. /// API_FIELD(Attributes="EditorOrder(20), Limit(0, 1)") - float QualityScale = 1.0f; + float Quality = 1.0f; + + /// + /// The quality scale factor applied when texture is invisible for some time (defined by TimeToInvisible). Used to decrease texture quality when it's not rendered. + /// + API_FIELD(Attributes="EditorOrder(25), Limit(0, 1)") + float QualityIfInvisible = 0.5f; + + /// + /// The time (in seconds) after which texture is considered to be invisible (if it's not rendered by a certain amount of time). + /// + API_FIELD(Attributes="EditorOrder(26), Limit(0)") + float TimeToInvisible = 20.0f; /// /// The minimum amount of loaded mip levels for textures in this group. Defines the amount of the mips that should be always loaded. Higher values decrease streaming usage and keep more mips loaded. From 4681d8da56dd59f083a44ed8d199716123680217 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 18 Jun 2021 14:42:09 +0200 Subject: [PATCH 196/198] Fix texture streaming minimum mips to load for block compressed textures --- Source/Engine/Graphics/Textures/StreamingTexture.cpp | 8 ++++++++ Source/Engine/Graphics/Textures/StreamingTexture.h | 10 ++-------- Source/Engine/Streaming/StreamingHandlers.cpp | 6 +++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.cpp b/Source/Engine/Graphics/Textures/StreamingTexture.cpp index 54063f536..7637e67f0 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.cpp +++ b/Source/Engine/Graphics/Textures/StreamingTexture.cpp @@ -110,6 +110,14 @@ bool StreamingTexture::Create(const TextureHeader& header) // That's one of the main advantages of the current resources streaming system. _header = header; _isBlockCompressed = PixelFormatExtensions::IsCompressed(_header.Format); + if (_isBlockCompressed) + { + // Ensure that streaming doesn't go too low because the hardware expects the texture to be min in size of compressed texture block + int32 lastMip = header.MipLevels - 1; + while (header.Width >> lastMip < 4 && header.Height >> lastMip < 4) + lastMip--; + _minMipCountBlockCompressed = header.MipLevels - lastMip + 1; + } // Request resource streaming #if GPU_ENABLE_TEXTURES_STREAMING diff --git a/Source/Engine/Graphics/Textures/StreamingTexture.h b/Source/Engine/Graphics/Textures/StreamingTexture.h index 2317c52db..324040baf 100644 --- a/Source/Engine/Graphics/Textures/StreamingTexture.h +++ b/Source/Engine/Graphics/Textures/StreamingTexture.h @@ -12,6 +12,7 @@ class FLAXENGINE_API StreamingTexture : public Object, public StreamableResource { friend class TextureBase; + friend class TexturesStreamingHandler; friend class StreamTextureMipTask; friend class StreamTextureResizeTask; protected: @@ -20,6 +21,7 @@ protected: GPUTexture* _texture; TextureHeader _header; volatile mutable int64 _streamingTasksCount; + int32 _minMipCountBlockCompressed; bool _isBlockCompressed; public: @@ -122,14 +124,6 @@ public: return &_header; } - /// - /// Gets a boolean indicating whether this is a using a block compress format (BC1, BC2, BC3, BC4, BC5, BC6H, BC7). - /// - FORCE_INLINE bool IsBlockCompressed() const - { - return _isBlockCompressed; - } - public: /// diff --git a/Source/Engine/Streaming/StreamingHandlers.cpp b/Source/Engine/Streaming/StreamingHandlers.cpp index 2fb4cd840..169d94c10 100644 --- a/Source/Engine/Streaming/StreamingHandlers.cpp +++ b/Source/Engine/Streaming/StreamingHandlers.cpp @@ -57,10 +57,10 @@ int32 TexturesStreamingHandler::CalculateResidency(StreamableResource* resource, #endif } - if (mipLevels > 0 && mipLevels < 3 && totalMipLevels > 1 && texture.IsBlockCompressed()) + if (mipLevels > 0 && mipLevels < texture._minMipCountBlockCompressed && texture._isBlockCompressed) { - // Block compressed textures require minimum size of 4 (3 mips or more) - mipLevels = 3; + // Block compressed textures require minimum size of 4 + mipLevels = texture._minMipCountBlockCompressed; } return Math::Clamp(mipLevels, 0, totalMipLevels); From f5a6a1823295d41aaad32d70f9f5ae61318ad760 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 18 Jun 2021 15:20:39 +0200 Subject: [PATCH 197/198] Fix stability on exit or when doing heavy resources streaming stress tests --- .../Graphics/Materials/MaterialShader.cpp | 2 +- .../DirectX/DX11/GPUContextDX11.cpp | 17 +++++++++++++++-- Source/Engine/Render2D/Font.cpp | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Source/Engine/Graphics/Materials/MaterialShader.cpp b/Source/Engine/Graphics/Materials/MaterialShader.cpp index e897c6258..1c332641d 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.cpp +++ b/Source/Engine/Graphics/Materials/MaterialShader.cpp @@ -64,7 +64,7 @@ MaterialShader::MaterialShader(const StringView& name) MaterialShader::~MaterialShader() { - ASSERT(!_isLoaded); + ASSERT(!_isLoaded && _shader); SAFE_DELETE_GPU_RESOURCE(_shader); } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp index b283c7a5f..b0cc5cb63 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp +++ b/Source/Engine/GraphicsDevice/DirectX/DX11/GPUContextDX11.cpp @@ -118,7 +118,7 @@ void GPUContextDX11::FrameBegin() _context->VSSetSamplers(0, ARRAY_COUNT(samplers), samplers); _context->DSSetSamplers(0, ARRAY_COUNT(samplers), samplers); _context->PSSetSamplers(0, ARRAY_COUNT(samplers), samplers); - _context->CSSetSamplers(0, ARRAY_COUNT(samplers), samplers); // TODO: maybe we don't want to bind those static sampler always? + _context->CSSetSamplers(0, ARRAY_COUNT(samplers), samplers); } #if GPU_ALLOW_PROFILE_EVENTS @@ -637,7 +637,20 @@ void GPUContextDX11::ClearState() FlushState(); - //_context->ClearState(); + _context->ClearState(); + ID3D11SamplerState* samplers[] = + { + _device->_samplerLinearClamp, + _device->_samplerPointClamp, + _device->_samplerLinearWrap, + _device->_samplerPointWrap, + _device->_samplerShadow, + _device->_samplerShadowPCF + }; + _context->VSSetSamplers(0, ARRAY_COUNT(samplers), samplers); + _context->DSSetSamplers(0, ARRAY_COUNT(samplers), samplers); + _context->PSSetSamplers(0, ARRAY_COUNT(samplers), samplers); + _context->CSSetSamplers(0, ARRAY_COUNT(samplers), samplers); } void GPUContextDX11::FlushState() diff --git a/Source/Engine/Render2D/Font.cpp b/Source/Engine/Render2D/Font.cpp index 537c604d9..26b652de8 100644 --- a/Source/Engine/Render2D/Font.cpp +++ b/Source/Engine/Render2D/Font.cpp @@ -440,5 +440,5 @@ void Font::FlushFaceSize() const String Font::ToString() const { - return String::Format(TEXT("Font {0} {1}"), _asset->GetFamilyName(), _size); + return String::Format(TEXT("Font {0} {1}"), _asset ? _asset->GetFamilyName() : String::Empty, _size); } From 94f50ce8cbd0c7e5979fc37d4c04cb4b4afa0359 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Fri, 18 Jun 2021 16:38:44 +0200 Subject: [PATCH 198/198] Cleanup some headers in Graphics module --- Source/Engine/Graphics/DynamicBuffer.h | 6 +-- Source/Engine/Graphics/GPUDevice.cpp | 36 +++++++++++++ Source/Engine/Graphics/GPUResource.h | 50 ++++--------------- .../Graphics/Materials/MaterialShader.h | 1 + Source/Engine/Graphics/RenderBuffers.h | 1 + Source/Engine/Graphics/Shaders/GPUShader.cpp | 1 + Source/Engine/Graphics/Shaders/GPUShader.h | 2 +- .../DirectX/DX12/DescriptorHeapDX12.h | 1 + Source/Engine/Render2D/FontTextureAtlas.h | 9 +--- Source/Engine/Terrain/TerrainManager.cpp | 3 +- .../Engine/Tools/TextureTool/TextureTool.cpp | 1 + 11 files changed, 58 insertions(+), 53 deletions(-) diff --git a/Source/Engine/Graphics/DynamicBuffer.h b/Source/Engine/Graphics/DynamicBuffer.h index eb8e9fb5f..e33a3420d 100644 --- a/Source/Engine/Graphics/DynamicBuffer.h +++ b/Source/Engine/Graphics/DynamicBuffer.h @@ -2,12 +2,13 @@ #pragma once +#include "Engine/Core/Collections/Array.h" #include "GPUBuffer.h" /// /// Dynamic GPU buffer that allows to update and use GPU data (index/vertex/other) during single frame (supports dynamic resizing) /// -class FLAXENGINE_API DynamicBuffer : public NonCopyable +class FLAXENGINE_API DynamicBuffer { protected: @@ -16,6 +17,7 @@ protected: uint32 _stride; public: + NON_COPYABLE(DynamicBuffer); /// /// Init @@ -45,8 +47,6 @@ public: return _buffer; } -public: - /// /// Clear data (begin for writing) /// diff --git a/Source/Engine/Graphics/GPUDevice.cpp b/Source/Engine/Graphics/GPUDevice.cpp index e3f6542b5..18c1b5da0 100644 --- a/Source/Engine/Graphics/GPUDevice.cpp +++ b/Source/Engine/Graphics/GPUDevice.cpp @@ -155,6 +155,42 @@ GPUPipelineState::Description GPUPipelineState::Description::DefaultFullscreenTr BlendingMode::Opaque, }; +GPUResource::GPUResource() + : PersistentScriptingObject(SpawnParams(Guid::New(), GPUResource::TypeInitializer)) +{ +} + +GPUResource::GPUResource(const SpawnParams& params) + : PersistentScriptingObject(params) +{ +} + +GPUResource::~GPUResource() +{ +#if !BUILD_RELEASE + ASSERT(_memoryUsage == 0); +#endif +} + +GPUResource::ObjectType GPUResource::GetObjectType() const +{ + return ObjectType::Other; +} + +uint64 GPUResource::GetMemoryUsage() const +{ + return _memoryUsage; +} + +#if GPU_ENABLE_RESOURCE_NAMING + +String GPUResource::GetName() const +{ + return String::Empty; +} + +#endif + void GPUResource::ReleaseGPU() { if (_memoryUsage != 0) diff --git a/Source/Engine/Graphics/GPUResource.h b/Source/Engine/Graphics/GPUResource.h index 4f7d09c63..5d4eff81c 100644 --- a/Source/Engine/Graphics/GPUResource.h +++ b/Source/Engine/Graphics/GPUResource.h @@ -2,8 +2,7 @@ #pragma once -#include "Engine/Core/Common.h" -#include "Engine/Core/NonCopyable.h" +#include "Engine/Core/Enums.h" #include "Engine/Scripting/ScriptingObject.h" #include "Config.h" @@ -15,7 +14,7 @@ /// /// The base class for all GPU resources. /// -API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API GPUResource : public PersistentScriptingObject, public NonCopyable +API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API GPUResource : public PersistentScriptingObject { DECLARE_SCRIPTING_TYPE_NO_SPAWN(GPUResource); public: @@ -35,33 +34,23 @@ protected: uint64 _memoryUsage = 0; public: + NON_COPYABLE(GPUResource); /// /// Initializes a new instance of the class. /// - GPUResource() - : PersistentScriptingObject(SpawnParams(Guid::New(), GPUResource::TypeInitializer)) - { - } + GPUResource(); /// /// Initializes a new instance of the class. /// /// The object initialization parameters. - GPUResource(const SpawnParams& params) - : PersistentScriptingObject(params) - { - } + GPUResource(const SpawnParams& params); /// /// Finalizes an instance of the class. /// - virtual ~GPUResource() - { -#if !BUILD_RELEASE - ASSERT(_memoryUsage == 0); -#endif - } + virtual ~GPUResource(); public: @@ -83,29 +72,19 @@ public: /// /// Gets resource object type. /// - virtual ObjectType GetObjectType() const - { - return ObjectType::Other; - } + virtual ObjectType GetObjectType() const; /// - /// Gets amount of GPU memory used by this resource (in bytes). - /// It's a rough estimation. GPU memory may be fragmented, compressed or sub-allocated so the actual memory pressure from this resource may vary (also depends on the current graphics backend). + /// Gets amount of GPU memory used by this resource (in bytes). It's a rough estimation. GPU memory may be fragmented, compressed or sub-allocated so the actual memory pressure from this resource may vary (also depends on the current graphics backend). /// - API_PROPERTY() FORCE_INLINE uint64 GetMemoryUsage() const - { - return _memoryUsage; - } + API_PROPERTY() uint64 GetMemoryUsage() const; #if GPU_ENABLE_RESOURCE_NAMING /// /// Gets the resource name. /// - virtual String GetName() const - { - return String::Empty; - } + virtual String GetName() const; #endif @@ -165,9 +144,6 @@ public: , _name(name.Get(), name.Length()) #endif { - ASSERT(device); - - // Register device->Resources.Add(this); } @@ -176,7 +152,6 @@ public: /// virtual ~GPUResourceBase() { - // Unregister if (_device) _device->Resources.Remove(this); } @@ -186,7 +161,6 @@ public: /// /// Gets the graphics device. /// - /// The device. FORCE_INLINE DeviceType* GetDevice() const { return _device; @@ -203,10 +177,7 @@ public: #endif void OnDeviceDispose() override { - // Base GPUResource::OnDeviceDispose(); - - // Unlink device handle _device = nullptr; } }; @@ -234,6 +205,5 @@ public: /// /// Gets the native pointer to the underlying view. It's a platform-specific handle. /// - /// The pointer. virtual void* GetNativePtr() const = 0; }; diff --git a/Source/Engine/Graphics/Materials/MaterialShader.h b/Source/Engine/Graphics/Materials/MaterialShader.h index 9879c87ec..4196b6de4 100644 --- a/Source/Engine/Graphics/Materials/MaterialShader.h +++ b/Source/Engine/Graphics/Materials/MaterialShader.h @@ -3,6 +3,7 @@ #pragma once #include "IMaterial.h" +#include "Engine/Core/Collections/Array.h" #include "Engine/Graphics/GPUPipelineState.h" #include "Engine/Renderer/Config.h" diff --git a/Source/Engine/Graphics/RenderBuffers.h b/Source/Engine/Graphics/RenderBuffers.h index 76ef50a05..6815d8d0c 100644 --- a/Source/Engine/Graphics/RenderBuffers.h +++ b/Source/Engine/Graphics/RenderBuffers.h @@ -3,6 +3,7 @@ #pragma once #include "Engine/Core/Math/Viewport.h" +#include "Engine/Core/Collections/Array.h" #include "Engine/Scripting/ScriptingObject.h" #include "Engine/Graphics/Textures/GPUTexture.h" diff --git a/Source/Engine/Graphics/Shaders/GPUShader.cpp b/Source/Engine/Graphics/Shaders/GPUShader.cpp index d14a204a5..c57d825d3 100644 --- a/Source/Engine/Graphics/Shaders/GPUShader.cpp +++ b/Source/Engine/Graphics/Shaders/GPUShader.cpp @@ -3,6 +3,7 @@ #include "GPUShader.h" #include "GPUConstantBuffer.h" #include "Engine/Core/Log.h" +#include "Engine/Core/Math/Math.h" #include "Engine/Serialization/MemoryReadStream.h" GPUShaderProgramsContainer::GPUShaderProgramsContainer() diff --git a/Source/Engine/Graphics/Shaders/GPUShader.h b/Source/Engine/Graphics/Shaders/GPUShader.h index 66f57b207..cc9ee8eb9 100644 --- a/Source/Engine/Graphics/Shaders/GPUShader.h +++ b/Source/Engine/Graphics/Shaders/GPUShader.h @@ -167,7 +167,7 @@ public: /// The Constant Buffer object. API_FUNCTION() FORCE_INLINE GPUConstantBuffer* GetCB(int32 slot) const { - ASSERT_LOW_LAYER(Math::IsInRange(slot, 0, ARRAY_COUNT(_constantBuffers) - 1)); + ASSERT_LOW_LAYER(slot >= 0 && slot < ARRAY_COUNT(_constantBuffers)); return _constantBuffers[slot]; } diff --git a/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h b/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h index 7b9bf20a2..262457e9c 100644 --- a/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h +++ b/Source/Engine/GraphicsDevice/DirectX/DX12/DescriptorHeapDX12.h @@ -4,6 +4,7 @@ #if GRAPHICS_API_DIRECTX12 +#include "Engine/Core/Collections/Array.h" #include "Engine/Graphics/GPUResource.h" #include "../IncludeDirectXHeaders.h" diff --git a/Source/Engine/Render2D/FontTextureAtlas.h b/Source/Engine/Render2D/FontTextureAtlas.h index 5f622ada0..f9cca5530 100644 --- a/Source/Engine/Render2D/FontTextureAtlas.h +++ b/Source/Engine/Render2D/FontTextureAtlas.h @@ -3,6 +3,7 @@ #pragma once #include "Engine/Core/Collections/Array.h" +#include "Engine/Core/Math/Vector2.h" #include "Engine/Content/Assets/Texture.h" #include "Engine/Graphics/Textures/GPUTexture.h" #include "Engine/Utilities/RectPack.h" @@ -91,7 +92,6 @@ public: /// /// Gets the atlas width. /// - /// The width. FORCE_INLINE uint32 GetWidth() const { return _width; @@ -100,7 +100,6 @@ public: /// /// Gets the atlas height. /// - /// The height. FORCE_INLINE uint32 GetHeight() const { return _height; @@ -109,7 +108,6 @@ public: /// /// Gets the atlas size. /// - /// The size. FORCE_INLINE Vector2 GetSize() const { return Vector2(static_cast(_width), static_cast(_height)); @@ -118,9 +116,6 @@ public: /// /// Determines whether this atlas is dirty and data need to be flushed. /// - /// - /// true if this atlas is dirty; otherwise, false. - /// FORCE_INLINE bool IsDirty() const { return _isDirty; @@ -129,7 +124,6 @@ public: /// /// Gets padding style for textures in the atlas. /// - /// The padding style. FORCE_INLINE PaddingStyle GetPaddingStyle() const { return _paddingStyle; @@ -138,7 +132,6 @@ public: /// /// Gets amount of pixels to pad textures inside an atlas. /// - /// The padding amount. uint32 GetPaddingAmount() const; public: diff --git a/Source/Engine/Terrain/TerrainManager.cpp b/Source/Engine/Terrain/TerrainManager.cpp index caf620ff0..e0a27d50d 100644 --- a/Source/Engine/Terrain/TerrainManager.cpp +++ b/Source/Engine/Terrain/TerrainManager.cpp @@ -6,12 +6,13 @@ #include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/GPUBuffer.h" #include "Engine/Core/Math/Color32.h" +#include "Engine/Core/Collections/ChunkedArray.h" +#include "Engine/Core/Collections/Dictionary.h" #include "Engine/Content/Content.h" #include "Engine/Engine/EngineService.h" #include "Engine/Content/Assets/MaterialBase.h" #include "Engine/Content/AssetReference.h" #include "Engine/Renderer/DrawCall.h" -#include "Engine/Core/Collections/ChunkedArray.h" // Must match structure defined in Terrain.shader struct TerrainVertex diff --git a/Source/Engine/Tools/TextureTool/TextureTool.cpp b/Source/Engine/Tools/TextureTool/TextureTool.cpp index e607a862e..be85b502d 100644 --- a/Source/Engine/Tools/TextureTool/TextureTool.cpp +++ b/Source/Engine/Tools/TextureTool/TextureTool.cpp @@ -16,6 +16,7 @@ #include "Engine/Graphics/PixelFormatExtensions.h" #if USE_EDITOR +#include "Engine/Core/Collections/Dictionary.h" namespace { Dictionary TexturesHasAlphaCache;