Files
UnrealEngineUWP/Engine/Source/Runtime/MoviePlayer/Private/MoviePlayerThreading.cpp
aurel cordonnier 8eebe8841f Merge UE5/RET @ 16305968 to UE5/Main
This represents UE4/Main @ 16261013 and Dev-PerfTest @ 16259937

[CL 16306996 by aurel cordonnier in ue5-main branch]
2021-05-12 18:10:03 -04:00

206 lines
5.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MoviePlayerThreading.h"
#include "MoviePlayer.h"
#include "HAL/Runnable.h"
#include "HAL/RunnableThread.h"
#include "Misc/ScopeLock.h"
#include "Framework/Application/SlateApplication.h"
#include "DefaultGameMoviePlayer.h"
#include "HAL/PlatformApplicationMisc.h"
FThreadSafeCounter FSlateLoadingSynchronizationMechanism::LoadingThreadInstanceCounter;
/**
* The Slate thread is simply run on a worker thread.
* Slate is run on another thread because the game thread (where Slate is usually run)
* is blocked loading things. Slate is very modular, which makes it very easy to run on another
* thread with no adverse effects.
* It does not enqueue render commands, because the RHI is not thread safe. Thus, it waits to
* enqueue render commands until the render thread tickables ticks, and then it calls them there.
*/
class FSlateLoadingThreadTask : public FRunnable
{
public:
FSlateLoadingThreadTask(class FSlateLoadingSynchronizationMechanism& InSyncMechanism)
: SyncMechanism(&InSyncMechanism)
{
}
/** FRunnable interface */
virtual bool Init() override;
virtual uint32 Run() override;
virtual void Stop() override;
private:
/** Hold a handle to our parent sync mechanism which handles all of our threading locks */
class FSlateLoadingSynchronizationMechanism* SyncMechanism;
};
FSlateLoadingSynchronizationMechanism::FSlateLoadingSynchronizationMechanism(
TSharedPtr<FMoviePlayerWidgetRenderer, ESPMode::ThreadSafe> InWidgetRenderer,
const TSharedPtr<IMovieStreamer, ESPMode::ThreadSafe>& InMovieStreamer)
: WidgetRenderer(InWidgetRenderer)
, MovieStreamer(InMovieStreamer)
{
}
FSlateLoadingSynchronizationMechanism::~FSlateLoadingSynchronizationMechanism()
{
DestroySlateThread();
}
void FSlateLoadingSynchronizationMechanism::Initialize()
{
check(IsInGameThread());
ResetSlateDrawPassEnqueued();
SetSlateMainLoopRunning();
bMainLoopRunning = true;
FString ThreadName = TEXT("SlateLoadingThread");
ThreadName.AppendInt(LoadingThreadInstanceCounter.Increment());
SlateRunnableTask = new FSlateLoadingThreadTask( *this );
SlateLoadingThread = FRunnableThread::Create(SlateRunnableTask, *ThreadName);
}
void FSlateLoadingSynchronizationMechanism::DestroySlateThread()
{
check(IsInGameThread());
if (SlateLoadingThread)
{
IsRunningSlateMainLoop.Reset();
while (bMainLoopRunning)
{
FPlatformApplicationMisc::PumpMessages(false);
FPlatformProcess::Sleep(0.001f);
}
delete SlateLoadingThread;
delete SlateRunnableTask;
SlateLoadingThread = nullptr;
SlateRunnableTask = nullptr;
}
}
bool FSlateLoadingSynchronizationMechanism::IsSlateDrawPassEnqueued()
{
return IsSlateDrawEnqueued.GetValue() != 0;
}
void FSlateLoadingSynchronizationMechanism::SetSlateDrawPassEnqueued()
{
IsSlateDrawEnqueued.Set(1);
}
void FSlateLoadingSynchronizationMechanism::ResetSlateDrawPassEnqueued()
{
IsSlateDrawEnqueued.Reset();
}
bool FSlateLoadingSynchronizationMechanism::IsSlateMainLoopRunning()
{
return IsRunningSlateMainLoop.GetValue() != 0;
}
void FSlateLoadingSynchronizationMechanism::SetSlateMainLoopRunning()
{
IsRunningSlateMainLoop.Set(1);
}
void FSlateLoadingSynchronizationMechanism::ResetSlateMainLoopRunning()
{
IsRunningSlateMainLoop.Reset();
}
void FSlateLoadingSynchronizationMechanism::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())
{
// Tick engine stuff.
if (MovieStreamer.IsValid())
{
MovieStreamer->TickPreEngine();
MovieStreamer->TickPostEngine();
}
FSlateRenderer* MainSlateRenderer = FSlateApplication::Get().GetRenderer();
FScopeLock ScopeLock(MainSlateRenderer->GetResourceCriticalSection());
WidgetRenderer->DrawWindow(DeltaTime);
SetSlateDrawPassEnqueued();
// Tick after rendering.
if (MovieStreamer.IsValid())
{
MovieStreamer->TickPostRender();
}
}
LastTime = CurrentTime;
}
while (IsSlateDrawPassEnqueued())
{
FPlatformProcess::Sleep(1.f / 60.f);
}
bMainLoopRunning = false;
}
bool FSlateLoadingThreadTask::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 FSlateLoadingThreadTask::Run()
{
FTaskTagScope Scope(ETaskTag::ESlateThread);
check( GSlateLoadingThreadId == FPlatformTLS::GetCurrentThreadId() );
SyncMechanism->SlateThreadRunMainLoop();
// Tear down the slate loading thread ID
FPlatformAtomics::InterlockedExchange((int32*)&GSlateLoadingThreadId, 0);
return 0;
}
void FSlateLoadingThreadTask::Stop()
{
SyncMechanism->ResetSlateDrawPassEnqueued();
SyncMechanism->ResetSlateMainLoopRunning();
}