From b49293205582b779a15df8d6e43dc06380ea6e21 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 28 Oct 2018 19:54:18 +0000 Subject: [PATCH] Replaced 'ImGui.DebugDrawOnWorldTick' console variable with conditional compilation switches and extended support to post actor tick event: - Replaced 'ImGui.DebugDrawOnWorldTick' with DRAW_EVENTS_ON_WORLD_TICK_START switch to enforce consistency in project setup. - Added DRAW_EVENTS_ON_POST_ACTOR_TICK to support post actor tick events (only for UE 4.18 or later). - Added DRAW_EVENTS_ORDER_WORLD_BEFORE_MULTI_CONTEXT to allow explicitly define order between multi-context and world-context draw events. - Rules can be configured in ImGui.Build.cs file. --- Source/ImGui/ImGui.Build.cs | 47 ++++++++++++++++++++ Source/ImGui/Private/ImGuiContextManager.cpp | 37 +++++++-------- Source/ImGui/Private/ImGuiContextManager.h | 4 ++ Source/ImGui/Private/ImGuiContextProxy.cpp | 23 +++++----- 4 files changed, 81 insertions(+), 30 deletions(-) diff --git a/Source/ImGui/ImGui.Build.cs b/Source/ImGui/ImGui.Build.cs index 2ac7ce8..95e68cc 100644 --- a/Source/ImGui/ImGui.Build.cs +++ b/Source/ImGui/ImGui.Build.cs @@ -1,11 +1,45 @@ // 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 @@ -89,6 +123,19 @@ public class ImGui : ModuleRules #if !UE_4_19_OR_LATER List PrivateDefinitions = Definitions; #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 f76ccff..dd3edd6 100644 --- a/Source/ImGui/Private/ImGuiContextManager.cpp +++ b/Source/ImGui/Private/ImGuiContextManager.cpp @@ -12,20 +12,6 @@ #include -namespace CVars -{ - TAutoConsoleVariable DebugDrawOnWorldTick(TEXT("ImGui.DebugDrawOnWorldTick"), 1, - TEXT("If this is enabled then all the ImGui debug draw events will be called during World Tick Start.\n") - TEXT("This has an advantage that all the global variables like GWorld are set to correct values.\n") - TEXT("A disadvantage is that objects are not yet updated. That can be changed by disabling this feature,") - TEXT("but preferable solution is to call debug code from object's own Tick function.\n") - TEXT("NOTE: Order of multi-context and world draw events depends on this value and is arranged in a way ") - TEXT("that world draw events and objects updates are closer together.\n") - TEXT("0: disabled, ImGui Debug Draw is called during Post-Tick\n") - TEXT("1: enabled (default), ImGui Debug Draw is called during World Tick Start"), - ECVF_Default); -} - namespace { #if WITH_EDITOR @@ -77,12 +63,18 @@ FImGuiContextManager::FImGuiContextManager() FontAtlas.GetTexDataAsRGBA32(&Pixels, &Width, &Height, &Bpp); FWorldDelegates::OnWorldTickStart.AddRaw(this, &FImGuiContextManager::OnWorldTickStart); +#if DRAW_EVENTS_ON_POST_ACTOR_TICK + FWorldDelegates::OnWorldPostActorTick.AddRaw(this, &FImGuiContextManager::OnWorldPostActorTick); +#endif } 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 + FWorldDelegates::OnWorldPostActorTick.RemoveAll(this); +#endif } void FImGuiContextManager::Tick(float DeltaSeconds) @@ -106,13 +98,22 @@ void FImGuiContextManager::OnWorldTickStart(ELevelTick TickType, float DeltaSeco { FImGuiContextProxy& ContextProxy = GetWorldContextProxy(*GWorld); ContextProxy.SetAsCurrent(); - if (CVars::DebugDrawOnWorldTick.GetValueOnGameThread() > 0) - { - ContextProxy.Draw(); - } + +#if DRAW_EVENTS_ON_WORLD_TICK_START + ContextProxy.Draw(); +#endif } } +#if DRAW_EVENTS_ON_POST_ACTOR_TICK +void FImGuiContextManager::OnWorldPostActorTick(UWorld* World, ELevelTick TickType, float DeltaSeconds) +{ + FImGuiContextProxy& ContextProxy = GetWorldContextProxy(*GWorld); + ContextProxy.SetAsCurrent(); + ContextProxy.Draw(); +} +#endif // DRAW_EVENTS_ON_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 90c3cf2..c53b935 100644 --- a/Source/ImGui/Private/ImGuiContextManager.h +++ b/Source/ImGui/Private/ImGuiContextManager.h @@ -92,6 +92,10 @@ private: void OnWorldTickStart(ELevelTick TickType, float DeltaSeconds); +#if DRAW_EVENTS_ON_POST_ACTOR_TICK + void OnWorldPostActorTick(UWorld* World, ELevelTick TickType, float DeltaSeconds); +#endif + #if WITH_EDITOR FContextData& GetEditorContextData(); #endif diff --git a/Source/ImGui/Private/ImGuiContextProxy.cpp b/Source/ImGui/Private/ImGuiContextProxy.cpp index 6a49b60..0f52e26 100644 --- a/Source/ImGui/Private/ImGuiContextProxy.cpp +++ b/Source/ImGui/Private/ImGuiContextProxy.cpp @@ -14,11 +14,6 @@ static constexpr float DEFAULT_CANVAS_WIDTH = 3840.f; static constexpr float DEFAULT_CANVAS_HEIGHT = 2160.f; -namespace CVars -{ - extern TAutoConsoleVariable DebugDrawOnWorldTick; -} - namespace { FString GetSaveDirectory() @@ -94,21 +89,25 @@ void FImGuiContextProxy::Draw() SetAsCurrent(); - const bool bSharedFirst = (CVars::DebugDrawOnWorldTick.GetValueOnGameThread() > 0); - // Broadcast draw event to allow listeners to draw their controls to this context. - if (bSharedFirst && SharedDrawEvent && SharedDrawEvent->IsBound()) - { - SharedDrawEvent->Broadcast(); - } +#if DRAW_EVENTS_ORDER_WORLD_BEFORE_MULTI_CONTEXT if (DrawEvent.IsBound()) { DrawEvent.Broadcast(); } - if (!bSharedFirst && SharedDrawEvent && SharedDrawEvent->IsBound()) +#endif // DRAW_EVENTS_ORDER_WORLD_BEFORE_MULTI_CONTEXT + + if (SharedDrawEvent && SharedDrawEvent->IsBound()) { SharedDrawEvent->Broadcast(); } + +#if !DRAW_EVENTS_ORDER_WORLD_BEFORE_MULTI_CONTEXT + if (DrawEvent.IsBound()) + { + DrawEvent.Broadcast(); + } +#endif // !DRAW_EVENTS_ORDER_WORLD_BEFORE_MULTI_CONTEXT } }