mirror of
https://github.com/kevinporetti/UnrealImGui.git
synced 2025-01-18 08:20:32 +00:00
Reduced lag between producing ImGui output and rendering it:
- ImGui Context Proxy Tick function has been split for Tick and Draw. Both functions may now be called separately from different places but they will be processed only once per frame. This opens possibility to define own mechanisms to trigger draw events and to independently update contexts. - Multi-context draw event delegate is passed to ImGui Context Proxy during construction rather than as an argument of Tick function. - To minimise lag between ending ImGui frame and rendering its output, ImGui Widget updates its own context just before painting. - Contexts without widget (editor, dedicated server) are updated by manager during post-tick event. - Instead of setting global CurrentContextIndex by ImGui Context Manager, information about context is passed to ImGui Demo as part of a closure. - Fixed bug in ImGui Demo where inactive contexts were triggering multi-PIE warning.
This commit is contained in:
parent
f18e1f0b68
commit
052ae0a201
@ -12,11 +12,6 @@
|
|||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
|
|
||||||
// Index of the currently updated context. Only valid during context manager tick.
|
|
||||||
// TODO: Move to public interface (but probably as a current world/viewport etc.)
|
|
||||||
int32 CurrentContextIndex = Utilities::INVALID_CONTEXT_INDEX;
|
|
||||||
|
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
#if WITH_EDITOR
|
#if WITH_EDITOR
|
||||||
@ -85,13 +80,10 @@ void FImGuiContextManager::Tick(float DeltaSeconds)
|
|||||||
|
|
||||||
for (auto& Pair : Contexts)
|
for (auto& Pair : Contexts)
|
||||||
{
|
{
|
||||||
auto ContextIndexSave = ScopeGuards::MakeStateSaver(CurrentContextIndex);
|
|
||||||
CurrentContextIndex = Pair.Key;
|
|
||||||
auto& ContextData = Pair.Value;
|
auto& ContextData = Pair.Value;
|
||||||
if (ContextData.CanTick())
|
if (ContextData.CanTick())
|
||||||
{
|
{
|
||||||
ContextData.ContextProxy.SetAsCurrent();
|
ContextData.ContextProxy.Tick(DeltaSeconds);
|
||||||
ContextData.ContextProxy.Tick(DeltaSeconds, &DrawMultiContextEvent);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,7 +105,7 @@ FImGuiContextManager::FContextData& FImGuiContextManager::GetEditorContextData()
|
|||||||
|
|
||||||
if (UNLIKELY(!Data))
|
if (UNLIKELY(!Data))
|
||||||
{
|
{
|
||||||
Data = &Contexts.Emplace(Utilities::EDITOR_CONTEXT_INDEX, FContextData{ GetEditorContextName(), ImGuiDemo });
|
Data = &Contexts.Emplace(Utilities::EDITOR_CONTEXT_INDEX, FContextData{ GetEditorContextName(), Utilities::EDITOR_CONTEXT_INDEX, DrawMultiContextEvent, ImGuiDemo, -1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
return *Data;
|
return *Data;
|
||||||
@ -127,7 +119,7 @@ FImGuiContextManager::FContextData& FImGuiContextManager::GetStandaloneWorldCont
|
|||||||
|
|
||||||
if (UNLIKELY(!Data))
|
if (UNLIKELY(!Data))
|
||||||
{
|
{
|
||||||
Data = &Contexts.Emplace(Utilities::STANDALONE_GAME_CONTEXT_INDEX, FContextData{ GetWorldContextName(), ImGuiDemo });
|
Data = &Contexts.Emplace(Utilities::STANDALONE_GAME_CONTEXT_INDEX, FContextData{ GetWorldContextName(), Utilities::STANDALONE_GAME_CONTEXT_INDEX, DrawMultiContextEvent, ImGuiDemo });
|
||||||
}
|
}
|
||||||
|
|
||||||
return *Data;
|
return *Data;
|
||||||
@ -167,7 +159,7 @@ FImGuiContextManager::FContextData& FImGuiContextManager::GetWorldContextData(co
|
|||||||
#if WITH_EDITOR
|
#if WITH_EDITOR
|
||||||
if (UNLIKELY(!Data))
|
if (UNLIKELY(!Data))
|
||||||
{
|
{
|
||||||
Data = &Contexts.Emplace(Index, FContextData{ GetWorldContextName(World), ImGuiDemo, WorldContext->PIEInstance });
|
Data = &Contexts.Emplace(Index, FContextData{ GetWorldContextName(World), Index, DrawMultiContextEvent, ImGuiDemo, WorldContext->PIEInstance });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -177,7 +169,7 @@ FImGuiContextManager::FContextData& FImGuiContextManager::GetWorldContextData(co
|
|||||||
#else
|
#else
|
||||||
if (UNLIKELY(!Data))
|
if (UNLIKELY(!Data))
|
||||||
{
|
{
|
||||||
Data = &Contexts.Emplace(Index, FContextData{ GetWorldContextName(World), ImGuiDemo });
|
Data = &Contexts.Emplace(Index, FContextData{ GetWorldContextName(World), Index, DrawMultiContextEvent, ImGuiDemo });
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -56,11 +56,11 @@ private:
|
|||||||
|
|
||||||
struct FContextData
|
struct FContextData
|
||||||
{
|
{
|
||||||
FContextData(const FString& ContextName, FImGuiDemo& Demo, int32 InPIEInstance = -1)
|
FContextData(const FString& ContextName, int32 ContextIndex, FSimpleMulticastDelegate& SharedDrawEvent, FImGuiDemo& Demo, int32 InPIEInstance = -1)
|
||||||
: PIEInstance(InPIEInstance)
|
: PIEInstance(InPIEInstance)
|
||||||
, ContextProxy(ContextName)
|
, ContextProxy(ContextName, &SharedDrawEvent)
|
||||||
{
|
{
|
||||||
ContextProxy.OnDraw().AddRaw(&Demo, &FImGuiDemo::DrawControls);
|
ContextProxy.OnDraw().AddLambda([&Demo, ContextIndex]() { Demo.DrawControls(ContextIndex); });
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCEINLINE bool CanTick() const { return PIEInstance < 0 || GEngine->GetWorldContextFromPIEInstance(PIEInstance); }
|
FORCEINLINE bool CanTick() const { return PIEInstance < 0 || GEngine->GetWorldContextFromPIEInstance(PIEInstance); }
|
||||||
@ -73,10 +73,10 @@ private:
|
|||||||
|
|
||||||
struct FContextData
|
struct FContextData
|
||||||
{
|
{
|
||||||
FContextData(const FString& ContextName, FImGuiDemo& Demo)
|
FContextData(const FString& ContextName, int32 ContextIndex, FSimpleMulticastDelegate& SharedDrawEvent, FImGuiDemo& Demo)
|
||||||
: ContextProxy(ContextName)
|
: ContextProxy(ContextName, &SharedDrawEvent)
|
||||||
{
|
{
|
||||||
ContextProxy.OnDraw().AddRaw(&Demo, &FImGuiDemo::DrawControls);
|
ContextProxy.OnDraw().AddLambda([&Demo, ContextIndex]() { Demo.DrawControls(ContextIndex); });
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCEINLINE bool CanTick() const { return true; }
|
FORCEINLINE bool CanTick() const { return true; }
|
||||||
|
@ -39,12 +39,13 @@ namespace
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FImGuiContextProxy::FImGuiContextProxy(const FString& InName)
|
FImGuiContextProxy::FImGuiContextProxy(const FString& InName, FSimpleMulticastDelegate* InSharedDrawEvent)
|
||||||
: Name(InName)
|
: Name(InName)
|
||||||
|
, SharedDrawEvent(InSharedDrawEvent)
|
||||||
, IniFilename(TCHAR_TO_ANSI(*GetIniFile(InName)))
|
, IniFilename(TCHAR_TO_ANSI(*GetIniFile(InName)))
|
||||||
{
|
{
|
||||||
// Create context.
|
// Create context.
|
||||||
Context = ImGui::CreateContext();
|
Context = TUniquePtr<ImGuiContext>(ImGui::CreateContext());
|
||||||
|
|
||||||
// Set this context in ImGui for initialization (any allocations will be tracked in this context).
|
// Set this context in ImGui for initialization (any allocations will be tracked in this context).
|
||||||
SetAsCurrent();
|
SetAsCurrent();
|
||||||
@ -80,35 +81,6 @@ FImGuiContextProxy::FImGuiContextProxy(const FString& InName)
|
|||||||
BeginFrame();
|
BeginFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
FImGuiContextProxy::FImGuiContextProxy(FImGuiContextProxy&& Other)
|
|
||||||
: Context(std::move(Other.Context))
|
|
||||||
, bHasActiveItem(Other.bHasActiveItem)
|
|
||||||
, DrawEvent(std::move(Other.DrawEvent))
|
|
||||||
, InputState(std::move(Other.InputState))
|
|
||||||
, DrawLists(std::move(Other.DrawLists))
|
|
||||||
, Name(std::move(Other.Name))
|
|
||||||
, IniFilename(std::move(Other.IniFilename))
|
|
||||||
{
|
|
||||||
Other.Context = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
FImGuiContextProxy& FImGuiContextProxy::operator=(FImGuiContextProxy&& Other)
|
|
||||||
{
|
|
||||||
// Swapping context so it can be destroyed with the other object.
|
|
||||||
using std::swap;
|
|
||||||
swap(Context, Other.Context);
|
|
||||||
|
|
||||||
// Just moving remaining data that doesn't affect cleanup.
|
|
||||||
bHasActiveItem = Other.bHasActiveItem;
|
|
||||||
DrawEvent = std::move(Other.DrawEvent);
|
|
||||||
InputState = std::move(Other.InputState);
|
|
||||||
DrawLists = std::move(Other.DrawLists);
|
|
||||||
Name = std::move(Other.Name);
|
|
||||||
IniFilename = std::move(Other.IniFilename);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
FImGuiContextProxy::~FImGuiContextProxy()
|
FImGuiContextProxy::~FImGuiContextProxy()
|
||||||
{
|
{
|
||||||
if (Context)
|
if (Context)
|
||||||
@ -118,17 +90,24 @@ FImGuiContextProxy::~FImGuiContextProxy()
|
|||||||
|
|
||||||
// Save context data and destroy.
|
// Save context data and destroy.
|
||||||
ImGuiImplementation::SaveCurrentContextIniSettings(IniFilename.c_str());
|
ImGuiImplementation::SaveCurrentContextIniSettings(IniFilename.c_str());
|
||||||
ImGui::DestroyContext(Context);
|
ImGui::DestroyContext(Context.Release());
|
||||||
|
|
||||||
// Set default context in ImGui to keep global context pointer valid.
|
// Set default context in ImGui to keep global context pointer valid.
|
||||||
ImGui::SetCurrentContext(&ImGuiImplementation::GetDefaultContext());
|
ImGui::SetCurrentContext(&ImGuiImplementation::GetDefaultContext());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FImGuiContextProxy::Tick(float DeltaSeconds, FSimpleMulticastDelegate* SharedDrawEvent)
|
void FImGuiContextProxy::Draw()
|
||||||
{
|
{
|
||||||
|
// Comparing to LastTickFrameNumber rather than GFrameNumber to make sure that this is driven by our own update cycle.
|
||||||
|
if (LastDrawFrameNumber < LastTickFrameNumber)
|
||||||
|
{
|
||||||
|
LastDrawFrameNumber = LastTickFrameNumber;
|
||||||
|
|
||||||
if (bIsFrameStarted)
|
if (bIsFrameStarted)
|
||||||
{
|
{
|
||||||
|
SetAsCurrent();
|
||||||
|
|
||||||
// Broadcast draw event to allow listeners to draw their controls to this context.
|
// Broadcast draw event to allow listeners to draw their controls to this context.
|
||||||
if (DrawEvent.IsBound())
|
if (DrawEvent.IsBound())
|
||||||
{
|
{
|
||||||
@ -138,6 +117,23 @@ void FImGuiContextProxy::Tick(float DeltaSeconds, FSimpleMulticastDelegate* Shar
|
|||||||
{
|
{
|
||||||
SharedDrawEvent->Broadcast();
|
SharedDrawEvent->Broadcast();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FImGuiContextProxy::Tick(float DeltaSeconds)
|
||||||
|
{
|
||||||
|
// Making sure that we tick only once per frame.
|
||||||
|
if (LastTickFrameNumber < GFrameNumber)
|
||||||
|
{
|
||||||
|
LastTickFrameNumber = GFrameNumber;
|
||||||
|
|
||||||
|
SetAsCurrent();
|
||||||
|
|
||||||
|
if (bIsFrameStarted)
|
||||||
|
{
|
||||||
|
// Make sure that draw events are called before the end of the frame.
|
||||||
|
Draw();
|
||||||
|
|
||||||
// Ending frame will produce render output that we capture and store for later use. This also puts context to
|
// 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.
|
// state in which it does not allow to draw controls, so we want to immediately start a new frame.
|
||||||
@ -151,6 +147,7 @@ void FImGuiContextProxy::Tick(float DeltaSeconds, FSimpleMulticastDelegate* Shar
|
|||||||
|
|
||||||
// Begin a new frame and set the context back to a state in which it allows to draw controls.
|
// Begin a new frame and set the context back to a state in which it allows to draw controls.
|
||||||
BeginFrame(DeltaSeconds);
|
BeginFrame(DeltaSeconds);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FImGuiContextProxy::BeginFrame(float DeltaTime)
|
void FImGuiContextProxy::BeginFrame(float DeltaTime)
|
||||||
|
@ -19,14 +19,14 @@ class FImGuiContextProxy
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
FImGuiContextProxy(const FString& Name);
|
FImGuiContextProxy(const FString& Name, FSimpleMulticastDelegate* InSharedDrawEvent);
|
||||||
~FImGuiContextProxy();
|
~FImGuiContextProxy();
|
||||||
|
|
||||||
FImGuiContextProxy(const FImGuiContextProxy&) = delete;
|
FImGuiContextProxy(const FImGuiContextProxy&) = delete;
|
||||||
FImGuiContextProxy& operator=(const FImGuiContextProxy&) = delete;
|
FImGuiContextProxy& operator=(const FImGuiContextProxy&) = delete;
|
||||||
|
|
||||||
FImGuiContextProxy(FImGuiContextProxy&& Other);
|
FImGuiContextProxy(FImGuiContextProxy&& Other) = default;
|
||||||
FImGuiContextProxy& operator=(FImGuiContextProxy&& Other);
|
FImGuiContextProxy& operator=(FImGuiContextProxy&& Other) = default;
|
||||||
|
|
||||||
// Get the name of this context.
|
// Get the name of this context.
|
||||||
const FString& GetName() const { return Name; }
|
const FString& GetName() const { return Name; }
|
||||||
@ -44,10 +44,10 @@ public:
|
|||||||
void RemoveInputState(const FImGuiInputState* InputStateToRemove) { if (InputState == InputStateToRemove) InputState = nullptr; }
|
void RemoveInputState(const FImGuiInputState* InputStateToRemove) { if (InputState == InputStateToRemove) InputState = nullptr; }
|
||||||
|
|
||||||
// Is this context the current ImGui context.
|
// Is this context the current ImGui context.
|
||||||
bool IsCurrentContext() const { return ImGui::GetCurrentContext() == Context; }
|
bool IsCurrentContext() const { return ImGui::GetCurrentContext() == Context.Get(); }
|
||||||
|
|
||||||
// Set this context as current ImGui context.
|
// Set this context as current ImGui context.
|
||||||
void SetAsCurrent() { ImGui::SetCurrentContext(Context); }
|
void SetAsCurrent() { ImGui::SetCurrentContext(Context.Get()); }
|
||||||
|
|
||||||
bool HasActiveItem() const { return bHasActiveItem; }
|
bool HasActiveItem() const { return bHasActiveItem; }
|
||||||
|
|
||||||
@ -56,9 +56,12 @@ public:
|
|||||||
// Delegate called right before ending the frame to allows listeners draw their controls.
|
// Delegate called right before ending the frame to allows listeners draw their controls.
|
||||||
FSimpleMulticastDelegate& OnDraw() { return DrawEvent; }
|
FSimpleMulticastDelegate& OnDraw() { return DrawEvent; }
|
||||||
|
|
||||||
// Tick to advance context to the next frame.
|
// Call draw events to allow listeners draw their widgets. Only one call per frame is processed. If it is not
|
||||||
// @param SharedDrawEvent - Shared draw event provided from outside to be called right after context own event
|
// called manually before, then it will be called from the Tick function.
|
||||||
void Tick(float DeltaSeconds, FSimpleMulticastDelegate* SharedDrawEvent = nullptr);
|
void Draw();
|
||||||
|
|
||||||
|
// Tick to advance context to the next frame. Only one call per frame will be processed.
|
||||||
|
void Tick(float DeltaSeconds);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -67,13 +70,18 @@ private:
|
|||||||
|
|
||||||
void UpdateDrawData(ImDrawData* DrawData);
|
void UpdateDrawData(ImDrawData* DrawData);
|
||||||
|
|
||||||
ImGuiContext* Context = nullptr;
|
TUniquePtr<ImGuiContext> Context;
|
||||||
|
|
||||||
bool bHasActiveItem = false;
|
|
||||||
EMouseCursor::Type MouseCursor = EMouseCursor::None;
|
EMouseCursor::Type MouseCursor = EMouseCursor::None;
|
||||||
|
bool bHasActiveItem = false;
|
||||||
|
|
||||||
bool bIsFrameStarted = false;
|
bool bIsFrameStarted = false;
|
||||||
FSimpleMulticastDelegate DrawEvent;
|
FSimpleMulticastDelegate DrawEvent;
|
||||||
|
FSimpleMulticastDelegate* SharedDrawEvent = nullptr;
|
||||||
|
|
||||||
|
uint32 LastTickFrameNumber = 0;
|
||||||
|
uint32 LastDrawFrameNumber = 0;
|
||||||
|
|
||||||
const FImGuiInputState* InputState = nullptr;
|
const FImGuiInputState* InputState = nullptr;
|
||||||
|
|
||||||
TArray<FImGuiDrawList> DrawLists;
|
TArray<FImGuiDrawList> DrawLists;
|
||||||
|
@ -17,13 +17,11 @@ namespace CVars
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Demo copied (with minor modifications) from ImGui examples. See https://github.com/ocornut/imgui.
|
// Demo copied (with minor modifications) from ImGui examples. See https://github.com/ocornut/imgui.
|
||||||
void FImGuiDemo::DrawControls()
|
void FImGuiDemo::DrawControls(int32 ContextIndex)
|
||||||
{
|
{
|
||||||
if (CVars::ShowDemo.GetValueOnGameThread() > 0)
|
if (CVars::ShowDemo.GetValueOnGameThread() > 0)
|
||||||
{
|
{
|
||||||
// TODO: This should be part of a public interface.
|
const int32 ContextBit = ContextIndex < 0 ? 0 : 1 << ContextIndex;
|
||||||
extern int32 CurrentContextIndex;
|
|
||||||
const int32 ContextBit = CurrentContextIndex < 0 ? 0 : 1 << CurrentContextIndex;
|
|
||||||
|
|
||||||
// 1. Show a simple window
|
// 1. Show a simple window
|
||||||
// Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets appears in a window automatically called "Debug"
|
// Tip: if we don't call ImGui::Begin()/ImGui::End() the widgets appears in a window automatically called "Debug"
|
||||||
@ -53,13 +51,28 @@ void FImGuiDemo::DrawControls()
|
|||||||
// 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowTestWindow()
|
// 3. Show the ImGui test window. Most of the sample code is in ImGui::ShowTestWindow()
|
||||||
if (ShowDemoWindowMask & ContextBit)
|
if (ShowDemoWindowMask & ContextBit)
|
||||||
{
|
{
|
||||||
// Display warning about running ImGui examples in multiple contexts.
|
// If more than one demo window is opened display warning about running ImGui examples in multiple contexts.
|
||||||
if (ShowDemoWindowMask != ContextBit)
|
|
||||||
|
// For everything, but the first windows in this frame we assume warning.
|
||||||
|
bool bWarning = true;
|
||||||
|
if (GFrameNumber > LastDemoWindowFrameNumber)
|
||||||
|
{
|
||||||
|
// If this is the first window in this frame, then we need to look at the last frame to see whether
|
||||||
|
// there were more than one windows. Higher frame distance automatically means that there were not.
|
||||||
|
bWarning = ((GFrameNumber - LastDemoWindowFrameNumber) == 1) && (DemoWindowCounter > 1);
|
||||||
|
|
||||||
|
LastDemoWindowFrameNumber = GFrameNumber;
|
||||||
|
DemoWindowCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DemoWindowCounter++;
|
||||||
|
|
||||||
|
if (bWarning)
|
||||||
{
|
{
|
||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Text, { 1.f, 1.f, 0.5f, 1.f });
|
ImGui::PushStyleColor(ImGuiCol_Text, { 1.f, 1.f, 0.5f, 1.f });
|
||||||
ImGui::TextWrapped("Demo Window is opend in more than one context, some of the ImGui examples may not work correctly.");
|
ImGui::TextWrapped("Demo Window is opened in more than one context, some of the ImGui examples may not work correctly.");
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
if (ImGui::IsItemHovered())
|
if (ImGui::IsItemHovered())
|
||||||
@ -70,6 +83,8 @@ void FImGuiDemo::DrawControls()
|
|||||||
"If you have a problem with an example try to run it in one context only.");
|
"If you have a problem with an example try to run it in one context only.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw demo window.
|
||||||
ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiSetCond_FirstUseEver);
|
ImGui::SetNextWindowPos(ImVec2(650, 20), ImGuiSetCond_FirstUseEver);
|
||||||
ImGui::ShowDemoWindow();
|
ImGui::ShowDemoWindow();
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ class FImGuiDemo
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void DrawControls();
|
void DrawControls(int32 ContextIndex);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
@ -18,4 +18,7 @@ private:
|
|||||||
|
|
||||||
int32 ShowDemoWindowMask = 0;
|
int32 ShowDemoWindowMask = 0;
|
||||||
int32 ShowAnotherWindowMask = 0;
|
int32 ShowAnotherWindowMask = 0;
|
||||||
|
|
||||||
|
int32 DemoWindowCounter = 0;
|
||||||
|
uint32 LastDemoWindowFrameNumber = 0;
|
||||||
};
|
};
|
||||||
|
@ -433,6 +433,10 @@ int32 SImGuiWidget::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeo
|
|||||||
{
|
{
|
||||||
if (FImGuiContextProxy* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex))
|
if (FImGuiContextProxy* ContextProxy = ModuleManager->GetContextManager().GetContextProxy(ContextIndex))
|
||||||
{
|
{
|
||||||
|
// Manually update ImGui context to minimise lag between creating and rendering ImGui output. This will also
|
||||||
|
// keep frame tearing at minimum because it is executed at the very end of the frame.
|
||||||
|
ContextProxy->Tick(FSlateApplication::Get().GetDeltaTime());
|
||||||
|
|
||||||
// Calculate offset that will transform vertex positions to screen space - rounded to avoid half pixel offsets.
|
// Calculate offset that will transform vertex positions to screen space - rounded to avoid half pixel offsets.
|
||||||
const FVector2D VertexPositionOffset{ FMath::RoundToFloat(MyClippingRect.Left), FMath::RoundToFloat(MyClippingRect.Top) };
|
const FVector2D VertexPositionOffset{ FMath::RoundToFloat(MyClippingRect.Left), FMath::RoundToFloat(MyClippingRect.Top) };
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user