From e324d3276910377f092d59fbb3bc0f5ec67f62f4 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 24 Oct 2024 19:34:42 +0200 Subject: [PATCH] Add async init and tint coloring based on type to debug commands --- Source/Editor/Windows/OutputLogWindow.cs | 14 +++++ Source/Engine/Debug/DebugCommands.cpp | 80 +++++++++++++++++++++--- Source/Engine/Debug/DebugCommands.h | 28 +++++++++ 3 files changed, 114 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Windows/OutputLogWindow.cs b/Source/Editor/Windows/OutputLogWindow.cs index 882730fed..f22befe9c 100644 --- a/Source/Editor/Windows/OutputLogWindow.cs +++ b/Source/Editor/Windows/OutputLogWindow.cs @@ -212,6 +212,11 @@ namespace FlaxEditor.Windows Name = command, Owner = this, }); + var flags = DebugCommands.GetCommandFlags(command); + if (flags.HasFlag(DebugCommands.CommandFlags.Exec)) + lastItem.TintColor = new Color(0.85f, 0.85f, 1.0f, 1.0f); + else if (flags.HasFlag(DebugCommands.CommandFlags.Read) && !flags.HasFlag(DebugCommands.CommandFlags.Write)) + lastItem.TintColor = new Color(0.85f, 0.85f, 0.85f, 1.0f); lastItem.Focused += item => { // Set command @@ -259,6 +264,15 @@ namespace FlaxEditor.Windows RootWindow.Window.LostFocus -= OnRootWindowLostFocus; } + /// + public override void OnGotFocus() + { + // Precache debug commands to reduce time-to-interactive + DebugCommands.InitAsync(); + + base.OnGotFocus(); + } + /// protected override void OnTextChanged() { diff --git a/Source/Engine/Debug/DebugCommands.cpp b/Source/Engine/Debug/DebugCommands.cpp index a9ae862f2..b041e779f 100644 --- a/Source/Engine/Debug/DebugCommands.cpp +++ b/Source/Engine/Debug/DebugCommands.cpp @@ -5,6 +5,7 @@ #include "Engine/Core/Collections/Array.h" #include "Engine/Engine/EngineService.h" #include "Engine/Threading/Threading.h" +#include "Engine/Threading/Task.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Scripting/BinaryModule.h" #include "Engine/Scripting/Scripting.h" @@ -103,6 +104,7 @@ namespace { CriticalSection Locker; bool Inited = false; + Task* AsyncTask = nullptr; Array Commands; void FindDebugCommands(BinaryModule* module) @@ -208,15 +210,45 @@ namespace void InitCommands() { + ASSERT_LOW_LAYER(!Inited); PROFILE_CPU(); - Inited = true; + + // Cache existing modules const auto& modules = BinaryModule::GetModules(); for (BinaryModule* module : modules) { FindDebugCommands(module); } + + // Link for modules load/reload actions Scripting::BinaryModuleLoaded.Bind(&FindDebugCommands); Scripting::ScriptsReloading.Bind(&OnScriptsReloading); + + // Mark as done + Locker.Lock(); + Inited = true; + AsyncTask = nullptr; + Locker.Unlock(); + } + + void EnsureInited() + { + // Check current state + Locker.Lock(); + bool inited = Inited; + Locker.Unlock(); + if (inited) + return; + + // Wait for any async task + if (AsyncTask) + AsyncTask->Wait(); + + // Do sync init if still not inited + Locker.Lock(); + if (!Inited) + InitCommands(); + Locker.Unlock(); } } @@ -231,6 +263,8 @@ public: void Dispose() override { // Cleanup + if (AsyncTask) + AsyncTask->Wait(); ScopeLock lock(Locker); Scripting::BinaryModuleLoaded.Unbind(&FindDebugCommands); Scripting::ScriptsReloading.Unbind(&OnScriptsReloading); @@ -264,9 +298,8 @@ void DebugCommands::Execute(StringView command) } // Ensure that commands cache has been created + EnsureInited(); ScopeLock lock(Locker); - if (!Inited) - InitCommands(); // Find command to run for (const CommandData& cmd : Commands) @@ -290,9 +323,8 @@ void DebugCommands::Search(StringView searchText, Array& matches, bo String searchTextCopy = searchText; searchText = searchTextCopy; + EnsureInited(); ScopeLock lock(Locker); - if (!Inited) - InitCommands(); if (startsWith) { @@ -316,13 +348,45 @@ void DebugCommands::Search(StringView searchText, Array& matches, bo } } -bool DebugCommands::Iterate(const StringView& searchText, int32& index) +void DebugCommands::InitAsync() { ScopeLock lock(Locker); + if (Inited) + return; + AsyncTask = Task::StartNew(InitCommands); +} + +DebugCommands::CommandFlags DebugCommands::GetCommandFlags(StringView command) +{ + CommandFlags result = CommandFlags::None; + // TODO: fix missing string handle on 1st command execution (command gets invalid after InitCommands due to dotnet GC or dotnet interop handles flush) + String commandCopy = command; + command = commandCopy; + EnsureInited(); + for (auto& e : Commands) + { + if (e.Name == command) + { + if (e.Method) + result |= CommandFlags::Exec; + else if (e.Field) + result |= CommandFlags::ReadWrite; + if (e.MethodGet) + result |= CommandFlags::Read; + if (e.MethodSet) + result |= CommandFlags::Write; + break; + } + } + return result; +} + +bool DebugCommands::Iterate(const StringView& searchText, int32& index) +{ + EnsureInited(); if (index >= 0) { - if (!Inited) - InitCommands(); + ScopeLock lock(Locker); while (index < Commands.Count()) { auto& command = Commands.Get()[index]; diff --git a/Source/Engine/Debug/DebugCommands.h b/Source/Engine/Debug/DebugCommands.h index f89cba371..e82963792 100644 --- a/Source/Engine/Debug/DebugCommands.h +++ b/Source/Engine/Debug/DebugCommands.h @@ -11,6 +11,21 @@ API_CLASS(static) class FLAXENGINE_API DebugCommands { DECLARE_SCRIPTING_TYPE_MINIMAL(DebugCommands); + // Types of debug command flags. + API_ENUM(Attributes="Flags") enum class CommandFlags + { + // Incorrect or missing command. + None = 0, + // Executable method. + Exec = 1, + // Can get value. + Read = 2, + // Can set value. + Write = 4, + // Can get and set value. + ReadWrite = Read | Write, + }; + public: /// /// Executes the command. @@ -26,7 +41,20 @@ public: /// True if filter commands that start with a specific search text, otherwise will return commands that contain a specific query. API_FUNCTION() static void Search(StringView searchText, API_PARAM(Out) Array& matches, bool startsWith = false); + /// + /// Starts asynchronous debug commands caching. Cna be used to minimize time-to-interactive when using console interface or when using scripted actions. + /// + API_FUNCTION() static void InitAsync(); + + /// + /// Returns flags of the command. + /// + /// The full name of the command. + API_FUNCTION() static CommandFlags GetCommandFlags(StringView command); + public: static bool Iterate(const StringView& searchText, int32& index); static StringView GetCommandName(int32 index); }; + +DECLARE_ENUM_OPERATORS(DebugCommands::CommandFlags);