Files
UnrealEngineUWP/Engine/Source/Runtime/PreLoadScreen/Private/PreLoadSlateThreading.cpp
nick darnell a8324a9e6a If the engine needs to exit, while the slate loading screen is running, stop processing new slate frames, everything is going to start shuttingdown and it wont be safe to continue to make draw requests.
#ROBOMERGE-SOURCE: CL 10499897 via CL 10499899 via CL 10499902 via CL 10499906
#ROBOMERGE-BOT: (v606-10482310)

[CL 10499911 by nick darnell in Main branch]
2019-12-03 15:09:56 -05:00

247 lines
7.4 KiB
C++

// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
#include "PreLoadSlateThreading.h"
#include "HAL/Runnable.h"
#include "HAL/RunnableThread.h"
#include "Misc/ScopeLock.h"
#include "Framework/Application/SlateApplication.h"
#include "HAL/PlatformApplicationMisc.h"
#include "HAL/PlatformAtomics.h"
#include "Rendering/SlateDrawBuffer.h"
#include "RenderingThread.h"
#include "PreLoadScreenManager.h"
FThreadSafeCounter FPreLoadScreenSlateSynchMechanism::LoadingThreadInstanceCounter;
bool FPreLoadScreenSlateThreadTask::Init()
{
// First thing to do is set the slate loading thread ID
// This guarantees all systems know that a slate thread exists
GSlateLoadingThreadId = FPlatformTLS::GetCurrentThreadId();
return true;
}
uint32 FPreLoadScreenSlateThreadTask::Run()
{
check(GSlateLoadingThreadId == FPlatformTLS::GetCurrentThreadId());
SyncMechanism->SlateThreadRunMainLoop();
// Tear down the slate loading thread ID
FPlatformAtomics::InterlockedExchange((int32*)&GSlateLoadingThreadId, 0);
return 0;
}
void FPreLoadScreenSlateThreadTask::Stop()
{
SyncMechanism->ResetSlateDrawPassEnqueued();
SyncMechanism->ResetSlateMainLoopRunning();
}
FPreLoadSlateWidgetRenderer::FPreLoadSlateWidgetRenderer(TSharedPtr<SWindow> InMainWindow, TSharedPtr<SVirtualWindow> InVirtualRenderWindow, FSlateRenderer* InRenderer)
: MainWindow(InMainWindow.Get())
, VirtualRenderWindow(InVirtualRenderWindow.ToSharedRef())
, SlateRenderer(InRenderer)
{
HittestGrid = MakeShareable(new FHittestGrid);
}
void FPreLoadSlateWidgetRenderer::DrawWindow(float DeltaTime)
{
// If the engine has requested exit, early out the draw loop, several
// of the things inside of here are not safe to perform while shutting down.
if (IsEngineExitRequested())
{
return;
}
if (GDynamicRHI && GDynamicRHI->RHIIsRenderingSuspended())
{
// This avoids crashes if we Suspend rendering whilst the loading screen is up
// as we don't want Slate to submit any more draw calls until we Resume.
return;
}
FVector2D DrawSize = VirtualRenderWindow->GetClientSizeInScreen();
FSlateApplication::Get().Tick(ESlateTickType::TimeOnly);
const float Scale = 1.0f;
FGeometry WindowGeometry = FGeometry::MakeRoot(DrawSize, FSlateLayoutTransform(Scale));
VirtualRenderWindow->SlatePrepass(WindowGeometry.Scale);
FSlateRect ClipRect = WindowGeometry.GetLayoutBoundingRect();
HittestGrid->SetHittestArea(VirtualRenderWindow->GetPositionInScreen(), VirtualRenderWindow->GetViewportSize());
HittestGrid->Clear();
// Get the free buffer & add our virtual window
FSlateDrawBuffer& DrawBuffer = SlateRenderer->GetDrawBuffer();
FSlateWindowElementList& WindowElementList = DrawBuffer.AddWindowElementList(VirtualRenderWindow);
WindowElementList.SetRenderTargetWindow(MainWindow);
int32 MaxLayerId = 0;
{
FPaintArgs PaintArgs(nullptr, *HittestGrid, FVector2D::ZeroVector, FSlateApplication::Get().GetCurrentTime(), FSlateApplication::Get().GetDeltaTime());
// Paint the window
MaxLayerId = VirtualRenderWindow->Paint(
PaintArgs,
WindowGeometry, ClipRect,
WindowElementList,
0,
FWidgetStyle(),
VirtualRenderWindow->IsEnabled());
}
SlateRenderer->DrawWindows(DrawBuffer);
DrawBuffer.ViewOffset = FVector2D::ZeroVector;
}
FPreLoadScreenSlateSynchMechanism::FPreLoadScreenSlateSynchMechanism(TSharedPtr<FPreLoadSlateWidgetRenderer, ESPMode::ThreadSafe> InWidgetRenderer)
: MainLoopCounter(0)
, WidgetRenderer(InWidgetRenderer)
{
}
FPreLoadScreenSlateSynchMechanism::~FPreLoadScreenSlateSynchMechanism()
{
DestroySlateThread();
}
void FPreLoadScreenSlateSynchMechanism::Initialize()
{
check(IsInGameThread());
ResetSlateDrawPassEnqueued();
SetSlateMainLoopRunning();
//Try to only spin up 1 Slate Loading Thread
int8 LoopRunningCount = FPlatformAtomics::InterlockedIncrement(&MainLoopCounter);
if (LoopRunningCount == 1)
{
FString ThreadName = TEXT("SlateLoadingThread");
ThreadName.AppendInt(LoadingThreadInstanceCounter.Increment());
SlateRunnableTask = new FPreLoadScreenSlateThreadTask(*this);
SlateLoadingThread = FRunnableThread::Create(SlateRunnableTask, *ThreadName);
}
}
void FPreLoadScreenSlateSynchMechanism::DestroySlateThread()
{
check(IsInGameThread());
if (SlateLoadingThread)
{
IsRunningSlateMainLoop.Reset();
while (FPlatformAtomics::AtomicRead(&MainLoopCounter) > 0)
{
FPlatformApplicationMisc::PumpMessages(false);
FPlatformProcess::Sleep(0.1f);
}
delete SlateLoadingThread;
delete SlateRunnableTask;
SlateLoadingThread = nullptr;
SlateRunnableTask = nullptr;
}
}
bool FPreLoadScreenSlateSynchMechanism::IsSlateDrawPassEnqueued()
{
return IsSlateDrawEnqueued.GetValue() != 0;
}
void FPreLoadScreenSlateSynchMechanism::SetSlateDrawPassEnqueued()
{
IsSlateDrawEnqueued.Set(1);
}
void FPreLoadScreenSlateSynchMechanism::ResetSlateDrawPassEnqueued()
{
IsSlateDrawEnqueued.Reset();
}
bool FPreLoadScreenSlateSynchMechanism::IsSlateMainLoopRunning()
{
return IsRunningSlateMainLoop.GetValue() != 0;
}
void FPreLoadScreenSlateSynchMechanism::SetSlateMainLoopRunning()
{
IsRunningSlateMainLoop.Set(1);
}
void FPreLoadScreenSlateSynchMechanism::ResetSlateMainLoopRunning()
{
IsRunningSlateMainLoop.Reset();
}
void FPreLoadScreenSlateSynchMechanism::SlateThreadRunMainLoop()
{
double LastTime = FPlatformTime::Seconds();
while (IsSlateMainLoopRunning())
{
double CurrentTime = FPlatformTime::Seconds();
double DeltaTime = CurrentTime - LastTime;
// 60 fps max
const double MaxTickRate = 1.0 / 60.0f;
const double TimeToWait = MaxTickRate - DeltaTime;
if (TimeToWait > 0)
{
FPlatformProcess::Sleep(TimeToWait);
CurrentTime = FPlatformTime::Seconds();
DeltaTime = CurrentTime - LastTime;
}
if (FSlateApplication::IsInitialized() && !IsSlateDrawPassEnqueued() && FPreLoadScreenManager::ShouldRender())
{
FSlateRenderer* MainSlateRenderer = FSlateApplication::Get().GetRenderer();
FScopeLock ScopeLock(MainSlateRenderer->GetResourceCriticalSection());
//Don't queue up a draw pass if our main loop is shutting down
if (IsSlateMainLoopRunning())
{
WidgetRenderer->DrawWindow(DeltaTime);
SetSlateDrawPassEnqueued();
}
//Queue up a render tick every time we tick on this sync thread.
FPreLoadScreenSlateSynchMechanism* SyncMech = this;
ENQUEUE_RENDER_COMMAND(PreLoadScreenRenderTick)(
[SyncMech](FRHICommandListImmediate& RHICmdList)
{
FPreLoadScreenManager* PreLoadManager = FPreLoadScreenManager::Get();
if (PreLoadManager && FPreLoadScreenManager::ShouldRender())
{
FPreLoadScreenManager::Get()->RenderTick();
}
SyncMech->ResetSlateDrawPassEnqueued();
}
);
}
LastTime = CurrentTime;
}
while (IsSlateDrawPassEnqueued())
{
FPlatformProcess::Sleep(0.1f);
}
FPlatformAtomics::InterlockedDecrement(&MainLoopCounter);
}