From 2b5d8716098488cdc36b7fb6fe1cd9fb49d4beda Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 7 Apr 2019 18:49:04 +0100 Subject: [PATCH 1/3] Introduced official version of the plugin: - Set plugin version to 1.14 - Added CHANGES.md file with overview of changes. --- CHANGES.md | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++ ImGui.uplugin | 2 +- 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 CHANGES.md diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..ad8a9a0 --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,97 @@ +This is only a summary provided to give a quick overview of changes. It does not list details which can be found in commit messages. If you think that more detailed changelog would be beneficiary, please raise it as an issue. + +Versions marked as 'unofficial' are labelled only for the needs of this changelog. Officially I maintain version numbers since 2019/04, starting from version 1.14. If you have any of the earlier commits then you will see plugin signed as a version 1.0. + +Change History +-------------- + +Version: 1.14 (2019/03) +- Added SImGuiLayout to resets layout for SImGuiWidget. +- Refactored rendering in SImGuiWidget to take advantage of layout reset. +- Reworked ImGui canvas dragging and scaling and moved it to SImGuiCanvasControl. +- Removed dependency on ImGui internal cursor data. + +Version: 1.13 (unofficial) (2019/03) +- Fixed mapping from FKey to ImGui key index to gracefully handle unsupported keys and work on platforms that do not support all the keys defined in ImGui key map. +- Fixed non-unity compile warnings and errors. + +Version: 1.12 (unofficial) (2018/12) +- Added support for sharing with game keyboard and gamepad input. +- Added FImGuiModuleSettings to handle delayed loading of UImGuiSettings and serve as settings proxy for other classes. + +Version: 1.11 (unofficial) (2018/10-11) +- Moved ImGui Draw events to be called at the end of the world update during post actor tick event. Only available in UE 4.18 or later, with old behaviour available as an option. +- Replaced console variable based configuration of ImGui Draw events with macros. +- Replaced console variable based configuration of software cursor with a setting. +- Console variables and logging that are primarily focused on module development are hidden by default and can be enabled by setting IMGUI_MODULE_DEVELOPER to 1. +- Replaced console variables with module properties and settings. +- Added console commands to control module properties. +- Added support to preserve and move module properties to hot-reloaded module. +- Moved properties to public interface. +- Added FImGuiModule interface to access properties instance. +- DEPRECIATED FImGuiModule functions to modify single properties. + +Version: 1.10 (unofficial) (2018/10) +- Changed module type to 'Developer' to make it easier strip it from shipping or other builds. +- Added runtime loader to allow automatically load developer module in runtime builds. + +Version: 1.09 (unofficial) (2018/08-09) +- Added interface to register user textures for use in ImGui. +- Fixed bad deallocation in Texture Manager. +- Updated to ImGui 1.61. +- Updated to UE 4.20. + +Version: 1.08 (unofficial) (2018/07-08) +- Added ImGui Input Handler to allow to customization of input handling. +- Added ImGui settings with editor page in project properties. +- Added command to switch input mode with configurable key binding. +- Added backward compatibility macros. +- Fixed hot-reloading issues with using ImGui implementation. + +Version: 1.07 (unofficial) (2018/05) +- Improved initialization to allow loading module in any loading phase. + +Version: 1.06 (unofficial) (2018/05) +- Updated to ImGui 1.61 +- Added support for gamepad and keyboard navigation. + +Version: 1.05 (unofficial) (2018/04) +- Added mode to scale and drag ImGui canvas. +- Using ImGui internal cursor data to draw drag icon. + +Version: 1.04 (unofficial) (2018/03) +- Minimised lag between ending ImGui frame and rendering draw data in Slate. +- Moved ImGui Draw event to be called during world tick start with configuration to use old behaviour. + +Version: 1.03 (unofficial) (2018/01-03) +- Fixed warnings and errors found in non-unity, Linux, PS4 or XBox builds. +- Added configuration to choose between hardware and software cursor with hardware cursor used by default. + +Version: 1.02 (unofficial) (2018/01) +- Updated to ImGui 1.53. +- Fixed problems with ImGui Demo working in multi-context environment. +- Added FImGuiModule interface to change input mode and demo visibility. +- Fixed input state issues after application loses focus. +- Added input state debugging and `ImGui.Debug.Input` console variable to switch it. + +Version: 1.01 (unofficial) (2017/10) +- Added `ImGui.ShowDemo` console variable to show/hide ImGui demo. +- Added automatic switching to right ImGui context at the beginning of the world tick. +- Updated to UE 4.18 + +Version: <=1.00 (unofficial) (2017/04-10) +- Added ImGui source code as an external module to expose it in IDE. +- Integrated ImGui source code to build as part of the ImGui module. +- Added FImGuiModule to implement ImGui module interface. +- Added FImGuiModuleManager to control other module components. +- Added FTextureManager to manage texture resources and map them to ImTextureID. +- Added SImGuiWidget to handle Slate input and render in Slate ImGui draw data. +- Added FImGuiInputState to collect and store input before it can be copied to ImGui IO. +- Added FContextProxy to represent a single ImGui context. +- Added FImGuiContextManager to create and manage ImGui context proxies. +- Added Multi-PIE support with each PIE instance getting a dedicated ImGui context proxy, input state and widget. +- Added `ImGui.InputEnabled` console variable to control whether input mode is enabled. +- Added widget debugging and `ImGui.Debug.Widget` console variable to switch it. +- Added ImGui software cursor. +- Added support for session reloading with ini file names based on world type and PIE index. +- Added FImGuiModule interface to register ImGui delegates called to draw ImGui controls. diff --git a/ImGui.uplugin b/ImGui.uplugin index 586d533..1e95a39 100644 --- a/ImGui.uplugin +++ b/ImGui.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, "Version": 1, - "VersionName": "1.0", + "VersionName": "1.14", "FriendlyName": "ImGui", "Description": "", "Category": "Debug", From 867a34e640aeac81243fb8ca90e662dc9fd2823c Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 10 Apr 2019 20:19:11 +0100 Subject: [PATCH 2/3] Changed interface to register ImGui debug delegates: - Added new FImGuiDelegates interface for ImGui debug delegates. - Added FImGuiDelegatesContainer for ImGui delegates. - Added code preserving delegates during hot-reloading and moving them to a new module. - Depreciated old FImGuiModule delegates interface and FImGuiDelegateHandle. - Delegates registered with depreciated interface are redirected and get benefit of being preserved during hot-reloading. This can be controlled with IMGUI_REDIRECT_OBSOLETE_DELEGATES. - Added IMGUI_WITH_OBSOLETE_DELEGATES allowing to strip depreciated interface from builds. - Modified context manager and context proxy to work with FImGuiDelegatesContainer. --- CHANGES.md | 7 +++ ImGui.uplugin | 2 +- Source/ImGui/Private/ImGuiContextManager.cpp | 6 +++ Source/ImGui/Private/ImGuiContextManager.h | 4 +- Source/ImGui/Private/ImGuiContextProxy.cpp | 50 ++++++++++++++----- Source/ImGui/Private/ImGuiContextProxy.h | 9 +++- Source/ImGui/Private/ImGuiDelegates.cpp | 22 ++++++++ .../ImGui/Private/ImGuiDelegatesContainer.cpp | 37 ++++++++++++++ .../ImGui/Private/ImGuiDelegatesContainer.h | 40 +++++++++++++++ Source/ImGui/Private/ImGuiModule.cpp | 44 ++++++++++++++++ .../Private/Utilities/WorldContextIndex.h | 5 ++ Source/ImGui/Public/ImGuiDelegates.h | 39 +++++++++++++++ Source/ImGui/Public/ImGuiModule.h | 5 ++ 13 files changed, 253 insertions(+), 17 deletions(-) create mode 100644 Source/ImGui/Private/ImGuiDelegates.cpp create mode 100644 Source/ImGui/Private/ImGuiDelegatesContainer.cpp create mode 100644 Source/ImGui/Private/ImGuiDelegatesContainer.h diff --git a/CHANGES.md b/CHANGES.md index ad8a9a0..ef62a2b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,13 @@ Versions marked as 'unofficial' are labelled only for the needs of this changelo Change History -------------- +Version: 1.15 (2019/04) +- Added new FImGuiDelegates interface for ImGui debug delegates. +- Added code preserving delegates during hot-reloading and moving them to a new module. +- DEPRECIATED old FImGuiModule delegates interface and FImGuiDelegateHandle. +- Delegates registered with depreciated interface are redirected and get benefit of being preserved during hot-reloading. This can be controlled with IMGUI_REDIRECT_OBSOLETE_DELEGATES. +- Added IMGUI_WITH_OBSOLETE_DELEGATES allowing to strip depreciated interface from builds (that interface will be officially removed in one of later releases). + Version: 1.14 (2019/03) - Added SImGuiLayout to resets layout for SImGuiWidget. - Refactored rendering in SImGuiWidget to take advantage of layout reset. diff --git a/ImGui.uplugin b/ImGui.uplugin index 1e95a39..6bc3553 100644 --- a/ImGui.uplugin +++ b/ImGui.uplugin @@ -1,7 +1,7 @@ { "FileVersion": 3, "Version": 1, - "VersionName": "1.14", + "VersionName": "1.15", "FriendlyName": "ImGui", "Description": "", "Category": "Debug", diff --git a/Source/ImGui/Private/ImGuiContextManager.cpp b/Source/ImGui/Private/ImGuiContextManager.cpp index ad8e769..bd419a7 100644 --- a/Source/ImGui/Private/ImGuiContextManager.cpp +++ b/Source/ImGui/Private/ImGuiContextManager.cpp @@ -4,6 +4,7 @@ #include "ImGuiContextManager.h" +#include "ImGuiDelegatesContainer.h" #include "ImGuiImplementation.h" #include "Utilities/ScopeGuards.h" #include "Utilities/WorldContext.h" @@ -89,6 +90,11 @@ void FImGuiContextManager::Tick(float DeltaSeconds) { ContextData.ContextProxy.Tick(DeltaSeconds); } + else + { + // Clear to make sure that we don't store objects registered for world that is no longer valid. + FImGuiDelegatesContainer::Get().OnWorldDebug(Pair.Key).Clear(); + } } } diff --git a/Source/ImGui/Private/ImGuiContextManager.h b/Source/ImGui/Private/ImGuiContextManager.h index f810f77..d3c48dc 100644 --- a/Source/ImGui/Private/ImGuiContextManager.h +++ b/Source/ImGui/Private/ImGuiContextManager.h @@ -72,7 +72,7 @@ private: { FContextData(const FString& ContextName, int32 ContextIndex, FSimpleMulticastDelegate& SharedDrawEvent, ImFontAtlas& FontAtlas, int32 InPIEInstance = -1) : PIEInstance(InPIEInstance) - , ContextProxy(ContextName, &SharedDrawEvent, &FontAtlas) + , ContextProxy(ContextName, ContextIndex, &SharedDrawEvent, &FontAtlas) { } @@ -87,7 +87,7 @@ private: struct FContextData { FContextData(const FString& ContextName, int32 ContextIndex, FSimpleMulticastDelegate& SharedDrawEvent, ImFontAtlas& FontAtlas) - : ContextProxy(ContextName, &SharedDrawEvent, &FontAtlas) + : ContextProxy(ContextName, ContextIndex, &SharedDrawEvent, &FontAtlas) { } diff --git a/Source/ImGui/Private/ImGuiContextProxy.cpp b/Source/ImGui/Private/ImGuiContextProxy.cpp index ffa6cc9..bf92fa3 100644 --- a/Source/ImGui/Private/ImGuiContextProxy.cpp +++ b/Source/ImGui/Private/ImGuiContextProxy.cpp @@ -4,6 +4,7 @@ #include "ImGuiContextProxy.h" +#include "ImGuiDelegatesContainer.h" #include "ImGuiImplementation.h" #include "ImGuiInteroperability.h" @@ -52,8 +53,9 @@ FImGuiContextProxy::FImGuiContextPtr::~FImGuiContextPtr() } } -FImGuiContextProxy::FImGuiContextProxy(const FString& InName, FSimpleMulticastDelegate* InSharedDrawEvent, ImFontAtlas* InFontAtlas) +FImGuiContextProxy::FImGuiContextProxy(const FString& InName, int32 InContextIndex, FSimpleMulticastDelegate* InSharedDrawEvent, ImFontAtlas* InFontAtlas) : Name(InName) + , ContextIndex(InContextIndex) , SharedDrawEvent(InSharedDrawEvent) , IniFilename(TCHAR_TO_ANSI(*GetIniFile(InName))) { @@ -91,22 +93,13 @@ void FImGuiContextProxy::Draw() // Broadcast draw event to allow listeners to draw their controls to this context. #if DRAW_EVENTS_ORDER_WORLD_BEFORE_MULTI_CONTEXT - if (DrawEvent.IsBound()) - { - DrawEvent.Broadcast(); - } + BroadcastWorldTick(); #endif // DRAW_EVENTS_ORDER_WORLD_BEFORE_MULTI_CONTEXT - if (SharedDrawEvent && SharedDrawEvent->IsBound()) - { - SharedDrawEvent->Broadcast(); - } + BroadcastMultiContextTick(); #if !DRAW_EVENTS_ORDER_WORLD_BEFORE_MULTI_CONTEXT - if (DrawEvent.IsBound()) - { - DrawEvent.Broadcast(); - } + BroadcastWorldTick(); #endif // !DRAW_EVENTS_ORDER_WORLD_BEFORE_MULTI_CONTEXT } } @@ -192,3 +185,34 @@ void FImGuiContextProxy::UpdateDrawData(ImDrawData* DrawData) DrawLists.Empty(); } } + +void FImGuiContextProxy::BroadcastWorldTick() +{ + if (DrawEvent.IsBound()) + { + DrawEvent.Broadcast(); + } + + if (ContextIndex != Utilities::INVALID_CONTEXT_INDEX) + { + FSimpleMulticastDelegate& WorldDebugEvent = FImGuiDelegatesContainer::Get().OnWorldDebug(ContextIndex); + if (WorldDebugEvent.IsBound()) + { + WorldDebugEvent.Broadcast(); + } + } +} + +void FImGuiContextProxy::BroadcastMultiContextTick() +{ + if (SharedDrawEvent && SharedDrawEvent->IsBound()) + { + SharedDrawEvent->Broadcast(); + } + + FSimpleMulticastDelegate& MultiContextDebugEvent = FImGuiDelegatesContainer::Get().OnMultiContextDebug(); + if (MultiContextDebugEvent.IsBound()) + { + MultiContextDebugEvent.Broadcast(); + } +} diff --git a/Source/ImGui/Private/ImGuiContextProxy.h b/Source/ImGui/Private/ImGuiContextProxy.h index b91f16b..433b4f2 100644 --- a/Source/ImGui/Private/ImGuiContextProxy.h +++ b/Source/ImGui/Private/ImGuiContextProxy.h @@ -4,6 +4,8 @@ #include "ImGuiDrawData.h" +#include "Utilities/WorldContextIndex.h" + #include #include @@ -41,7 +43,7 @@ class FImGuiContextProxy public: - FImGuiContextProxy(const FString& Name, FSimpleMulticastDelegate* InSharedDrawEvent, ImFontAtlas* InFontAtlas); + FImGuiContextProxy(const FString& Name, int32 InContextIndex, FSimpleMulticastDelegate* InSharedDrawEvent, ImFontAtlas* InFontAtlas); FImGuiContextProxy(const FImGuiContextProxy&) = delete; FImGuiContextProxy& operator=(const FImGuiContextProxy&) = delete; @@ -96,6 +98,9 @@ private: void UpdateDrawData(ImDrawData* DrawData); + void BroadcastWorldTick(); + void BroadcastMultiContextTick(); + FImGuiContextPtr Context; FVector2D DisplaySize = FVector2D::ZeroVector; @@ -115,6 +120,8 @@ private: TArray DrawLists; + int32 ContextIndex = Utilities::INVALID_CONTEXT_INDEX; + FString Name; std::string IniFilename; }; diff --git a/Source/ImGui/Private/ImGuiDelegates.cpp b/Source/ImGui/Private/ImGuiDelegates.cpp new file mode 100644 index 0000000..46b78c2 --- /dev/null +++ b/Source/ImGui/Private/ImGuiDelegates.cpp @@ -0,0 +1,22 @@ +// Distributed under the MIT License (MIT) (see accompanying LICENSE file) + +#include "ImGuiPrivatePCH.h" + +#include "ImGuiDelegates.h" +#include "ImGuiDelegatesContainer.h" + + +FSimpleMulticastDelegate& FImGuiDelegates::OnWorldDebug() +{ + return OnWorldDebug(GWorld); +} + +FSimpleMulticastDelegate& FImGuiDelegates::OnWorldDebug(UWorld* World) +{ + return FImGuiDelegatesContainer::Get().OnWorldDebug(World); +} + +FSimpleMulticastDelegate& FImGuiDelegates::OnMultiContextDebug() +{ + return FImGuiDelegatesContainer::Get().OnMultiContextDebug(); +} diff --git a/Source/ImGui/Private/ImGuiDelegatesContainer.cpp b/Source/ImGui/Private/ImGuiDelegatesContainer.cpp new file mode 100644 index 0000000..6565e6d --- /dev/null +++ b/Source/ImGui/Private/ImGuiDelegatesContainer.cpp @@ -0,0 +1,37 @@ +// Distributed under the MIT License (MIT) (see accompanying LICENSE file) + +#include "ImGuiPrivatePCH.h" + +#include "ImGuiDelegatesContainer.h" + +#include "Utilities/WorldContextIndex.h" + + +FImGuiDelegatesContainer FImGuiDelegatesContainer::DefaultInstance; + +FImGuiDelegatesContainer* FImGuiDelegatesContainer::InstancePtr = &FImGuiDelegatesContainer::DefaultInstance; + +void FImGuiDelegatesContainer::MoveContainer(FImGuiDelegatesContainer& Dst) +{ + // Only move data if pointer points to default instance, otherwise our data has already been moved and we only + // keep pointer to a more recent version. + if (InstancePtr == &DefaultInstance) + { + Dst = MoveTemp(DefaultInstance); + DefaultInstance.Clear(); + } + + // Update pointer to the most recent version. + InstancePtr = &Dst; +} + +FSimpleMulticastDelegate& FImGuiDelegatesContainer::OnWorldDebug(UWorld* World) +{ + return OnWorldDebug(Utilities::GetWorldContextIndex(*World)); +} + +void FImGuiDelegatesContainer::Clear() +{ + WorldDelegates.Empty(); + MultiContextDelegate.Clear(); +} diff --git a/Source/ImGui/Private/ImGuiDelegatesContainer.h b/Source/ImGui/Private/ImGuiDelegatesContainer.h new file mode 100644 index 0000000..fa07aac --- /dev/null +++ b/Source/ImGui/Private/ImGuiDelegatesContainer.h @@ -0,0 +1,40 @@ +// Distributed under the MIT License (MIT) (see accompanying LICENSE file) + +#pragma once + +#include +#include + + +struct FImGuiDelegatesContainer +{ +public: + + // Get the current instance (can change during hot-reloading). + static FImGuiDelegatesContainer& Get() { return *InstancePtr; } + + // If this is an active container move its data to a destination and redirect all future calls to that instance. + static void MoveContainer(FImGuiDelegatesContainer& Dst); + + // Get delegate to ImGui world debug event from known world instance. + FSimpleMulticastDelegate& OnWorldDebug(UWorld* World); + + // Get delegate to ImGui world debug event from known context index. + FSimpleMulticastDelegate& OnWorldDebug(int32 ContextIndex) { return WorldDelegates.FindOrAdd(ContextIndex); } + + // Get delegate to ImGui multi-context debug event. + FSimpleMulticastDelegate& OnMultiContextDebug() { return MultiContextDelegate; } + +private: + + void Clear(); + + TMap WorldDelegates; + FSimpleMulticastDelegate MultiContextDelegate; + + // Default container instance. + static FImGuiDelegatesContainer DefaultInstance; + + // Pointer to the container instance that can be overwritten during hot-reloading. + static FImGuiDelegatesContainer* InstancePtr; +}; diff --git a/Source/ImGui/Private/ImGuiModule.cpp b/Source/ImGui/Private/ImGuiModule.cpp index 6a619c2..92b5d1a 100644 --- a/Source/ImGui/Private/ImGuiModule.cpp +++ b/Source/ImGui/Private/ImGuiModule.cpp @@ -4,6 +4,7 @@ #include "ImGuiModuleManager.h" +#include "ImGuiDelegatesContainer.h" #include "ImGuiTextureHandle.h" #include "TextureManager.h" #include "Utilities/WorldContext.h" @@ -16,6 +17,8 @@ #include +#define IMGUI_REDIRECT_OBSOLETE_DELEGATES 1 + #define LOCTEXT_NAMESPACE "FImGuiModule" @@ -38,18 +41,29 @@ static FImGuiModuleManager* ImGuiModuleManager = nullptr; static FImGuiEditor* ImGuiEditor = nullptr; #endif +#if IMGUI_WITH_OBSOLETE_DELEGATES + #if WITH_EDITOR FImGuiDelegateHandle FImGuiModule::AddEditorImGuiDelegate(const FImGuiDelegate& Delegate) { +#if IMGUI_REDIRECT_OBSOLETE_DELEGATES + return { FImGuiDelegatesContainer::Get().OnWorldDebug(Utilities::EDITOR_CONTEXT_INDEX).Add(Delegate), + EDelegateCategory::Default, Utilities::EDITOR_CONTEXT_INDEX }; +#else checkf(ImGuiModuleManager, TEXT("Null pointer to internal module implementation. Is module available?")); return { ImGuiModuleManager->GetContextManager().GetEditorContextProxy().OnDraw().Add(Delegate), EDelegateCategory::Default, Utilities::EDITOR_CONTEXT_INDEX }; +#endif // IMGUI_REDIRECT_OBSOLETE_DELEGATES } #endif FImGuiDelegateHandle FImGuiModule::AddWorldImGuiDelegate(const FImGuiDelegate& Delegate) { +#if IMGUI_REDIRECT_OBSOLETE_DELEGATES + const int32 ContextIndex = Utilities::GetWorldContextIndex((UWorld*)GWorld); + return { FImGuiDelegatesContainer::Get().OnWorldDebug(ContextIndex).Add(Delegate), EDelegateCategory::Default, ContextIndex }; +#else checkf(ImGuiModuleManager, TEXT("Null pointer to internal module implementation. Is module available?")); #if WITH_EDITOR @@ -71,17 +85,32 @@ FImGuiDelegateHandle FImGuiModule::AddWorldImGuiDelegate(const FImGuiDelegate& D #endif return{ Proxy.OnDraw().Add(Delegate), EDelegateCategory::Default, Index }; +#endif // IMGUI_REDIRECT_OBSOLETE_DELEGATES } FImGuiDelegateHandle FImGuiModule::AddMultiContextImGuiDelegate(const FImGuiDelegate& Delegate) { +#if IMGUI_REDIRECT_OBSOLETE_DELEGATES + return { FImGuiDelegatesContainer::Get().OnMultiContextDebug().Add(Delegate), EDelegateCategory::MultiContext }; +#else checkf(ImGuiModuleManager, TEXT("Null pointer to internal module implementation. Is module available?")); return { ImGuiModuleManager->GetContextManager().OnDrawMultiContext().Add(Delegate), EDelegateCategory::MultiContext }; +#endif } void FImGuiModule::RemoveImGuiDelegate(const FImGuiDelegateHandle& Handle) { +#if IMGUI_REDIRECT_OBSOLETE_DELEGATES + if (Handle.Category == EDelegateCategory::MultiContext) + { + FImGuiDelegatesContainer::Get().OnMultiContextDebug().Remove(Handle.Handle); + } + else + { + FImGuiDelegatesContainer::Get().OnWorldDebug(Handle.Index).Remove(Handle.Handle); + } +#else if (ImGuiModuleManager) { if (Handle.Category == EDelegateCategory::MultiContext) @@ -93,8 +122,11 @@ void FImGuiModule::RemoveImGuiDelegate(const FImGuiDelegateHandle& Handle) Proxy->OnDraw().Remove(Handle.Handle); } } +#endif } +#endif // IMGUI_WITH_OBSOLETE_DELEGATES + FImGuiTextureHandle FImGuiModule::FindTextureHandle(const FName& Name) { const TextureIndex Index = ImGuiModuleManager->GetTextureManager().FindTextureIndex(Name); @@ -154,6 +186,10 @@ void FImGuiModule::ShutdownModule() // deleted. This can cause troubles after hot-reload when code in other modules calls ImGui interface functions // which are statically bound to the obsolete module. To keep ImGui code functional we can redirect context handle // to point to the new module. + + // When shutting down during hot-reloading, we might want to rewire handles used in statically bound functions + // or move data to a new module. + FModuleManager::Get().OnModulesChanged().AddLambda([this] (FName Name, EModuleChangeReason Reason) { if (Reason == EModuleChangeReason::ModuleLoaded && Name == "ImGui") @@ -161,8 +197,11 @@ void FImGuiModule::ShutdownModule() FImGuiModule& LoadedModule = FImGuiModule::Get(); if (&LoadedModule != this) { + // Statically bound functions will be still made to the obsolete module so we need to ImGuiImplementation::SetImGuiContextHandle(LoadedModule.GetImGuiContextHandle()); + FImGuiDelegatesContainer::MoveContainer(LoadedModule.GetDelegatesContainer()); + if (bMoveProperties) { bMoveProperties = false; @@ -184,6 +223,11 @@ ImGuiContext** FImGuiModule::GetImGuiContextHandle() { return ImGuiImplementation::GetImGuiContextHandle(); } + +FImGuiDelegatesContainer& FImGuiModule::GetDelegatesContainer() +{ + return FImGuiDelegatesContainer::Get(); +} #endif FImGuiModuleProperties& FImGuiModule::GetProperties() diff --git a/Source/ImGui/Private/Utilities/WorldContextIndex.h b/Source/ImGui/Private/Utilities/WorldContextIndex.h index 6bf8c8e..39b36b2 100644 --- a/Source/ImGui/Private/Utilities/WorldContextIndex.h +++ b/Source/ImGui/Private/Utilities/WorldContextIndex.h @@ -56,6 +56,11 @@ namespace Utilities return (World.WorldType == EWorldType::Editor) ? EDITOR_CONTEXT_INDEX : GetWorldContextIndex(World.GetGameInstance()); } + FORCEINLINE int32 GetWorldContextIndex(const UWorld* World) + { + return World ? GetWorldContextIndex(*World) : INVALID_CONTEXT_INDEX; + } + #else template diff --git a/Source/ImGui/Public/ImGuiDelegates.h b/Source/ImGui/Public/ImGuiDelegates.h index a4514dc..cf9c986 100644 --- a/Source/ImGui/Public/ImGuiDelegates.h +++ b/Source/ImGui/Public/ImGuiDelegates.h @@ -5,6 +5,43 @@ #include +/** + * Delegates to ImGui debug events called. World delegates are called once per frame to draw debug for owning world + * and are automatically cleared when that world becomes invalid. Multi-context delegates are called once for every + * world that needs to be debugged. + * In engine version 4.18 or later delegates are called during OnWorldPostActorTick event while in older versions + * 4.18 during OnWorldTickStart event. Calling behaviours can be changed in build configuration. + */ +class IMGUI_API FImGuiDelegates +{ +public: + + /** + * Get a delegate to ImGui world debug event for current world (GWorld). + * @returns Simple multicast delegate to debug events called once per frame to debug current world + */ + static FSimpleMulticastDelegate& OnWorldDebug(); + + /** + * Get a delegate to ImGui world debug event for given world. + * @param World - World for which we need a delegate + * @returns Simple multicast delegate to debug events called once per frame to debug given world + */ + static FSimpleMulticastDelegate& OnWorldDebug(UWorld* World); + + /** + * Get a delegate to ImGui multi-context debug event. + * @returns Simple multicast delegate to debug events called once per frame for every world to debug + */ + static FSimpleMulticastDelegate& OnMultiContextDebug(); +}; + + +/** Enable to support legacy ImGui delegates API. */ +#define IMGUI_WITH_OBSOLETE_DELEGATES 1 + +#if IMGUI_WITH_OBSOLETE_DELEGATES + /** Delegate that allows to subscribe for ImGui events. */ typedef FSimpleMulticastDelegate::FDelegate FImGuiDelegate; @@ -53,3 +90,5 @@ private: friend class FImGuiModule; }; + +#endif // IMGUI_WITH_OBSOLETE_DELEGATES diff --git a/Source/ImGui/Public/ImGuiModule.h b/Source/ImGui/Public/ImGuiModule.h index b5486bc..e8c6f42 100644 --- a/Source/ImGui/Public/ImGuiModule.h +++ b/Source/ImGui/Public/ImGuiModule.h @@ -34,6 +34,8 @@ public: return FModuleManager::Get().IsModuleLoaded("ImGui"); } +#if IMGUI_WITH_OBSOLETE_DELEGATES + #if WITH_EDITOR /** * Add a delegate called at the end of editor debug frame to draw debug controls in its ImGui context, creating @@ -71,6 +73,8 @@ public: */ virtual void RemoveImGuiDelegate(const FImGuiDelegateHandle& Handle); +#endif // #if IMGUI_WITH_OBSOLETE_DELEGATES + /** * If it exists, get a handle to the texture with given resource name. * @@ -165,5 +169,6 @@ public: #if WITH_EDITOR virtual void SetProperties(const FImGuiModuleProperties& Properties); virtual struct ImGuiContext** GetImGuiContextHandle(); + virtual struct FImGuiDelegatesContainer& GetDelegatesContainer(); #endif }; From 4a9d66889bdfd49421e875c3f249f606240ce3c2 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 14 Apr 2019 12:16:15 +0100 Subject: [PATCH 3/3] Added ImGui early debug events: - Added to FImGuiDelegates interface early debug delegates called during world tick start. - Delegates are called in fixed order: multi-context early debug, world early debug (called during world tick start), world debug, multi-context debug (called during world post actor tick or if not available, during world tick start). - Removed from build script configuration of debug delegates. --- CHANGES.md | 3 + Source/ImGui/ImGui.Build.cs | 46 -------------- Source/ImGui/Private/ImGuiContextManager.cpp | 19 +++--- Source/ImGui/Private/ImGuiContextManager.h | 2 +- Source/ImGui/Private/ImGuiContextProxy.cpp | 62 ++++++++++++++----- Source/ImGui/Private/ImGuiContextProxy.h | 18 ++++-- Source/ImGui/Private/ImGuiDelegates.cpp | 15 +++++ .../ImGui/Private/ImGuiDelegatesContainer.cpp | 10 +-- .../ImGui/Private/ImGuiDelegatesContainer.h | 23 +++++-- Source/ImGui/Private/VersionCompatibility.h | 5 ++ Source/ImGui/Public/ImGuiDelegates.h | 31 ++++++++-- 11 files changed, 143 insertions(+), 91 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ef62a2b..365e229 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,9 @@ Version: 1.15 (2019/04) - DEPRECIATED old FImGuiModule delegates interface and FImGuiDelegateHandle. - Delegates registered with depreciated interface are redirected and get benefit of being preserved during hot-reloading. This can be controlled with IMGUI_REDIRECT_OBSOLETE_DELEGATES. - Added IMGUI_WITH_OBSOLETE_DELEGATES allowing to strip depreciated interface from builds (that interface will be officially removed in one of later releases). +- Added new ImGui early debug delegates called during world tick start. +- Delegates are called in fixed order: multi-context early debug, world early debug (called during world tick start), world debug, multi-context debug (called during world post actor tick or if not available, during world tick start). +- Removed from build script configuration of debug delegates. Version: 1.14 (2019/03) - Added SImGuiLayout to resets layout for SImGuiWidget. diff --git a/Source/ImGui/ImGui.Build.cs b/Source/ImGui/ImGui.Build.cs index c882062..550d943 100644 --- a/Source/ImGui/ImGui.Build.cs +++ b/Source/ImGui/ImGui.Build.cs @@ -1,45 +1,11 @@ // Distributed under the MIT License (MIT) (see accompanying LICENSE file) -#if UE_4_18_OR_LATER -#define WITH_POST_ACTOR_TICK -#endif - using System.Collections.Generic; using System.IO; using UnrealBuildTool; public class ImGui : ModuleRules { - // Defines when events should be broadcast. Note that at the end of the ImGui frame some global variables might be - // not set and so it seems preferable to use post actor tick (not available in older engine versions) or world tick - // start events. If more control is required Late mode with manually calling events may be used (as in practice - // events are guaranteed to be raised only once per frame). - enum EEventsBroadcastMode - { - OnWorldTickStart, // Broadcast during world tick start event. -#if WITH_POST_ACTOR_TICK - OnPostActorTick, // Broadcast during post actor tick event. -#endif - Late, // Broadcast at the end of the ImGui frame. - } - - // Defines order in which multi-context and world-context events are called. - enum EEventsOrder - { - MultiBeforeWorldContext, - WorldBeforeMultiContext, - } - -#if WITH_POST_ACTOR_TICK - EEventsBroadcastMode DrawEventsBroadcastMode = EEventsBroadcastMode.OnPostActorTick; - EEventsOrder DrawEventsOrder = EEventsOrder.WorldBeforeMultiContext; -#else - EEventsBroadcastMode DrawEventsBroadcastMode = EEventsBroadcastMode.OnWorldTickStart; - EEventsOrder DrawEventsOrder = EEventsOrder.MultiBeforeWorldContext; -#endif // WITH_POST_ACTOR_TICK - - - #if WITH_FORWARDED_MODULE_RULES_CTOR public ImGui(ReadOnlyTargetRules Target) : base(Target) #else @@ -128,17 +94,5 @@ public class ImGui : ModuleRules #endif PrivateDefinitions.Add(string.Format("RUNTIME_LOADER_ENABLED={0}", bEnableRuntimeLoader ? 1 : 0)); - - bool bDrawOnWorldTickStart = (DrawEventsBroadcastMode == EEventsBroadcastMode.OnWorldTickStart); -#if WITH_POST_ACTOR_TICK - bool bDrawOnPostActorTick = (DrawEventsBroadcastMode == EEventsBroadcastMode.OnPostActorTick); -#else - bool bDrawOnPostActorTick = false; -#endif - - PrivateDefinitions.Add(string.Format("DRAW_EVENTS_ON_WORLD_TICK_START={0}", bDrawOnWorldTickStart ? 1 : 0)); - PrivateDefinitions.Add(string.Format("DRAW_EVENTS_ON_POST_ACTOR_TICK={0}", bDrawOnPostActorTick ? 1 : 0)); - PrivateDefinitions.Add(string.Format("DRAW_EVENTS_ORDER_WORLD_BEFORE_MULTI_CONTEXT={0}", - (DrawEventsOrder == EEventsOrder.WorldBeforeMultiContext) ? 1 : 0)); } } diff --git a/Source/ImGui/Private/ImGuiContextManager.cpp b/Source/ImGui/Private/ImGuiContextManager.cpp index bd419a7..388e60f 100644 --- a/Source/ImGui/Private/ImGuiContextManager.cpp +++ b/Source/ImGui/Private/ImGuiContextManager.cpp @@ -64,7 +64,7 @@ FImGuiContextManager::FImGuiContextManager() FontAtlas.GetTexDataAsRGBA32(&Pixels, &Width, &Height, &Bpp); FWorldDelegates::OnWorldTickStart.AddRaw(this, &FImGuiContextManager::OnWorldTickStart); -#if DRAW_EVENTS_ON_POST_ACTOR_TICK +#if ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK FWorldDelegates::OnWorldPostActorTick.AddRaw(this, &FImGuiContextManager::OnWorldPostActorTick); #endif } @@ -73,7 +73,7 @@ FImGuiContextManager::~FImGuiContextManager() { // Order matters because contexts can be created during World Tick Start events. FWorldDelegates::OnWorldTickStart.RemoveAll(this); -#if DRAW_EVENTS_ON_POST_ACTOR_TICK +#if ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK FWorldDelegates::OnWorldPostActorTick.RemoveAll(this); #endif } @@ -103,22 +103,23 @@ void FImGuiContextManager::OnWorldTickStart(ELevelTick TickType, float DeltaSeco if (GWorld) { FImGuiContextProxy& ContextProxy = GetWorldContextProxy(*GWorld); + + // Set as current, so we have right context ready when updating world objects. ContextProxy.SetAsCurrent(); -#if DRAW_EVENTS_ON_WORLD_TICK_START - ContextProxy.Draw(); + ContextProxy.DrawEarlyDebug(); +#if !ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK + ContextProxy.DrawDebug(); #endif } } -#if DRAW_EVENTS_ON_POST_ACTOR_TICK +#if ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK void FImGuiContextManager::OnWorldPostActorTick(UWorld* World, ELevelTick TickType, float DeltaSeconds) { - FImGuiContextProxy& ContextProxy = GetWorldContextProxy(*GWorld); - ContextProxy.SetAsCurrent(); - ContextProxy.Draw(); + GetWorldContextProxy(*GWorld).DrawDebug(); } -#endif // DRAW_EVENTS_ON_POST_ACTOR_TICK +#endif // ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK #if WITH_EDITOR FImGuiContextManager::FContextData& FImGuiContextManager::GetEditorContextData() diff --git a/Source/ImGui/Private/ImGuiContextManager.h b/Source/ImGui/Private/ImGuiContextManager.h index d3c48dc..a2a3208 100644 --- a/Source/ImGui/Private/ImGuiContextManager.h +++ b/Source/ImGui/Private/ImGuiContextManager.h @@ -100,7 +100,7 @@ private: void OnWorldTickStart(ELevelTick TickType, float DeltaSeconds); -#if DRAW_EVENTS_ON_POST_ACTOR_TICK +#if ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK void OnWorldPostActorTick(UWorld* World, ELevelTick TickType, float DeltaSeconds); #endif diff --git a/Source/ImGui/Private/ImGuiContextProxy.cpp b/Source/ImGui/Private/ImGuiContextProxy.cpp index bf92fa3..38916e6 100644 --- a/Source/ImGui/Private/ImGuiContextProxy.cpp +++ b/Source/ImGui/Private/ImGuiContextProxy.cpp @@ -83,24 +83,34 @@ FImGuiContextProxy::FImGuiContextProxy(const FString& InName, int32 InContextInd BeginFrame(); } -void FImGuiContextProxy::Draw() +void FImGuiContextProxy::DrawEarlyDebug() { - if (bIsFrameStarted && !bIsDrawCalled) + if (bIsFrameStarted && !bIsDrawEarlyDebugCalled) { - bIsDrawCalled = true; + bIsDrawEarlyDebugCalled = true; SetAsCurrent(); - // Broadcast draw event to allow listeners to draw their controls to this context. -#if DRAW_EVENTS_ORDER_WORLD_BEFORE_MULTI_CONTEXT - BroadcastWorldTick(); -#endif // DRAW_EVENTS_ORDER_WORLD_BEFORE_MULTI_CONTEXT + // Delegates called in order specified in FImGuiDelegates. + BroadcastMultiContextEarlyDebug(); + BroadcastWorldEarlyDebug(); + } +} - BroadcastMultiContextTick(); +void FImGuiContextProxy::DrawDebug() +{ + if (bIsFrameStarted && !bIsDrawDebugCalled) + { + bIsDrawDebugCalled = true; -#if !DRAW_EVENTS_ORDER_WORLD_BEFORE_MULTI_CONTEXT - BroadcastWorldTick(); -#endif // !DRAW_EVENTS_ORDER_WORLD_BEFORE_MULTI_CONTEXT + // Make sure that early debug is always called first to guarantee order specified in FImGuiDelegates. + DrawEarlyDebug(); + + SetAsCurrent(); + + // Delegates called in order specified in FImGuiDelegates. + BroadcastWorldDebug(); + BroadcastMultiContextDebug(); } } @@ -116,7 +126,7 @@ void FImGuiContextProxy::Tick(float DeltaSeconds) if (bIsFrameStarted) { // Make sure that draw events are called before the end of the frame. - Draw(); + DrawDebug(); // Ending frame will produce render output that we capture and store for later use. This also puts context to // state in which it does not allow to draw controls, so we want to immediately start a new frame. @@ -149,7 +159,8 @@ void FImGuiContextProxy::BeginFrame(float DeltaTime) ImGui::NewFrame(); bIsFrameStarted = true; - bIsDrawCalled = false; + bIsDrawEarlyDebugCalled = false; + bIsDrawDebugCalled = false; } } @@ -186,7 +197,28 @@ void FImGuiContextProxy::UpdateDrawData(ImDrawData* DrawData) } } -void FImGuiContextProxy::BroadcastWorldTick() +void FImGuiContextProxy::BroadcastWorldEarlyDebug() +{ + if (ContextIndex != Utilities::INVALID_CONTEXT_INDEX) + { + FSimpleMulticastDelegate& WorldEarlyDebugEvent = FImGuiDelegatesContainer::Get().OnWorldEarlyDebug(ContextIndex); + if (WorldEarlyDebugEvent.IsBound()) + { + WorldEarlyDebugEvent.Broadcast(); + } + } +} + +void FImGuiContextProxy::BroadcastMultiContextEarlyDebug() +{ + FSimpleMulticastDelegate& MultiContextEarlyDebugEvent = FImGuiDelegatesContainer::Get().OnMultiContextEarlyDebug(); + if (MultiContextEarlyDebugEvent.IsBound()) + { + MultiContextEarlyDebugEvent.Broadcast(); + } +} + +void FImGuiContextProxy::BroadcastWorldDebug() { if (DrawEvent.IsBound()) { @@ -203,7 +235,7 @@ void FImGuiContextProxy::BroadcastWorldTick() } } -void FImGuiContextProxy::BroadcastMultiContextTick() +void FImGuiContextProxy::BroadcastMultiContextDebug() { if (SharedDrawEvent && SharedDrawEvent->IsBound()) { diff --git a/Source/ImGui/Private/ImGuiContextProxy.h b/Source/ImGui/Private/ImGuiContextProxy.h index 433b4f2..8809866 100644 --- a/Source/ImGui/Private/ImGuiContextProxy.h +++ b/Source/ImGui/Private/ImGuiContextProxy.h @@ -84,9 +84,11 @@ public: // Delegate called right before ending the frame to allows listeners draw their controls. FSimpleMulticastDelegate& OnDraw() { return DrawEvent; } - // Call draw events to allow listeners draw their widgets. Only one call per frame is processed. If it is not - // called manually before, then it will be called from the Tick function. - void Draw(); + // Call early debug events to allow listeners draw their debug widgets. + void DrawEarlyDebug(); + + // Call debug events to allow listeners draw their debug widgets. + void DrawDebug(); // Tick to advance context to the next frame. Only one call per frame will be processed. void Tick(float DeltaSeconds); @@ -98,8 +100,11 @@ private: void UpdateDrawData(ImDrawData* DrawData); - void BroadcastWorldTick(); - void BroadcastMultiContextTick(); + void BroadcastWorldEarlyDebug(); + void BroadcastMultiContextEarlyDebug(); + + void BroadcastWorldDebug(); + void BroadcastMultiContextDebug(); FImGuiContextPtr Context; @@ -109,7 +114,8 @@ private: bool bHasActiveItem = false; bool bIsFrameStarted = false; - bool bIsDrawCalled = false; + bool bIsDrawEarlyDebugCalled = false; + bool bIsDrawDebugCalled = false; uint32 LastFrameNumber = 0; diff --git a/Source/ImGui/Private/ImGuiDelegates.cpp b/Source/ImGui/Private/ImGuiDelegates.cpp index 46b78c2..7ac36bd 100644 --- a/Source/ImGui/Private/ImGuiDelegates.cpp +++ b/Source/ImGui/Private/ImGuiDelegates.cpp @@ -6,6 +6,21 @@ #include "ImGuiDelegatesContainer.h" +FSimpleMulticastDelegate& FImGuiDelegates::OnWorldEarlyDebug() +{ + return OnWorldEarlyDebug(GWorld); +} + +FSimpleMulticastDelegate& FImGuiDelegates::OnWorldEarlyDebug(UWorld* World) +{ + return FImGuiDelegatesContainer::Get().OnWorldEarlyDebug(World); +} + +FSimpleMulticastDelegate& FImGuiDelegates::OnMultiContextEarlyDebug() +{ + return FImGuiDelegatesContainer::Get().OnMultiContextEarlyDebug(); +} + FSimpleMulticastDelegate& FImGuiDelegates::OnWorldDebug() { return OnWorldDebug(GWorld); diff --git a/Source/ImGui/Private/ImGuiDelegatesContainer.cpp b/Source/ImGui/Private/ImGuiDelegatesContainer.cpp index 6565e6d..93e5cf5 100644 --- a/Source/ImGui/Private/ImGuiDelegatesContainer.cpp +++ b/Source/ImGui/Private/ImGuiDelegatesContainer.cpp @@ -25,13 +25,15 @@ void FImGuiDelegatesContainer::MoveContainer(FImGuiDelegatesContainer& Dst) InstancePtr = &Dst; } -FSimpleMulticastDelegate& FImGuiDelegatesContainer::OnWorldDebug(UWorld* World) +int32 FImGuiDelegatesContainer::GetContextIndex(UWorld* World) { - return OnWorldDebug(Utilities::GetWorldContextIndex(*World)); + return Utilities::GetWorldContextIndex(*World); } void FImGuiDelegatesContainer::Clear() { - WorldDelegates.Empty(); - MultiContextDelegate.Clear(); + WorldEarlyDebugDelegates.Empty(); + WorldDebugDelegates.Empty(); + MultiContextEarlyDebugDelegate.Clear(); + MultiContextDebugDelegate.Clear(); } diff --git a/Source/ImGui/Private/ImGuiDelegatesContainer.h b/Source/ImGui/Private/ImGuiDelegatesContainer.h index fa07aac..6d864b6 100644 --- a/Source/ImGui/Private/ImGuiDelegatesContainer.h +++ b/Source/ImGui/Private/ImGuiDelegatesContainer.h @@ -16,21 +16,34 @@ public: // If this is an active container move its data to a destination and redirect all future calls to that instance. static void MoveContainer(FImGuiDelegatesContainer& Dst); + // Get delegate to ImGui world early debug event from known world instance. + FSimpleMulticastDelegate& OnWorldEarlyDebug(UWorld* World) { return OnWorldEarlyDebug(GetContextIndex(World)); } + + // Get delegate to ImGui world early debug event from known context index. + FSimpleMulticastDelegate& OnWorldEarlyDebug(int32 ContextIndex) { return WorldEarlyDebugDelegates.FindOrAdd(ContextIndex); } + + // Get delegate to ImGui multi-context early debug event. + FSimpleMulticastDelegate& OnMultiContextEarlyDebug() { return MultiContextEarlyDebugDelegate; } + // Get delegate to ImGui world debug event from known world instance. - FSimpleMulticastDelegate& OnWorldDebug(UWorld* World); + FSimpleMulticastDelegate& OnWorldDebug(UWorld* World) { return OnWorldDebug(GetContextIndex(World)); } // Get delegate to ImGui world debug event from known context index. - FSimpleMulticastDelegate& OnWorldDebug(int32 ContextIndex) { return WorldDelegates.FindOrAdd(ContextIndex); } + FSimpleMulticastDelegate& OnWorldDebug(int32 ContextIndex) { return WorldDebugDelegates.FindOrAdd(ContextIndex); } // Get delegate to ImGui multi-context debug event. - FSimpleMulticastDelegate& OnMultiContextDebug() { return MultiContextDelegate; } + FSimpleMulticastDelegate& OnMultiContextDebug() { return MultiContextDebugDelegate; } private: + int32 GetContextIndex(UWorld* World); + void Clear(); - TMap WorldDelegates; - FSimpleMulticastDelegate MultiContextDelegate; + TMap WorldEarlyDebugDelegates; + TMap WorldDebugDelegates; + FSimpleMulticastDelegate MultiContextEarlyDebugDelegate; + FSimpleMulticastDelegate MultiContextDebugDelegate; // Default container instance. static FImGuiDelegatesContainer DefaultInstance; diff --git a/Source/ImGui/Private/VersionCompatibility.h b/Source/ImGui/Private/VersionCompatibility.h index 04e6005..339cf57 100644 --- a/Source/ImGui/Private/VersionCompatibility.h +++ b/Source/ImGui/Private/VersionCompatibility.h @@ -23,3 +23,8 @@ // that renames FStringClassReference to FSoftClassPath, so it is still possible tu use the old type name in code. // The old header forwards to the new one but if used it outputs a warning, so we want to avoid it. #define ENGINE_COMPATIBILITY_LEGACY_STRING_CLASS_REF BELOW_ENGINE_VERSION(4, 18) + +// Starting from version 4.18 engine has a world post actor tick event which if available, provides a good opportunity +// to call debug delegates after world actors are already updated. +#define ENGINE_COMPATIBILITY_WITH_WORLD_POST_ACTOR_TICK FROM_ENGINE_VERSION(4, 18) + diff --git a/Source/ImGui/Public/ImGuiDelegates.h b/Source/ImGui/Public/ImGuiDelegates.h index cf9c986..d59f93d 100644 --- a/Source/ImGui/Public/ImGuiDelegates.h +++ b/Source/ImGui/Public/ImGuiDelegates.h @@ -6,16 +6,37 @@ /** - * Delegates to ImGui debug events called. World delegates are called once per frame to draw debug for owning world - * and are automatically cleared when that world becomes invalid. Multi-context delegates are called once for every - * world that needs to be debugged. - * In engine version 4.18 or later delegates are called during OnWorldPostActorTick event while in older versions - * 4.18 during OnWorldTickStart event. Calling behaviours can be changed in build configuration. + * Delegates to ImGui debug events. World delegates are called once per frame during world updates and have invocation + * lists cleared after their worlds become invalid. Multi-context delegates are called once for every updated world. + * Early debug delegates are called during world tick start and debug delegates are called during world post actor tick + * or in engine versions below 4.18 during world tick start. + * + * Order of events is defined in a way that multi-context delegates can be used to draw headers and/or footers: + * multi-context early debug, world early debug, world debug, multi-context debug. */ class IMGUI_API FImGuiDelegates { public: + /** + * Get a delegate to ImGui world early debug event for current world (GWorld). + * @returns Simple multicast delegate to debug events called once per frame to debug current world + */ + static FSimpleMulticastDelegate& OnWorldEarlyDebug(); + + /** + * Get a delegate to ImGui world early debug event for given world. + * @param World - World for which we need a delegate + * @returns Simple multicast delegate to debug events called once per frame to debug given world + */ + static FSimpleMulticastDelegate& OnWorldEarlyDebug(UWorld* World); + + /** + * Get a delegate to ImGui multi-context early debug event. + * @returns Simple multicast delegate to debug events called once per frame for every world to debug + */ + static FSimpleMulticastDelegate& OnMultiContextEarlyDebug(); + /** * Get a delegate to ImGui world debug event for current world (GWorld). * @returns Simple multicast delegate to debug events called once per frame to debug current world