802 lines
28 KiB
C++
802 lines
28 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Render/DisplayClusterRenderManager.h"
|
|
#include "Config/IPDisplayClusterConfigManager.h"
|
|
|
|
#include "Engine/GameViewportClient.h"
|
|
#include "Engine/GameEngine.h"
|
|
|
|
#include "Misc/DisplayClusterGlobals.h"
|
|
#include "Misc/DisplayClusterHelpers.h"
|
|
#include "Misc/DisplayClusterLog.h"
|
|
#include "Misc/DisplayClusterStrings.h"
|
|
|
|
#include "DisplayClusterConfigurationStrings.h"
|
|
|
|
#include "Components/DisplayClusterCameraComponent.h"
|
|
#include "DisplayClusterViewportClient.h"
|
|
|
|
#include "Config/IPDisplayClusterConfigManager.h"
|
|
#include "DisplayClusterConfigurationTypes.h"
|
|
|
|
#include "Game/IPDisplayClusterGameManager.h"
|
|
|
|
#include "Render/Device/DisplayClusterRenderDeviceFactoryInternal.h"
|
|
|
|
#include "Render/Device/IDisplayClusterRenderDeviceFactory.h"
|
|
#include "Render/Monitoring/DisplayClusterVblankMonitor.h"
|
|
#include "Render/PostProcess/IDisplayClusterPostProcess.h"
|
|
#include "Render/Projection/IDisplayClusterProjectionPolicyFactory.h"
|
|
#include "Render/Projection/IDisplayClusterProjectionPolicy.h"
|
|
#include "Render/Synchronization/IDisplayClusterRenderSyncPolicyFactory.h"
|
|
|
|
#include "Render/Containers/DisplayClusterRender_MeshComponent.h"
|
|
#include "Render/Containers/DisplayClusterRender_Texture.h"
|
|
|
|
#include "Render/Presentation/DisplayClusterPresentationNative.h"
|
|
|
|
#include "Render/Synchronization/DisplayClusterRenderSyncPolicyFactoryInternal.h"
|
|
#include "Render/Synchronization/DisplayClusterRenderSyncPolicyNone.h"
|
|
|
|
#include "Framework/Application/SlateApplication.h"
|
|
|
|
#include "UnrealClient.h"
|
|
#include "Kismet/GameplayStatics.h"
|
|
|
|
#include "CineCameraComponent.h"
|
|
#include "Engine/Scene.h"
|
|
|
|
#include "DisplayClusterRootActor.h"
|
|
|
|
#if PLATFORM_WINDOWS
|
|
#include "Windows/AllowWindowsPlatformTypes.h"
|
|
#include "Windows.h"
|
|
#include "Windows/HideWindowsPlatformTypes.h"
|
|
#endif
|
|
|
|
|
|
static TAutoConsoleVariable<int32> CVarSyncDiagnosticsVBlankMonitoring(
|
|
TEXT("nDisplay.sync.diag.VBlankMonitoring"),
|
|
0,
|
|
TEXT("Sync diagnostics: V-blank monitoring\n")
|
|
TEXT("0 : disabled\n")
|
|
TEXT("1 : enabled (if policy supports only)\n")
|
|
,
|
|
ECVF_ReadOnly
|
|
);
|
|
|
|
#include "Misc/DisplayClusterDataCache.h"
|
|
|
|
/**
|
|
* The cache for DC texture objects. (Singleton)
|
|
* Allows you to reuse textures with the same unique name.
|
|
*/
|
|
class FDisplayClusterRenderTextureCache
|
|
: public TDisplayClusterDataCache<FDisplayClusterRender_Texture>
|
|
{
|
|
public:
|
|
static TSharedPtr<FDisplayClusterRender_Texture, ESPMode::ThreadSafe> GetOrCreateRenderTexture(const FString& InTextureName)
|
|
{
|
|
static FDisplayClusterRenderTextureCache TextureCacheSingleton;
|
|
|
|
const FString UniqueName = HashString(InTextureName);
|
|
|
|
TSharedPtr<FDisplayClusterRender_Texture, ESPMode::ThreadSafe> TextureRef = TextureCacheSingleton.Find(UniqueName);
|
|
if (!TextureRef.IsValid())
|
|
{
|
|
TextureRef = MakeShared<FDisplayClusterRender_Texture, ESPMode::ThreadSafe>(UniqueName);
|
|
TextureCacheSingleton.Add(TextureRef);
|
|
}
|
|
|
|
return TextureRef;
|
|
}
|
|
};
|
|
|
|
//---------------------------------------------------
|
|
// FDisplayClusterRenderManager
|
|
//---------------------------------------------------
|
|
FDisplayClusterRenderManager::FDisplayClusterRenderManager()
|
|
{
|
|
// Instantiate and register internal render device factory
|
|
TSharedPtr<IDisplayClusterRenderDeviceFactory> NewRenderDeviceFactory(new FDisplayClusterRenderDeviceFactoryInternal);
|
|
RegisterRenderDeviceFactory(DisplayClusterStrings::args::dev::Mono, NewRenderDeviceFactory);
|
|
RegisterRenderDeviceFactory(DisplayClusterStrings::args::dev::QBS, NewRenderDeviceFactory);
|
|
RegisterRenderDeviceFactory(DisplayClusterStrings::args::dev::SbS, NewRenderDeviceFactory);
|
|
RegisterRenderDeviceFactory(DisplayClusterStrings::args::dev::TB, NewRenderDeviceFactory);
|
|
|
|
// Instantiate and register internal sync policy factory
|
|
TSharedPtr<IDisplayClusterRenderSyncPolicyFactory> NewSyncPolicyFactory(new FDisplayClusterRenderSyncPolicyFactoryInternal);
|
|
RegisterSynchronizationPolicyFactory(DisplayClusterConfigurationStrings::config::cluster::render_sync::None, NewSyncPolicyFactory); // None
|
|
RegisterSynchronizationPolicyFactory(DisplayClusterConfigurationStrings::config::cluster::render_sync::Ethernet, NewSyncPolicyFactory); // Ethernet
|
|
RegisterSynchronizationPolicyFactory(DisplayClusterConfigurationStrings::config::cluster::render_sync::EthernetBarrier, NewSyncPolicyFactory); // Ethernet_Simple
|
|
RegisterSynchronizationPolicyFactory(DisplayClusterConfigurationStrings::config::cluster::render_sync::Nvidia, NewSyncPolicyFactory); // NVIDIA
|
|
|
|
// Instantiate V-blank monitor (it won't auto-start polling)
|
|
VBlankMonitor = MakeShared<FDisplayClusterVBlankMonitor, ESPMode::ThreadSafe>();
|
|
}
|
|
|
|
FDisplayClusterRenderManager::~FDisplayClusterRenderManager()
|
|
{
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
// IPDisplayClusterManager
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool FDisplayClusterRenderManager::Init(EDisplayClusterOperationMode OperationMode)
|
|
{
|
|
CurrentOperationMode = OperationMode;
|
|
|
|
return true;
|
|
}
|
|
|
|
void FDisplayClusterRenderManager::Release()
|
|
{
|
|
//@note: No need to release our RenderDevice. It will be released in a safe way by TSharedPtr.
|
|
}
|
|
|
|
bool FDisplayClusterRenderManager::StartSession(UDisplayClusterConfigurationData* InConfigData, const FString& InNodeId)
|
|
{
|
|
if (CurrentOperationMode == EDisplayClusterOperationMode::Disabled)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Operation mode is 'Disabled' so no initialization will be performed"));
|
|
return true;
|
|
}
|
|
|
|
// Set callback on viewport created. We want to make sure the DisplayClusterViewportClient is used.
|
|
UGameViewportClient::OnViewportCreated().AddRaw(this, &FDisplayClusterRenderManager::OnViewportCreatedHandler_CheckViewportClass);
|
|
|
|
// Create synchronization object
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Instantiating synchronization policy object..."));
|
|
SyncPolicy = CreateRenderSyncPolicy();
|
|
if (SyncPolicy)
|
|
{
|
|
SyncPolicy->Initialize();
|
|
}
|
|
|
|
// Instantiate render device
|
|
TSharedPtr<IDisplayClusterRenderDevice, ESPMode::ThreadSafe> NewRenderDevice;
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Instantiating stereo device..."));
|
|
NewRenderDevice = CreateRenderDevice();
|
|
|
|
// Set new device as the engine's stereoscopic device
|
|
if (GEngine && NewRenderDevice.IsValid())
|
|
{
|
|
GEngine->StereoRenderingDevice = StaticCastSharedPtr<IStereoRendering>(NewRenderDevice);
|
|
RenderDevicePtr = NewRenderDevice.Get();
|
|
}
|
|
|
|
// Start v-blank monitoring if requested
|
|
if (!!CVarSyncDiagnosticsVBlankMonitoring.GetValueOnGameThread())
|
|
{
|
|
VBlankMonitor->StartMonitoring();
|
|
}
|
|
|
|
// When session is starting in Editor the device won't be initialized so we avoid nullptr access here.
|
|
//@todo Now we always have a device, even for Editor. Change the condition working on the EditorDevice.
|
|
return (RenderDevicePtr ? RenderDevicePtr->Initialize() : true);
|
|
}
|
|
|
|
void FDisplayClusterRenderManager::EndSession()
|
|
{
|
|
#if WITH_EDITOR
|
|
if (GIsEditor && RenderDevicePtr)
|
|
{
|
|
// Since we can run multiple PIE sessions we have to clean device before the next one.
|
|
GEngine->StereoRenderingDevice.Reset();
|
|
RenderDevicePtr = nullptr;
|
|
}
|
|
#endif
|
|
|
|
SyncPolicy.Reset();
|
|
}
|
|
|
|
bool FDisplayClusterRenderManager::StartScene(UWorld* InWorld)
|
|
{
|
|
if (RenderDevicePtr)
|
|
{
|
|
RenderDevicePtr->StartScene(InWorld);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void FDisplayClusterRenderManager::EndScene()
|
|
{
|
|
if (RenderDevicePtr)
|
|
{
|
|
RenderDevicePtr->EndScene();
|
|
}
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS
|
|
static bool SimulateMouseClick(HWND WindowHandle)
|
|
{
|
|
RECT WindowRect; // Window rect in screen coordinates
|
|
|
|
if (!GetWindowRect(WindowHandle, &WindowRect))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const double ScreenWidth = double(::GetSystemMetrics(SM_CXSCREEN));
|
|
const double ScreenHeight = double(::GetSystemMetrics(SM_CYSCREEN));
|
|
|
|
check(ScreenWidth > 0.5);
|
|
check(ScreenHeight > 0.5);
|
|
|
|
INPUT Inputs[3] = {{ 0 }};
|
|
|
|
// Expected coordinates are normalized to screen dimensions of 65535x65535
|
|
|
|
const double Left = double(WindowRect.left);
|
|
const double Right = double(WindowRect.right);
|
|
const double Top = double(WindowRect.top);
|
|
const double Bottom = double(WindowRect.bottom);
|
|
|
|
Inputs[0].type = INPUT_MOUSE;
|
|
Inputs[0].mi.dx = LONG((Left + (Right - Left)/2.0) * (65535.0 / ScreenWidth));
|
|
Inputs[0].mi.dy = LONG((Top + (Bottom - Top)/2.0) * (65535.0 / ScreenHeight));
|
|
Inputs[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
|
|
|
|
Inputs[1].type = INPUT_MOUSE;
|
|
Inputs[1].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
|
|
|
|
Inputs[2].type = INPUT_MOUSE;
|
|
Inputs[2].mi.dwFlags = MOUSEEVENTF_LEFTUP;
|
|
|
|
return !!::SendInput(3, Inputs, sizeof(INPUT));
|
|
}
|
|
#endif //PLATFORM_WINDOWS
|
|
|
|
void FDisplayClusterRenderManager::PreTick(float DeltaSeconds)
|
|
{
|
|
static const bool bIsRenderingOffscreen = FParse::Param(FCommandLine::Get(), TEXT("RenderOffscreen"));
|
|
|
|
if(!bIsRenderingOffscreen && !bWasWindowFocused)
|
|
{
|
|
if (UGameViewportClient* GameViewportClient = GEngine->GameViewport)
|
|
{
|
|
if (TSharedPtr<SWindow> Window = GameViewportClient->GetWindow())
|
|
{
|
|
if (TSharedPtr<const FGenericWindow> NativeWindow = Window->GetNativeWindow())
|
|
{
|
|
if (const void* WindowHandle = NativeWindow->GetOSWindowHandle())
|
|
{
|
|
#if PLATFORM_WINDOWS
|
|
const HWND GameHWND = (HWND)WindowHandle;
|
|
|
|
::SetWindowPos(GameHWND, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
|
|
::SetForegroundWindow(GameHWND);
|
|
::SetCapture(GameHWND);
|
|
::SetFocus(GameHWND);
|
|
::SetActiveWindow(GameHWND);
|
|
|
|
SimulateMouseClick(GameHWND);
|
|
#endif
|
|
FSlateApplication::Get().SetAllUserFocusToGameViewport();
|
|
|
|
bWasWindowFocused = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (RenderDevicePtr)
|
|
{
|
|
RenderDevicePtr->PreTick(DeltaSeconds);
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
// IDisplayClusterRenderManager
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
IDisplayClusterRenderDevice* FDisplayClusterRenderManager::GetRenderDevice() const
|
|
{
|
|
return RenderDevicePtr;
|
|
}
|
|
|
|
bool FDisplayClusterRenderManager::RegisterRenderDeviceFactory(const FString& InDeviceType, TSharedPtr<IDisplayClusterRenderDeviceFactory>& InFactory)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Registering factory for rendering device type: %s"), *InDeviceType);
|
|
|
|
if (!InFactory.IsValid())
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("Invalid factory object"));
|
|
return false;
|
|
}
|
|
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
|
|
if (RenderDeviceFactories.Contains(InDeviceType))
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("Setting a new factory for '%s' rendering device type"), *InDeviceType);
|
|
}
|
|
|
|
RenderDeviceFactories.Emplace(InDeviceType, InFactory);
|
|
}
|
|
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Registered factory for rendering device type: %s"), *InDeviceType);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FDisplayClusterRenderManager::UnregisterRenderDeviceFactory(const FString& InDeviceType)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Unregistering factory for rendering device type: %s"), *InDeviceType);
|
|
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
|
|
if (!RenderDeviceFactories.Contains(InDeviceType))
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("A factory for '%s' rendering device type not found"), *InDeviceType);
|
|
return false;
|
|
}
|
|
|
|
RenderDeviceFactories.Remove(InDeviceType);
|
|
}
|
|
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Unregistered factory for rendering device type: %s"), *InDeviceType);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FDisplayClusterRenderManager::RegisterSynchronizationPolicyFactory(const FString& InSyncPolicyType, TSharedPtr<IDisplayClusterRenderSyncPolicyFactory>& InFactory)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Registering factory for synchronization policy: %s"), *InSyncPolicyType);
|
|
|
|
if (!InFactory.IsValid())
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("Invalid factory object"));
|
|
return false;
|
|
}
|
|
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
|
|
if (SyncPolicyFactories.Contains(InSyncPolicyType))
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("A new factory for '%s' synchronization policy was set"), *InSyncPolicyType);
|
|
}
|
|
|
|
SyncPolicyFactories.Emplace(InSyncPolicyType, InFactory);
|
|
}
|
|
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Registered factory for synchronization policy: %s"), *InSyncPolicyType);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FDisplayClusterRenderManager::UnregisterSynchronizationPolicyFactory(const FString& InSyncPolicyType)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Unregistering factory for synchronization policy: %s"), *InSyncPolicyType);
|
|
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
|
|
if (!SyncPolicyFactories.Contains(InSyncPolicyType))
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("A factory for '%s' synchronization policy not found"), *InSyncPolicyType);
|
|
return false;
|
|
}
|
|
|
|
SyncPolicyFactories.Remove(InSyncPolicyType);
|
|
}
|
|
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Unregistered factory for synchronization policy: %s"), *InSyncPolicyType);
|
|
|
|
return true;
|
|
}
|
|
|
|
TSharedPtr<IDisplayClusterRenderSyncPolicy> FDisplayClusterRenderManager::GetCurrentSynchronizationPolicy()
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
return SyncPolicy;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// Projection Policy
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
bool FDisplayClusterRenderManager::RegisterProjectionPolicyFactory(const FString& InProjectionType, TSharedPtr<IDisplayClusterProjectionPolicyFactory>& InFactory)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Registering factory for projection type: %s"), *InProjectionType);
|
|
|
|
if (!InFactory.IsValid())
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("Invalid factory object"));
|
|
return false;
|
|
}
|
|
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
|
|
if (ProjectionPolicyFactories.Contains(InProjectionType))
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("A new factory for '%s' projection policy was set"), *InProjectionType);
|
|
}
|
|
|
|
ProjectionPolicyFactories.Emplace(InProjectionType, InFactory);
|
|
}
|
|
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Registered factory for projection type: %s"), *InProjectionType);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FDisplayClusterRenderManager::UnregisterProjectionPolicyFactory(const FString& InProjectionType)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Unregistering factory for projection policy: %s"), *InProjectionType);
|
|
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
|
|
if (!ProjectionPolicyFactories.Contains(InProjectionType))
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("A handler for '%s' projection type not found"), *InProjectionType);
|
|
return false;
|
|
}
|
|
|
|
ProjectionPolicyFactories.Remove(InProjectionType);
|
|
}
|
|
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Unregistered factory for projection policy: %s"), *InProjectionType);
|
|
|
|
return true;
|
|
}
|
|
|
|
TSharedPtr<IDisplayClusterProjectionPolicyFactory> FDisplayClusterRenderManager::GetProjectionPolicyFactory(const FString& InProjectionType)
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
|
|
TSharedPtr<IDisplayClusterProjectionPolicyFactory> Factory;
|
|
if (!DisplayClusterHelpers::map::template ExtractValue(ProjectionPolicyFactories, InProjectionType, Factory))
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("No factory found for projection policy: %s"), *InProjectionType);
|
|
}
|
|
|
|
return Factory;
|
|
}
|
|
|
|
void FDisplayClusterRenderManager::GetRegisteredProjectionPolicies(TArray<FString>& OutPolicyIDs) const
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
ProjectionPolicyFactories.GetKeys(OutPolicyIDs);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// PostProcess
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
bool FDisplayClusterRenderManager::RegisterPostProcessFactory(const FString& InPostProcessType, TSharedPtr<IDisplayClusterPostProcessFactory>& InFactory)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Registering factory for postprocess type: %s"), *InPostProcessType);
|
|
|
|
if (!InFactory.IsValid())
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("Invalid factory object"));
|
|
return false;
|
|
}
|
|
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
|
|
if (PostProcessFactories.Contains(InPostProcessType))
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("A new factory for '%s' postprocess was set"), *InPostProcessType);
|
|
}
|
|
|
|
PostProcessFactories.Emplace(InPostProcessType, InFactory);
|
|
}
|
|
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Registered factory for postprocess type: %s"), *InPostProcessType);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FDisplayClusterRenderManager::UnregisterPostProcessFactory(const FString& InPostProcessType)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Unregistering factory for postprocess: %s"), *InPostProcessType);
|
|
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
|
|
if (!PostProcessFactories.Contains(InPostProcessType))
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("A handler for '%s' postprocess type not found"), *InPostProcessType);
|
|
return false;
|
|
}
|
|
|
|
PostProcessFactories.Remove(InPostProcessType);
|
|
}
|
|
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Unregistered factory for postprocess: %s"), *InPostProcessType);
|
|
|
|
return true;
|
|
}
|
|
|
|
TSharedPtr<IDisplayClusterPostProcessFactory> FDisplayClusterRenderManager::GetPostProcessFactory(const FString& InPostProcessType)
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
|
|
TSharedPtr<IDisplayClusterPostProcessFactory> Factory;
|
|
if (!DisplayClusterHelpers::map::template ExtractValue(PostProcessFactories, InPostProcessType, Factory))
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("No factory found for postprocess: %s"), *InPostProcessType);
|
|
}
|
|
|
|
return Factory;
|
|
}
|
|
|
|
void FDisplayClusterRenderManager::GetRegisteredPostProcess(TArray<FString>& OutPostProcessIDs) const
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
PostProcessFactories.GetKeys(OutPostProcessIDs);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// Warp Policy
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
bool FDisplayClusterRenderManager::RegisterWarpPolicyFactory(const FString& InWarpPolicyType, TSharedPtr<IDisplayClusterWarpPolicyFactory>& InFactory)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Registering factory for warp policy type: %s"), *InWarpPolicyType);
|
|
|
|
if (!InFactory.IsValid())
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("Invalid factory object"));
|
|
return false;
|
|
}
|
|
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
|
|
if (WarpPolicyFactories.Contains(InWarpPolicyType))
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("A new factory for '%s' warp policy was set"), *InWarpPolicyType);
|
|
}
|
|
|
|
WarpPolicyFactories.Emplace(InWarpPolicyType, InFactory);
|
|
}
|
|
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Registered factory for warp policy type: %s"), *InWarpPolicyType);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool FDisplayClusterRenderManager::UnregisterWarpPolicyFactory(const FString& InWarpPolicyType)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Unregistering factory for warp policy: %s"), *InWarpPolicyType);
|
|
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
|
|
if (!WarpPolicyFactories.Contains(InWarpPolicyType))
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("A handler for '%s' warp policy type not found"), *InWarpPolicyType);
|
|
return false;
|
|
}
|
|
|
|
WarpPolicyFactories.Remove(InWarpPolicyType);
|
|
}
|
|
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Unregistered factory for warp policy: %s"), *InWarpPolicyType);
|
|
|
|
return true;
|
|
}
|
|
|
|
TSharedPtr<IDisplayClusterWarpPolicyFactory> FDisplayClusterRenderManager::GetWarpPolicyFactory(const FString& InWarpPolicyType)
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
|
|
TSharedPtr<IDisplayClusterWarpPolicyFactory> Factory;
|
|
if (!DisplayClusterHelpers::map::template ExtractValue(WarpPolicyFactories, InWarpPolicyType, Factory))
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("No factory found for warp policy: %s"), *InWarpPolicyType);
|
|
}
|
|
|
|
return Factory;
|
|
}
|
|
|
|
void FDisplayClusterRenderManager::GetRegisteredWarpPolicies(TArray<FString>& OutWarpPolicyIDs) const
|
|
{
|
|
FScopeLock Lock(&CritSecInternals);
|
|
WarpPolicyFactories.GetKeys(OutWarpPolicyIDs);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
// Resources
|
|
//------------------------------------------------------------------------------------------------------------------------------
|
|
TSharedPtr<IDisplayClusterRender_MeshComponent, ESPMode::ThreadSafe> FDisplayClusterRenderManager::CreateMeshComponent() const
|
|
{
|
|
return MakeShared<FDisplayClusterRender_MeshComponent, ESPMode::ThreadSafe>();
|
|
}
|
|
|
|
TSharedPtr<IDisplayClusterRender_Texture, ESPMode::ThreadSafe> FDisplayClusterRenderManager::GetOrCreateCachedTexture(const FString& InUniqueTextureName) const
|
|
{
|
|
return FDisplayClusterRenderTextureCache::GetOrCreateRenderTexture(InUniqueTextureName);
|
|
}
|
|
|
|
IDisplayClusterViewportManager* FDisplayClusterRenderManager::GetViewportManager() const
|
|
{
|
|
ADisplayClusterRootActor* RootActor = GDisplayCluster->GetGameMgr()->GetRootActor();
|
|
if (RootActor)
|
|
{
|
|
return RootActor->GetViewportManager();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
// FDisplayClusterRenderManager
|
|
//////////////////////////////////////////////////////////////////////////////////////////////
|
|
TSharedPtr<IDisplayClusterRenderDevice, ESPMode::ThreadSafe> FDisplayClusterRenderManager::CreateRenderDevice() const
|
|
{
|
|
TSharedPtr<IDisplayClusterRenderDevice, ESPMode::ThreadSafe> NewRenderDevice;
|
|
|
|
if (CurrentOperationMode == EDisplayClusterOperationMode::Cluster)
|
|
{
|
|
if (GDynamicRHI == nullptr)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Error, TEXT("GDynamicRHI is null. Cannot detect RHI name."));
|
|
return nullptr;
|
|
}
|
|
|
|
// Monoscopic
|
|
if (FParse::Param(FCommandLine::Get(), DisplayClusterStrings::args::dev::Mono))
|
|
{
|
|
NewRenderDevice = RenderDeviceFactories[DisplayClusterStrings::args::dev::Mono]->Create(DisplayClusterStrings::args::dev::Mono);
|
|
}
|
|
// Quad buffer stereo
|
|
else if (FParse::Param(FCommandLine::Get(), DisplayClusterStrings::args::dev::QBS))
|
|
{
|
|
NewRenderDevice = RenderDeviceFactories[DisplayClusterStrings::args::dev::QBS]->Create(DisplayClusterStrings::args::dev::QBS);
|
|
}
|
|
// Side-by-side
|
|
else if (FParse::Param(FCommandLine::Get(), DisplayClusterStrings::args::dev::SbS))
|
|
{
|
|
NewRenderDevice = RenderDeviceFactories[DisplayClusterStrings::args::dev::SbS]->Create(DisplayClusterStrings::args::dev::SbS);
|
|
}
|
|
// Top-bottom
|
|
else if (FParse::Param(FCommandLine::Get(), DisplayClusterStrings::args::dev::TB))
|
|
{
|
|
NewRenderDevice = RenderDeviceFactories[DisplayClusterStrings::args::dev::TB]->Create(DisplayClusterStrings::args::dev::TB);
|
|
}
|
|
// Leave native render but inject custom present for cluster synchronization
|
|
else
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("No rendering device specified! A native present handler will be instantiated when viewport is available"));
|
|
UGameViewportClient::OnViewportCreated().AddRaw(const_cast<FDisplayClusterRenderManager*>(this), &FDisplayClusterRenderManager::OnViewportCreatedHandler_SetCustomPresent);
|
|
}
|
|
}
|
|
else if (CurrentOperationMode == EDisplayClusterOperationMode::Editor)
|
|
{
|
|
#if 0
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Instantiating DX11 mono device for PIE"));
|
|
NewRenderDevice = MakeShared<FDisplayClusterDeviceMonoscopicDX11>();
|
|
#endif
|
|
}
|
|
else if (CurrentOperationMode == EDisplayClusterOperationMode::Disabled)
|
|
{
|
|
// Stereo device is not needed
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("No need to instantiate stereo device"));
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("Unknown operation mode"));
|
|
}
|
|
|
|
if (!NewRenderDevice.IsValid())
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("No stereo device created"));
|
|
}
|
|
|
|
return NewRenderDevice;
|
|
}
|
|
|
|
TSharedPtr<IDisplayClusterRenderSyncPolicy> FDisplayClusterRenderManager::CreateRenderSyncPolicy() const
|
|
{
|
|
if (CurrentOperationMode != EDisplayClusterOperationMode::Cluster)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Synchronization policy is not available for current operation mode"));
|
|
return nullptr;
|
|
}
|
|
|
|
if (GDynamicRHI == nullptr)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Error, TEXT("GDynamicRHI is null. Cannot detect RHI name."));
|
|
return nullptr;
|
|
}
|
|
|
|
const UDisplayClusterConfigurationData* ConfigData = GDisplayCluster->GetPrivateConfigMgr()->GetConfig();
|
|
if (!ConfigData)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Error, TEXT("Couldn't get configuration data"));
|
|
return nullptr;
|
|
}
|
|
|
|
FString SyncPolicyType;
|
|
TMap<FString, FString>* SyncPolicyParams = nullptr;
|
|
|
|
// Always use 'none' sync policy while rendering headless
|
|
if (FParse::Param(FCommandLine::Get(), TEXT("RenderOffscreen")))
|
|
{
|
|
SyncPolicyType = DisplayClusterConfigurationStrings::config::cluster::render_sync::HeadlessRenderingSyncPolicy;
|
|
SyncPolicyParams = nullptr;
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Headless rendering requested. Using '%s' sync policy."), *SyncPolicyType);
|
|
}
|
|
// Otherwise use sync policy from config
|
|
else
|
|
{
|
|
SyncPolicyType = ConfigData->Cluster->Sync.RenderSyncPolicy.Type;
|
|
SyncPolicyParams = &ConfigData->Cluster->Sync.RenderSyncPolicy.Parameters;
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Requested sync policy is '%s'"), *SyncPolicyType);
|
|
}
|
|
|
|
// Instantiate policy instance
|
|
TSharedPtr<IDisplayClusterRenderSyncPolicy> NewSyncPolicy;
|
|
if (SyncPolicyFactories.Contains(SyncPolicyType))
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("A factory for the requested synchronization policy <%s> was found"), *SyncPolicyType);
|
|
NewSyncPolicy = SyncPolicyFactories[SyncPolicyType]->Create(SyncPolicyType, SyncPolicyParams ? *SyncPolicyParams : TMap<FString, FString>());
|
|
}
|
|
// Fallback if requested policy is not available
|
|
else
|
|
{
|
|
const FString DefaultPolicy = DisplayClusterConfigurationStrings::config::cluster::render_sync::EthernetBarrier;
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("No factory found for the requested synchronization policy <%s>. Default '%s' policy will be used."), *SyncPolicyType, *DefaultPolicy);
|
|
NewSyncPolicy = SyncPolicyFactories[DefaultPolicy]->Create(DefaultPolicy, TMap<FString, FString>());
|
|
}
|
|
|
|
return NewSyncPolicy;
|
|
}
|
|
|
|
void FDisplayClusterRenderManager::ResizeWindow(int32 WinX, int32 WinY, int32 ResX, int32 ResY)
|
|
{
|
|
UGameEngine* Engine = Cast<UGameEngine>(GEngine);
|
|
TSharedPtr<SWindow> Window = Engine->GameViewportWindow.Pin();
|
|
check(Window.IsValid());
|
|
|
|
UE_LOG(LogDisplayClusterRender, Log, TEXT("Adjusting game window: pos [%d, %d], size [%d x %d]"), WinX, WinY, ResX, ResY);
|
|
|
|
// Adjust window position/size
|
|
Window->ReshapeWindow(FVector2D(WinX, WinY), FVector2D(ResX, ResY));
|
|
}
|
|
|
|
void FDisplayClusterRenderManager::OnViewportCreatedHandler_SetCustomPresent() const
|
|
{
|
|
if (GEngine && GEngine->GameViewport)
|
|
{
|
|
if (!GEngine->GameViewport->Viewport->GetViewportRHI().IsValid())
|
|
{
|
|
GEngine->GameViewport->OnBeginDraw().AddRaw(this, &FDisplayClusterRenderManager::OnBeginDrawHandler);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDisplayClusterRenderManager::OnViewportCreatedHandler_CheckViewportClass() const
|
|
{
|
|
if (GEngine && GEngine->GameViewport)
|
|
{
|
|
UDisplayClusterViewportClient* const GameViewport = Cast<UDisplayClusterViewportClient>(GEngine->GameViewport);
|
|
if (!GameViewport)
|
|
{
|
|
UE_LOG(LogDisplayClusterRender, Warning, TEXT("DisplayClusterViewportClient is not set as a default GameViewport class"));
|
|
}
|
|
}
|
|
}
|
|
|
|
void FDisplayClusterRenderManager::OnBeginDrawHandler() const
|
|
{
|
|
static bool initialized = false;
|
|
if (!initialized && GEngine->GameViewport->Viewport->GetViewportRHI().IsValid())
|
|
{
|
|
FDisplayClusterPresentationNative* const NativePresentHandler = new FDisplayClusterPresentationNative(GEngine->GameViewport->Viewport, SyncPolicy);
|
|
check(NativePresentHandler);
|
|
GEngine->GameViewport->Viewport->GetViewportRHI().GetReference()->SetCustomPresent(NativePresentHandler);
|
|
initialized = true;
|
|
}
|
|
}
|