You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
--- Keep track on if we are locked similar to slate main rendering, also keep track of locking thread (Not used yet) Don't lock on flush end if we weren't locking before Surround 2x main render commands w/ 'UE::RenderCommandPipe::FSyncScope' (To serialize commands in addition to manual sync call). Minor: Replace existing manual unlock -> lock with 'FScopeUnlock' #rb Justin.Marcus [CL 28599192 by daren cheng in ue5-main branch]
816 lines
26 KiB
C++
816 lines
26 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "PreLoadScreenManager.h"
|
|
|
|
#include "Engine/GameEngine.h"
|
|
#include "Framework/Application/SlateApplication.h"
|
|
#include "GlobalShader.h"
|
|
#include "ShaderCompiler.h"
|
|
|
|
#include "PreLoadScreen.h"
|
|
#include "PreLoadSettingsContainer.h"
|
|
|
|
#include "HAL/ThreadManager.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "RenderingThread.h"
|
|
|
|
#if BUILD_EMBEDDED_APP
|
|
#include "Misc/EmbeddedCommunication.h"
|
|
#endif
|
|
|
|
#if PLATFORM_ANDROID
|
|
#if USE_ANDROID_EVENTS
|
|
#include "Android/AndroidEventManager.h"
|
|
#endif
|
|
#endif
|
|
|
|
IMPLEMENT_MODULE(FDefaultModuleImpl, PreLoadScreen);
|
|
|
|
DEFINE_LOG_CATEGORY_STATIC(LogPreLoadScreenManager, Log, All);
|
|
|
|
TUniquePtr<FPreLoadScreenManager, FPreLoadScreenManager::FPreLoadScreenManagerDelete> FPreLoadScreenManager::Instance;
|
|
FCriticalSection FPreLoadScreenManager::AcquireCriticalSection;
|
|
TAtomic<bool> FPreLoadScreenManager::bRenderingEnabled(true);
|
|
TAtomic<bool> FPreLoadScreenManager::bIsLocked(false);
|
|
TAtomic<bool> FPreLoadScreenManager::bIsLockedByGameThread(false);
|
|
TAtomic<bool> FPreLoadScreenManager::bIsLockedByRenderThread(false);
|
|
|
|
FPreLoadScreenManager* FPreLoadScreenManager::Get()
|
|
{
|
|
return Instance.Get();
|
|
}
|
|
|
|
void FPreLoadScreenManager::Create()
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
// Lock in case a user decide to create/detroy the manager multiple times
|
|
FScopeLock Lock(&AcquireCriticalSection);
|
|
TGuardValue LockedGuard(bIsLocked, true);
|
|
TGuardValue LockedGuardGameThread(bIsLockedByGameThread, true);
|
|
|
|
if (!Instance.IsValid() && ArePreLoadScreensEnabled())
|
|
{
|
|
Instance = TUniquePtr<FPreLoadScreenManager, FPreLoadScreenManager::FPreLoadScreenManagerDelete>(new FPreLoadScreenManager);
|
|
}
|
|
}
|
|
|
|
void FPreLoadScreenManager::Destroy()
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
FScopeLock Lock(&AcquireCriticalSection); // Make sure the render thread is completed before cleaning up
|
|
TGuardValue LockedGuard(bIsLocked, true);
|
|
TGuardValue LockedGuardGameThread(bIsLockedByGameThread, true);
|
|
|
|
if (Instance.IsValid())
|
|
{
|
|
Instance->CleanUpResources();
|
|
Instance.Reset();
|
|
}
|
|
}
|
|
|
|
FPreLoadScreenManager::FPreLoadScreenManager()
|
|
: ActivePreLoadScreenIndex(-1)
|
|
, LastTickTime(0.0)
|
|
, bInitialized(false)
|
|
, SyncMechanism(nullptr)
|
|
, bIsResponsibleForRendering(false)
|
|
, bHasRenderPreLoadScreenFrame_RenderThread(false)
|
|
, LastRenderTickTime(0.0)
|
|
, OriginalSlateSleepVariableValue(0.f)
|
|
, bIsEngineLoadingComplete(false)
|
|
{}
|
|
|
|
void FPreLoadScreenManager::Initialize(FSlateRenderer& InSlateRenderer)
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
if (bInitialized || !ArePreLoadScreensEnabled())
|
|
{
|
|
return;
|
|
}
|
|
|
|
bInitialized = true;
|
|
|
|
// Initialize shaders, because otherwise they might not be guaranteed to exist at this point
|
|
if (!FPlatformProperties::RequiresCookedData() && GShaderCompilingManager)
|
|
{
|
|
TArray<int32> ShaderMapIds;
|
|
ShaderMapIds.Add(GlobalShaderMapId);
|
|
GShaderCompilingManager->FinishCompilation(TEXT("Global"), ShaderMapIds);
|
|
}
|
|
|
|
if (FApp::CanEverRender())
|
|
{
|
|
// Make sure we haven't created a game window already, if so use that. If not make a new one
|
|
UGameEngine* GameEngine = Cast<UGameEngine>(GEngine);
|
|
TSharedRef<SWindow> GameWindow = (GameEngine && GameEngine->GameViewportWindow.IsValid()) ? GameEngine->GameViewportWindow.Pin().ToSharedRef() : UGameEngine::CreateGameWindow();
|
|
|
|
VirtualRenderWindow =
|
|
SNew(SVirtualWindow)
|
|
.Size(GameWindow->GetClientSizeInScreen());
|
|
|
|
MainWindow = GameWindow;
|
|
|
|
WidgetRenderer = MakeShared<FPreLoadSlateWidgetRenderer, ESPMode::ThreadSafe>(GameWindow, VirtualRenderWindow, &InSlateRenderer);
|
|
}
|
|
|
|
LastRenderTickTime = FPlatformTime::Seconds();
|
|
LastTickTime = FPlatformTime::Seconds();
|
|
}
|
|
|
|
void FPreLoadScreenManager::RegisterPreLoadScreen(const TSharedPtr<IPreLoadScreen>& PreLoadScreen)
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
PreLoadScreens.Add(PreLoadScreen);
|
|
}
|
|
|
|
void FPreLoadScreenManager::UnRegisterPreLoadScreen(const TSharedPtr<IPreLoadScreen>& PreLoadScreen)
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
FScopeLock Lock(&AcquireCriticalSection);
|
|
TGuardValue LockedGuard(bIsLocked, true);
|
|
TGuardValue LockedGuardGameThread(bIsLockedByGameThread, true);
|
|
|
|
if (PreLoadScreen.IsValid())
|
|
{
|
|
const int32 IndexOf = PreLoadScreens.IndexOfByKey(PreLoadScreen);
|
|
if (PreLoadScreens.IsValidIndex(IndexOf))
|
|
{
|
|
if (IndexOf == ActivePreLoadScreenIndex)
|
|
{
|
|
ensureMsgf(false, TEXT("Can't remove an active preloadscreen."));
|
|
return;
|
|
}
|
|
|
|
TSharedPtr<IPreLoadScreen> PreviousActivePreLoadScreen = PreLoadScreens.IsValidIndex(ActivePreLoadScreenIndex)
|
|
? PreLoadScreens[ActivePreLoadScreenIndex] : nullptr;
|
|
|
|
PreLoadScreen->CleanUp();
|
|
PreLoadScreens.RemoveAtSwap(IndexOf);
|
|
|
|
if (PreviousActivePreLoadScreen)
|
|
{
|
|
ActivePreLoadScreenIndex = PreLoadScreens.IndexOfByKey(PreviousActivePreLoadScreen);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FPreLoadScreenManager::PlayFirstPreLoadScreen(EPreLoadScreenTypes PreLoadScreenTypeToPlay)
|
|
{
|
|
for (int32 PreLoadScreenIndex = 0; PreLoadScreenIndex < PreLoadScreens.Num(); ++PreLoadScreenIndex)
|
|
{
|
|
if (PreLoadScreens[PreLoadScreenIndex]->GetPreLoadScreenType() == PreLoadScreenTypeToPlay)
|
|
{
|
|
PlayPreLoadScreenAtIndex(PreLoadScreenIndex);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FPreLoadScreenManager::PlayPreLoadScreenAtIndex(int32 Index)
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
if (ArePreLoadScreensEnabled())
|
|
{
|
|
FScopeLock Lock(&AcquireCriticalSection);
|
|
TGuardValue LockedGuard(bIsLocked, true);
|
|
TGuardValue LockedGuardGameThread(bIsLockedByGameThread, true);
|
|
|
|
if (ensureAlwaysMsgf(!HasValidActivePreLoadScreen(), TEXT("Call to FPreLoadScreenManager::PlayPreLoadScreenAtIndex when something is already playing.")))
|
|
{
|
|
ActivePreLoadScreenIndex = Index;
|
|
if (ensureAlwaysMsgf(HasValidActivePreLoadScreen(), TEXT("Call to FPreLoadScreenManager::PlayPreLoadScreenAtIndex with an invalid index! Nothing will play!")))
|
|
{
|
|
IPreLoadScreen* ActiveScreen = GetActivePreLoadScreen();
|
|
if (ActiveScreen->GetPreLoadScreenType() == EPreLoadScreenTypes::EarlyStartupScreen)
|
|
{
|
|
HandleEarlyStartupPlay();
|
|
}
|
|
else if (ActiveScreen->GetPreLoadScreenType() == EPreLoadScreenTypes::EngineLoadingScreen)
|
|
{
|
|
HandleEngineLoadingPlay();
|
|
}
|
|
else if (ActiveScreen->GetPreLoadScreenType() == EPreLoadScreenTypes::CustomSplashScreen)
|
|
{
|
|
HandleCustomSplashScreenPlay();
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogPreLoadScreenManager, Fatal, TEXT("Attempting to play an Active PreLoadScreen type that hasn't been implemented inside of PreLoadScreenmanager!"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ActivePreLoadScreenIndex = INDEX_NONE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FPreLoadScreenManager::PlayPreLoadScreenWithTag(FName InTag)
|
|
{
|
|
for (int32 PreLoadScreenIndex = 0; PreLoadScreenIndex < PreLoadScreens.Num(); ++PreLoadScreenIndex)
|
|
{
|
|
if (PreLoadScreens[PreLoadScreenIndex]->GetPreLoadScreenTag() == InTag)
|
|
{
|
|
PlayPreLoadScreenAtIndex(PreLoadScreenIndex);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void FPreLoadScreenManager::HandleEarlyStartupPlay()
|
|
{
|
|
if (ensureAlwaysMsgf(HasActivePreLoadScreenType(EPreLoadScreenTypes::EarlyStartupScreen), TEXT("Invalid Active PreLoadScreen!")))
|
|
{
|
|
IPreLoadScreen* PreLoadScreen = GetActivePreLoadScreen();
|
|
if (PreLoadScreen)
|
|
{
|
|
SCOPED_BOOT_TIMING("FPreLoadScreenManager::HandleEarlyStartupPlay()");
|
|
|
|
PreLoadScreen->OnPlay(MainWindow);
|
|
|
|
{
|
|
TSharedPtr<SWindow> MainWindowPtr = MainWindow.Pin();
|
|
if (MainWindowPtr.IsValid() && PreLoadScreen->GetWidget().IsValid())
|
|
{
|
|
MainWindowPtr->SetContent(PreLoadScreen->GetWidget().ToSharedRef());
|
|
}
|
|
}
|
|
|
|
bool bDidDisableScreensaver = false;
|
|
if (FPlatformApplicationMisc::IsScreensaverEnabled())
|
|
{
|
|
bDidDisableScreensaver = FPlatformApplicationMisc::ControlScreensaver(FGenericPlatformApplicationMisc::EScreenSaverAction::Disable);
|
|
}
|
|
|
|
FPlatformMisc::HidePlatformStartupScreen();
|
|
|
|
RegisterDelegatesForEarlyStartupPlay();
|
|
|
|
{
|
|
SCOPED_BOOT_TIMING("FPreLoadScreenManager::EarlyPlayFrameTick()");
|
|
|
|
//We run this PreLoadScreen until its finished or we lose the MainWindow as EarlyPreLoadPlay is synchronous
|
|
while (!PreLoadScreen->IsDone())
|
|
{
|
|
EarlyPlayFrameTick();
|
|
}
|
|
}
|
|
|
|
CleanUpDelegatesForEarlyStartupPlay();
|
|
|
|
if (bDidDisableScreensaver)
|
|
{
|
|
FPlatformApplicationMisc::ControlScreensaver(FGenericPlatformApplicationMisc::EScreenSaverAction::Enable);
|
|
}
|
|
|
|
StopPreLoadScreen();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FPreLoadScreenManager::RegisterDelegatesForEarlyStartupPlay()
|
|
{
|
|
//Have to register to handle FlushRenderingCommands for the length of the PreLoadScreen
|
|
FCoreRenderDelegates::OnFlushRenderingCommandsStart.AddRaw(this, &FPreLoadScreenManager::HandleFlushRenderingCommandsStart);
|
|
FCoreRenderDelegates::OnFlushRenderingCommandsEnd.AddRaw(this, &FPreLoadScreenManager::HandleFlushRenderingCommandsEnd);
|
|
}
|
|
|
|
void FPreLoadScreenManager::CleanUpDelegatesForEarlyStartupPlay()
|
|
{
|
|
FCoreRenderDelegates::OnFlushRenderingCommandsStart.RemoveAll(this);
|
|
FCoreRenderDelegates::OnFlushRenderingCommandsEnd.RemoveAll(this);
|
|
}
|
|
|
|
void FPreLoadScreenManager::HandleEngineLoadingPlay()
|
|
{
|
|
if (ensureAlwaysMsgf(HasActivePreLoadScreenType(EPreLoadScreenTypes::EngineLoadingScreen), TEXT("Invalid Active PreLoadScreen!")))
|
|
{
|
|
IPreLoadScreen* PreLoadScreen = GetActivePreLoadScreen();
|
|
if (PreLoadScreen)
|
|
{
|
|
PreLoadScreen->OnPlay(MainWindow.Pin());
|
|
|
|
if (PreLoadScreen->GetWidget().IsValid() && VirtualRenderWindow.IsValid())
|
|
{
|
|
VirtualRenderWindow->SetContent(PreLoadScreen->GetWidget().ToSharedRef());
|
|
}
|
|
|
|
//Need to update bIsResponsibleForRendering as a PreLoadScreen may not have updated it before this point
|
|
if (!bIsResponsibleForRendering && PreLoadScreen->ShouldRender())
|
|
{
|
|
bIsResponsibleForRendering = true;
|
|
IsResponsibleForRenderingDelegate.Broadcast(bIsResponsibleForRendering);
|
|
}
|
|
}
|
|
|
|
if (WidgetRenderer.IsValid())
|
|
{
|
|
if (ensure(SyncMechanism == nullptr))
|
|
{
|
|
SyncMechanism = new FPreLoadScreenSlateSynchMechanism(WidgetRenderer);
|
|
SyncMechanism->Initialize();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FPreLoadScreenManager::HandleCustomSplashScreenPlay()
|
|
{
|
|
if (ensureAlwaysMsgf(HasActivePreLoadScreenType(EPreLoadScreenTypes::CustomSplashScreen), TEXT("Invalid Active PreLoadScreen!")))
|
|
{
|
|
IPreLoadScreen* PreLoadScreen = GetActivePreLoadScreen();
|
|
if (PreLoadScreen && MainWindow.IsValid())
|
|
{
|
|
SCOPED_BOOT_TIMING("FPreLoadScreenManager::HandleCustomSplashScreenPlay()");
|
|
|
|
PreLoadScreen->OnPlay(MainWindow.Pin());
|
|
|
|
if (PreLoadScreen->GetWidget().IsValid())
|
|
{
|
|
MainWindow.Pin()->SetContent(PreLoadScreen->GetWidget().ToSharedRef());
|
|
}
|
|
|
|
bool bDidDisableScreensaver = false;
|
|
if (FPlatformApplicationMisc::IsScreensaverEnabled())
|
|
{
|
|
bDidDisableScreensaver = FPlatformApplicationMisc::ControlScreensaver(FGenericPlatformApplicationMisc::EScreenSaverAction::Disable);
|
|
}
|
|
|
|
FPlatformMisc::HidePlatformStartupScreen();
|
|
FPlatformMisc::PlatformHandleSplashScreen(false);
|
|
|
|
while (!PreLoadScreen->IsDone())
|
|
{
|
|
EarlyPlayFrameTick();
|
|
}
|
|
|
|
if (bDidDisableScreensaver)
|
|
{
|
|
FPlatformApplicationMisc::ControlScreensaver(FGenericPlatformApplicationMisc::EScreenSaverAction::Enable);
|
|
}
|
|
|
|
StopPreLoadScreen();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FPreLoadScreenManager::StaticRenderTick_RenderThread()
|
|
{
|
|
LLM_SCOPE(ELLMTag::RenderingThreadMemory);
|
|
check(IsInRenderingThread());
|
|
|
|
FScopeLock Lock(&AcquireCriticalSection);
|
|
TGuardValue LockedGuard(bIsLocked, true);
|
|
TGuardValue LockedGuardGameThread(bIsLockedByRenderThread, true);
|
|
|
|
if (ensure(FPreLoadScreenManager::Get())) // The manager should clear the slate render thread before closing
|
|
{
|
|
FPreLoadScreenManager::Get()->RenderTick_RenderThread();
|
|
}
|
|
}
|
|
|
|
void FPreLoadScreenManager::RenderTick_RenderThread()
|
|
{
|
|
//Calculate tick time
|
|
const double CurrentTime = FPlatformTime::Seconds();
|
|
double DeltaTime = CurrentTime - LastRenderTickTime;
|
|
LastRenderTickTime = CurrentTime;
|
|
|
|
//Check if we have an active index before doing any work
|
|
if (HasValidActivePreLoadScreen() && bRenderingEnabled)
|
|
{
|
|
IPreLoadScreen* PreLoadScreen = GetActivePreLoadScreen();
|
|
|
|
check(PreLoadScreen && IsInRenderingThread());
|
|
if (MainWindow.IsValid() && VirtualRenderWindow.IsValid() && !PreLoadScreen->IsDone())
|
|
{
|
|
GFrameNumberRenderThread++;
|
|
GRHICommandList.GetImmediateCommandList().BeginFrame();
|
|
PreLoadScreen->RenderTick(DeltaTime);
|
|
GRHICommandList.GetImmediateCommandList().EndFrame();
|
|
GRHICommandList.GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::FlushRHIThreadFlushResources);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FPreLoadScreenManager::HasRegisteredPreLoadScreenType(EPreLoadScreenTypes PreLoadScreenTypeToCheck) const
|
|
{
|
|
bool HasMatchingRegisteredScreen = false;
|
|
for (const TSharedPtr<IPreLoadScreen>& Screen : PreLoadScreens)
|
|
{
|
|
if (Screen.IsValid() && (Screen->GetPreLoadScreenType() == PreLoadScreenTypeToCheck))
|
|
{
|
|
HasMatchingRegisteredScreen = true;
|
|
}
|
|
}
|
|
|
|
return HasMatchingRegisteredScreen;
|
|
}
|
|
|
|
bool FPreLoadScreenManager::HasActivePreLoadScreenType(EPreLoadScreenTypes PreLoadScreenTypeToCheck) const
|
|
{
|
|
return (HasValidActivePreLoadScreen() && (GetActivePreLoadScreen()->GetPreLoadScreenType() == PreLoadScreenTypeToCheck));
|
|
}
|
|
|
|
bool FPreLoadScreenManager::HasValidActivePreLoadScreen() const
|
|
{
|
|
IPreLoadScreen* PreLoadScreen = nullptr;
|
|
return (PreLoadScreens.IsValidIndex(ActivePreLoadScreenIndex) && PreLoadScreens[ActivePreLoadScreenIndex].IsValid());
|
|
}
|
|
|
|
IPreLoadScreen* FPreLoadScreenManager::GetActivePreLoadScreen()
|
|
{
|
|
return HasValidActivePreLoadScreen() ? PreLoadScreens[ActivePreLoadScreenIndex].Get() : nullptr;
|
|
}
|
|
|
|
const IPreLoadScreen* FPreLoadScreenManager::GetActivePreLoadScreen() const
|
|
{
|
|
return HasValidActivePreLoadScreen() ? PreLoadScreens[ActivePreLoadScreenIndex].Get() : nullptr;
|
|
}
|
|
|
|
bool FPreLoadScreenManager::HasActivePreLoadScreenTypeForEarlyStartup() const
|
|
{
|
|
return HasActivePreLoadScreenType(EPreLoadScreenTypes::EarlyStartupScreen) || HasActivePreLoadScreenType(EPreLoadScreenTypes::CustomSplashScreen);
|
|
}
|
|
|
|
void FPreLoadScreenManager::EarlyPlayFrameTick()
|
|
{
|
|
if (ensureAlwaysMsgf(HasActivePreLoadScreenTypeForEarlyStartup(), TEXT("EarlyPlayFrameTick called without a valid EarlyPreLoadScreen!")))
|
|
{
|
|
GameLogicFrameTick();
|
|
EarlyPlayRenderFrameTick();
|
|
}
|
|
}
|
|
|
|
void FPreLoadScreenManager::GameLogicFrameTick()
|
|
{
|
|
IPreLoadScreen* ActivePreLoadScreen = GetActivePreLoadScreen();
|
|
if (ensureAlwaysMsgf(ActivePreLoadScreen, TEXT("Invalid Active PreLoadScreen during GameLogicFameTick!")))
|
|
{
|
|
//First spin the platform by having it sleep a bit
|
|
const float SleepTime = ActivePreLoadScreen ? ActivePreLoadScreen->GetAddedTickDelay() : 0.f;
|
|
if (SleepTime > 0)
|
|
{
|
|
FPlatformProcess::Sleep(SleepTime);
|
|
}
|
|
|
|
double CurrentTime = FPlatformTime::Seconds();
|
|
double DeltaTime = CurrentTime - LastTickTime;
|
|
LastTickTime = CurrentTime;
|
|
|
|
//Clamp to what should be more then any max reasonable time. This is to help with cases of
|
|
//backgrounding or setting breakpoints to trigger huge ticks
|
|
const double MaxTickTime = 5.0;
|
|
DeltaTime = FMath::Min(DeltaTime, MaxTickTime);
|
|
|
|
//We have to manually tick everything as we are looping the main thread here
|
|
FTaskGraphInterface::Get().ProcessThreadUntilIdle(ENamedThreads::GameThread);
|
|
FTSTicker::GetCoreTicker().Tick(DeltaTime);
|
|
FThreadManager::Get().Tick();
|
|
|
|
//Tick any platform specific things we need here
|
|
PlatformSpecificGameLogicFrameTick();
|
|
|
|
//Tick the Active Screen
|
|
ActivePreLoadScreen->Tick(DeltaTime);
|
|
|
|
// Pump messages to handle input , etc from system
|
|
FPlatformApplicationMisc::PumpMessages(true);
|
|
|
|
FSlateApplication::Get().PollGameDeviceState();
|
|
// Gives widgets a chance to process any accumulated input
|
|
FSlateApplication::Get().FinishedInputThisFrame();
|
|
|
|
FSlateApplication::Get().GetPlatformApplication()->Tick(DeltaTime);
|
|
|
|
//Needed as this won't be incrementing on its own and some other tick functions rely on this (like analytics)
|
|
GFrameCounter++;
|
|
}
|
|
}
|
|
|
|
void FPreLoadScreenManager::PlatformSpecificGameLogicFrameTick()
|
|
{
|
|
#if PLATFORM_ANDROID
|
|
Android_PlatformSpecificGameLogicFrameTick();
|
|
#endif //PLATFORM_ANDROID
|
|
|
|
#if PLATFORM_IOS
|
|
IOS_PlatformSpecificGameLogicFrameTick();
|
|
#endif //PLATFORM_IOS
|
|
}
|
|
|
|
void FPreLoadScreenManager::EnableRendering(bool bEnabled)
|
|
{
|
|
FScopeLock ScopeLock(&AcquireCriticalSection);
|
|
TGuardValue LockedGuard(bIsLocked, true);
|
|
TGuardValue LockedGuardGameThread(bIsLockedByGameThread, IsInGameThread());
|
|
TGuardValue LockedGuardRenderThread(bIsLockedByGameThread, IsInRenderingThread());
|
|
|
|
bRenderingEnabled = bEnabled;
|
|
}
|
|
|
|
void FPreLoadScreenManager::EarlyPlayRenderFrameTick()
|
|
{
|
|
bool bIsResponsibleForRendering_Local = true;
|
|
|
|
if (!bRenderingEnabled || !FSlateApplication::IsInitialized())
|
|
{
|
|
// If rendering disabled, FPreLoadScreenManager is responsible for rendering but choosing not to, probably because the
|
|
// app is not in the foreground.
|
|
|
|
// Cycle lock to give a chance to another thread to re-enable rendering.
|
|
{
|
|
FScopeUnlock ScopeUnlock(&AcquireCriticalSection);
|
|
TGuardValue LockedGuard(bIsLocked, false);
|
|
TGuardValue LockedGuardGameThread(bIsLockedByGameThread, false);
|
|
TGuardValue LockedGuardRenderThread(bIsLockedByGameThread, false);
|
|
FPlatformProcess::Sleep(0);
|
|
}
|
|
return;
|
|
}
|
|
|
|
IPreLoadScreen* ActivePreLoadScreen = PreLoadScreens[ActivePreLoadScreenIndex].Get();
|
|
if (ensureAlwaysMsgf(ActivePreLoadScreen, TEXT("Invalid Active PreLoadScreen during EarlyPlayRenderFrameTick!")))
|
|
{
|
|
if (!ActivePreLoadScreen->ShouldRender())
|
|
{
|
|
bIsResponsibleForRendering_Local = false;
|
|
}
|
|
|
|
if (bIsResponsibleForRendering_Local != bIsResponsibleForRendering)
|
|
{
|
|
bIsResponsibleForRendering = bIsResponsibleForRendering_Local;
|
|
IsResponsibleForRenderingDelegate.Broadcast(bIsResponsibleForRendering);
|
|
}
|
|
|
|
if (bIsResponsibleForRendering_Local)
|
|
{
|
|
FSlateApplication& SlateApp = FSlateApplication::Get();
|
|
float SlateDeltaTime = SlateApp.GetDeltaTime();
|
|
FPreLoadScreenManager* Self = this;
|
|
|
|
{
|
|
UE::RenderCommandPipe::FSyncScope SyncScope;
|
|
|
|
//Setup Slate Render Command
|
|
ENQUEUE_RENDER_COMMAND(BeginPreLoadScreenFrame)(
|
|
[Self, SlateDeltaTime](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
FScopeLock Lock(&FPreLoadScreenManager::AcquireCriticalSection);
|
|
TGuardValue LockedGuard(bIsLocked, true);
|
|
TGuardValue LockedGuardRenderThread(bIsLockedByGameThread, true);
|
|
|
|
// Self is still valid because we do a FlushRenderingCommands in StopPreLoadScreen
|
|
if (Self->bRenderingEnabled && Self->HasActivePreLoadScreenTypeForEarlyStartup() && !Self->bHasRenderPreLoadScreenFrame_RenderThread)
|
|
{
|
|
GFrameNumberRenderThread++;
|
|
GRHICommandList.GetImmediateCommandList().BeginFrame();
|
|
|
|
Self->bHasRenderPreLoadScreenFrame_RenderThread = true;
|
|
IPreLoadScreen* ActivePreLoadScreen = Self->PreLoadScreens[Self->ActivePreLoadScreenIndex].Get();
|
|
ActivePreLoadScreen->RenderTick(SlateDeltaTime);
|
|
}
|
|
});
|
|
}
|
|
|
|
{
|
|
FScopeUnlock ScopeUnlock(&AcquireCriticalSection);
|
|
TGuardValue LockedGuard(bIsLocked, false);
|
|
TGuardValue LockedGuardGameThread(bIsLockedByGameThread, false);
|
|
TGuardValue LockedGuardRenderThread(bIsLockedByGameThread, false);
|
|
SlateApp.Tick();
|
|
|
|
// Synchronize the game thread and the render thread so that the render thread doesn't get too far behind.
|
|
SlateApp.GetRenderer()->Sync();
|
|
}
|
|
|
|
{
|
|
UE::RenderCommandPipe::FSyncScope SyncScope;
|
|
|
|
ENQUEUE_RENDER_COMMAND(FinishPreLoadScreenFrame)(
|
|
[Self](FRHICommandListImmediate& RHICmdList)
|
|
{
|
|
// Self is still valid because we do a FlushRenderingCommands in StopPreLoadScreen
|
|
Self->bHasRenderPreLoadScreenFrame_RenderThread = false;
|
|
GRHICommandList.GetImmediateCommandList().EndFrame();
|
|
GRHICommandList.GetImmediateCommandList().ImmediateFlush(EImmediateFlushType::FlushRHIThreadFlushResources);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void FPreLoadScreenManager::StopPreLoadScreen()
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
if (HasValidActivePreLoadScreen())
|
|
{
|
|
if (ensureMsgf(HasActivePreLoadScreenTypeForEarlyStartup(), TEXT("WaitForEngineLoadingScreenToFinish should be called when using an EngineLoadingScreen.")))
|
|
{
|
|
HandleStopPreLoadScreen();
|
|
}
|
|
|
|
{
|
|
FScopeUnlock ScopeUnlock(&AcquireCriticalSection);
|
|
TGuardValue LockedGuard(bIsLocked, false);
|
|
TGuardValue LockedGuardGameThread(bIsLockedByGameThread, false);
|
|
TGuardValue LockedGuardRenderThread(bIsLockedByGameThread, false);
|
|
FlushRenderingCommands();
|
|
}
|
|
}
|
|
}
|
|
|
|
void FPreLoadScreenManager::HandleStopPreLoadScreen()
|
|
{
|
|
{
|
|
FScopeLock Lock(&AcquireCriticalSection); // prevent stop while we are rendering the preloadscreen
|
|
TGuardValue LockedGuard(bIsLocked, true);
|
|
TGuardValue LockedGuardGameThread(bIsLockedByGameThread, IsInGameThread());
|
|
TGuardValue LockedGuardRenderThread(bIsLockedByGameThread, IsInRenderingThread());
|
|
|
|
if (HasValidActivePreLoadScreen())
|
|
{
|
|
PreLoadScreens[ActivePreLoadScreenIndex]->OnStop();
|
|
}
|
|
|
|
ActivePreLoadScreenIndex = -1;
|
|
|
|
//Clear our window content
|
|
if (MainWindow.IsValid())
|
|
{
|
|
MainWindow.Pin()->SetContent(SNullWidget::NullWidget);
|
|
}
|
|
if (VirtualRenderWindow.IsValid())
|
|
{
|
|
VirtualRenderWindow->SetContent(SNullWidget::NullWidget);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FPreLoadScreenManager::PassPreLoadScreenWindowBackToGame() const
|
|
{
|
|
if (IsUsingMainWindow())
|
|
{
|
|
FScopeLock Lock(&AcquireCriticalSection); // wait until we finish with rendering before moving the context
|
|
TGuardValue LockedGuard(bIsLocked, true);
|
|
TGuardValue LockedGuardGameThread(bIsLockedByGameThread, IsInGameThread());
|
|
TGuardValue LockedGuardRenderThread(bIsLockedByGameThread, IsInRenderingThread());
|
|
|
|
UGameEngine* GameEngine = Cast<UGameEngine>(GEngine);
|
|
if (MainWindow.IsValid() && GameEngine)
|
|
{
|
|
GameEngine->GameViewportWindow = MainWindow;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogPreLoadScreenManager, Warning, TEXT("FPreLoadScreenManager::PassLoadingScreenWindowBackToGame failed. No Window"));
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FPreLoadScreenManager::IsUsingMainWindow() const
|
|
{
|
|
return MainWindow.IsValid();
|
|
}
|
|
|
|
TSharedPtr<SWindow> FPreLoadScreenManager::GetRenderWindow()
|
|
{
|
|
return MainWindow.IsValid() ? MainWindow.Pin() : nullptr;
|
|
}
|
|
|
|
void FPreLoadScreenManager::WaitForEngineLoadingScreenToFinish()
|
|
{
|
|
check(IsInGameThread());
|
|
|
|
//Start just doing game logic ticks until the Screen is finished.
|
|
//Since this is a non-early screen, rendering happens separately still on the Slate rendering thread, so only need
|
|
//the game logic ticks
|
|
if (HasActivePreLoadScreenType(EPreLoadScreenTypes::EngineLoadingScreen))
|
|
{
|
|
IPreLoadScreen* ActivePreLoadScreen = GetActivePreLoadScreen();
|
|
while (ActivePreLoadScreen && !ActivePreLoadScreen->IsDone())
|
|
{
|
|
GameLogicFrameTick();
|
|
}
|
|
}
|
|
|
|
//No longer need SyncMechanism now that the widget has finished rendering
|
|
if (SyncMechanism != nullptr)
|
|
{
|
|
SyncMechanism->DestroySlateThread();
|
|
|
|
delete SyncMechanism;
|
|
SyncMechanism = nullptr;
|
|
}
|
|
|
|
HandleStopPreLoadScreen();
|
|
}
|
|
|
|
void FPreLoadScreenManager::SetEngineLoadingComplete(bool IsEngineLoadingFinished)
|
|
{
|
|
bIsEngineLoadingComplete = IsEngineLoadingFinished;
|
|
|
|
IPreLoadScreen* PreLoadScreen = GetActivePreLoadScreen();
|
|
if (PreLoadScreen)
|
|
{
|
|
PreLoadScreen->SetEngineLoadingFinished(IsEngineLoadingFinished);
|
|
}
|
|
}
|
|
|
|
bool FPreLoadScreenManager::ArePreLoadScreensEnabled()
|
|
{
|
|
bool bEnabled = !GIsEditor && !IsRunningDedicatedServer() && !IsRunningCommandlet() && GUseThreadedRendering;
|
|
|
|
#if !UE_BUILD_SHIPPING
|
|
bEnabled &= !FParse::Param(FCommandLine::Get(), TEXT("NoLoadingScreen"));
|
|
#endif
|
|
|
|
#if PLATFORM_UNIX
|
|
bEnabled = false;
|
|
#endif
|
|
|
|
return bEnabled;
|
|
}
|
|
|
|
void FPreLoadScreenManager::HandleFlushRenderingCommandsStart()
|
|
{
|
|
//Whenever we flush rendering commands we need to unlock this critical section or we will softlock due to our enqueued rendering command never being able to
|
|
//acquire this critical section as its both locked on the GT and the GT won't unlock it as it's waiting for the rendering command to finish to unlock
|
|
AcquireCriticalSection.Unlock();
|
|
|
|
// Note: Don't modify bIsLocked as that will determine if we relock when flush completes
|
|
}
|
|
|
|
void FPreLoadScreenManager::HandleFlushRenderingCommandsEnd()
|
|
{
|
|
//Relock critical section after the FlushRenderCommands finishes as we are
|
|
AcquireCriticalSection.Lock();
|
|
|
|
// Check if we were locked before, if not release lock to prevent flush from leaving us in undesired locked stated
|
|
if (!bIsLocked)
|
|
{
|
|
AcquireCriticalSection.Unlock();
|
|
}
|
|
}
|
|
|
|
void FPreLoadScreenManager::CleanUpResources()
|
|
{
|
|
// Since we are on the game thread, the PreLoadScreen must be completed.
|
|
//But if we are in EngineLoadingScreen, then the thread may be still active if WaitForEngineLoadingScreenToFinish was not called.
|
|
bool bHasActiPreLoadScreen = HasValidActivePreLoadScreen();
|
|
ensureMsgf(!bHasActiPreLoadScreen, TEXT("StopPreLoadScreen or WaitForEngineLoadingScreenToFinish (if EngineLoadingScreen) should be called before we destroy the Screen Manager."));
|
|
|
|
if (SyncMechanism)
|
|
{
|
|
SyncMechanism->DestroySlateThread();
|
|
delete SyncMechanism;
|
|
SyncMechanism = nullptr;
|
|
}
|
|
|
|
for (TSharedPtr<IPreLoadScreen>& PreLoadScreen : PreLoadScreens)
|
|
{
|
|
if (PreLoadScreen.IsValid())
|
|
{
|
|
PreLoadScreen->CleanUp();
|
|
}
|
|
|
|
PreLoadScreen.Reset();
|
|
}
|
|
|
|
OnPreLoadScreenManagerCleanUp.Broadcast();
|
|
|
|
//Make sure our FPreLoadSettingsContainer is cleaned up. We do this here instead of one of the
|
|
//StartupScreens because we don't know how many of them will be using the same PreLoadScreenContainer, however any
|
|
//other game specific settings containers should be cleaned up by their screens/modules
|
|
BeginCleanup(&FPreLoadSettingsContainerBase::Get());
|
|
}
|
|
|
|
#if PLATFORM_ANDROID
|
|
void FPreLoadScreenManager::Android_PlatformSpecificGameLogicFrameTick()
|
|
{
|
|
#if USE_ANDROID_EVENTS
|
|
// Process any Android events or we may have issues returning from background
|
|
FAppEventManager::GetInstance()->Tick();
|
|
#endif //USE_ANDROIID_EVENTS
|
|
}
|
|
#endif //PLATFORM_ANDROID
|
|
|
|
#if PLATFORM_IOS
|
|
void FPreLoadScreenManager::IOS_PlatformSpecificGameLogicFrameTick()
|
|
{
|
|
// drain the async task queue from the game thread
|
|
[FIOSAsyncTask ProcessAsyncTasks];
|
|
}
|
|
#endif //PLATFORM_IOS
|