542 lines
18 KiB
C++
542 lines
18 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
D3D12Viewport.cpp: D3D viewport RHI implementation.
|
|
=============================================================================*/
|
|
|
|
#include "D3D12RHIPrivate.h"
|
|
#include "Features/IModularFeatures.h"
|
|
#include "HDRHelper.h"
|
|
#include "RHIUtilities.h"
|
|
#include "HAL/ThreadHeartBeat.h"
|
|
#include "Windows/IDXGISwapchainProvider.h"
|
|
|
|
#include "Windows/AllowWindowsPlatformTypes.h"
|
|
|
|
extern FD3D12Texture* GetSwapChainSurface(FD3D12Device* Parent, EPixelFormat PixelFormat, uint32 SizeX, uint32 SizeY, IDXGISwapChain* SwapChain, uint32 BackBufferIndex, TRefCountPtr<ID3D12Resource> BackBufferResourceOverride);
|
|
|
|
static int32 GD3D12UseAllowTearing = 1;
|
|
static FAutoConsoleVariableRef CVarD3DUseAllowTearing(
|
|
TEXT("r.D3D12.UseAllowTearing"),
|
|
GD3D12UseAllowTearing,
|
|
TEXT("Enable new dxgi flip mode with d3d12"),
|
|
ECVF_RenderThreadSafe | ECVF_ReadOnly
|
|
);
|
|
|
|
FD3D12Viewport::FD3D12Viewport(class FD3D12Adapter* InParent, HWND InWindowHandle, uint32 InSizeX, uint32 InSizeY, bool bInIsFullscreen, EPixelFormat InPreferredPixelFormat)
|
|
: FD3D12AdapterChild(InParent)
|
|
, WindowHandle(InWindowHandle)
|
|
, SizeX(InSizeX)
|
|
, SizeY(InSizeY)
|
|
, PixelFormat(InPreferredPixelFormat)
|
|
, bIsFullscreen(bInIsFullscreen)
|
|
, bNeedSwapChain(!FParse::Param(FCommandLine::Get(), TEXT("RenderOffScreen")))
|
|
{
|
|
check(IsInGameThread());
|
|
GetParentAdapter()->GetViewports().Add(this);
|
|
}
|
|
|
|
//Init for a Viewport that will do the presenting
|
|
void FD3D12Viewport::Init()
|
|
{
|
|
FD3D12Adapter* Adapter = GetParentAdapter();
|
|
IDXGIFactory2* Factory2 = Adapter->GetDXGIFactory2();
|
|
|
|
TArray<IDXGISwapchainProvider*> DXGISwapchainProviderModules = IModularFeatures::Get().GetModularFeatureImplementations<IDXGISwapchainProvider>(IDXGISwapchainProvider::GetModularFeatureName());
|
|
IDXGISwapchainProvider* DXGISwapchainProvider = nullptr;
|
|
for (IDXGISwapchainProvider* ProviderModule : DXGISwapchainProviderModules)
|
|
{
|
|
if (ProviderModule->SupportsRHI(ERHIInterfaceType::D3D12))
|
|
{
|
|
DXGISwapchainProvider = ProviderModule;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (DXGISwapchainProvider)
|
|
{
|
|
static bool bCustomSwapchainLogged = false;
|
|
if (!bCustomSwapchainLogged)
|
|
{
|
|
UE_LOG(LogD3D12RHI, Log, TEXT("Found a custom swapchain provider: '%s'."), DXGISwapchainProvider->GetProviderName());
|
|
bCustomSwapchainLogged = true;
|
|
}
|
|
}
|
|
|
|
bAllowTearing = false;
|
|
if (GD3D12UseAllowTearing)
|
|
{
|
|
if (IDXGIFactory5* Factory5 = Adapter->GetDXGIFactory5())
|
|
{
|
|
BOOL AllowTearing = FALSE;
|
|
Factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &AllowTearing, sizeof(AllowTearing));
|
|
bAllowTearing = (AllowTearing != FALSE);
|
|
}
|
|
}
|
|
|
|
bool bStereoMode = false;
|
|
if (FD3D12DynamicRHI::GetD3DRHI()->IsQuadBufferStereoEnabled())
|
|
{
|
|
if (Factory2->IsWindowedStereoEnabled())
|
|
{
|
|
bStereoMode = true;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogD3D12RHI, Log, TEXT("FD3D12Viewport::FD3D12Viewport was not able to create stereo SwapChain; Please enable stereo in driver settings."));
|
|
FD3D12DynamicRHI::GetD3DRHI()->DisableQuadBufferStereo();
|
|
}
|
|
}
|
|
|
|
InitializeBackBufferArrays();
|
|
|
|
// Create the swapchain.
|
|
if (bNeedSwapChain)
|
|
{
|
|
// The command queue used here is irrelevant in regard to multi - GPU as it gets overriden in the Resize
|
|
ID3D12CommandQueue* CommandQueue = Adapter->GetDevice(0)->GetQueue(ED3D12QueueType::Direct).D3DCommandQueue;
|
|
|
|
UINT SwapChainFlags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
|
if (bAllowTearing)
|
|
{
|
|
SwapChainFlags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
|
}
|
|
|
|
DXGI_SWAP_CHAIN_DESC1 SwapChainDesc1{};
|
|
|
|
SwapChainDesc1.Width = SizeX;
|
|
SwapChainDesc1.Height = SizeY;
|
|
SwapChainDesc1.Format = UE::DXGIUtilities::GetSwapChainFormat(PixelFormat);
|
|
SwapChainDesc1.Stereo = bStereoMode ? TRUE : FALSE;
|
|
SwapChainDesc1.SampleDesc.Count = 1;
|
|
SwapChainDesc1.SampleDesc.Quality = 0;
|
|
SwapChainDesc1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
|
|
SwapChainDesc1.BufferCount = NumBackBuffers;
|
|
SwapChainDesc1.Scaling = DXGI_SCALING_NONE;
|
|
SwapChainDesc1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
|
SwapChainDesc1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
|
|
SwapChainDesc1.Flags = SwapChainFlags;
|
|
|
|
DXGI_SWAP_CHAIN_FULLSCREEN_DESC FullscreenDesc{};
|
|
FullscreenDesc.RefreshRate.Numerator = 0;
|
|
FullscreenDesc.RefreshRate.Denominator = 0;
|
|
FullscreenDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
|
|
FullscreenDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
|
|
FullscreenDesc.Windowed = !bIsFullscreen;
|
|
|
|
HRESULT hr = DXGISwapchainProvider ?
|
|
DXGISwapchainProvider->CreateSwapChainForHwnd(Factory2, CommandQueue, WindowHandle, &SwapChainDesc1, &FullscreenDesc, nullptr, SwapChain1.GetInitReference()) :
|
|
Factory2->CreateSwapChainForHwnd(CommandQueue, WindowHandle, &SwapChainDesc1, &FullscreenDesc, nullptr, SwapChain1.GetInitReference());
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
UE_LOG(LogD3D12RHI, Warning, TEXT("Failed to create swapchain with the following parameters:"));
|
|
UE_LOG(LogD3D12RHI, Warning, TEXT("\tWidth: %d Height: %d DXGI format: %d"), SwapChainDesc1.Width, SwapChainDesc1.Height, SwapChainDesc1.Format);
|
|
UE_LOG(LogD3D12RHI, Warning, TEXT("\tBack buffer count: %d"), SwapChainDesc1.BufferCount);
|
|
UE_LOG(LogD3D12RHI, Warning, TEXT("\tWindows handle: 0x%x (IsWindow: %s)"), WindowHandle, IsWindow(WindowHandle) ? TEXT("true") : TEXT("false"));
|
|
UE_LOG(LogD3D12RHI, Warning, TEXT("\tFullscreen: %s"), bIsFullscreen ? TEXT("true") : TEXT("false"));
|
|
UE_LOG(LogD3D12RHI, Warning, TEXT("\tSwapchain flags: 0x%08x"), SwapChainDesc1.Flags);
|
|
UE_LOG(LogD3D12RHI, Warning, TEXT("\tCustom swapchain provider: %s"), DXGISwapchainProvider ? DXGISwapchainProvider->GetProviderName() : TEXT("none"));
|
|
|
|
VERIFYD3D12RESULT(hr);
|
|
}
|
|
|
|
if (SwapChain1)
|
|
{
|
|
SwapChain1->QueryInterface(IID_PPV_ARGS(SwapChain2.GetInitReference()));
|
|
#if DXGI_MAX_SWAPCHAIN_INTERFACE >= 3
|
|
SwapChain1->QueryInterface(IID_PPV_ARGS(SwapChain3.GetInitReference()));
|
|
#endif
|
|
#if DXGI_MAX_SWAPCHAIN_INTERFACE >= 4
|
|
SwapChain1->QueryInterface(IID_PPV_ARGS(SwapChain4.GetInitReference()));
|
|
#endif
|
|
}
|
|
}
|
|
|
|
{
|
|
// Don't make the windows association call and release back buffer at the same time (see notes on critical section)
|
|
FScopeLock Lock(&DXGIBackBufferLock);
|
|
|
|
// Set the DXGI message hook to not change the window behind our back.
|
|
Factory2->MakeWindowAssociation(WindowHandle, DXGI_MWA_NO_WINDOW_CHANGES);
|
|
}
|
|
|
|
// Resize to setup mGPU correctly.
|
|
Resize(SizeX, SizeY, bIsFullscreen, PixelFormat);
|
|
|
|
// Tell the window to redraw when they can.
|
|
// @todo: For Slate viewports, it doesn't make sense to post WM_PAINT messages (we swallow those.)
|
|
::PostMessageW(WindowHandle, WM_PAINT, 0, 0);
|
|
}
|
|
|
|
void FD3D12Viewport::FinalDestroyInternal()
|
|
{
|
|
}
|
|
|
|
void FD3D12Viewport::ClearPresentQueue()
|
|
{
|
|
}
|
|
|
|
void FD3D12Viewport::ConditionalResetSwapChain(bool bIgnoreFocus)
|
|
{
|
|
if (!bIsValid)
|
|
{
|
|
if (bFullscreenLost)
|
|
{
|
|
FlushRenderingCommands();
|
|
bFullscreenLost = false;
|
|
Resize(SizeX, SizeY, false, PixelFormat);
|
|
}
|
|
else
|
|
{
|
|
// Check if the viewport's window is focused before resetting the swap chain's fullscreen state.
|
|
HWND FocusWindow = ::GetFocus();
|
|
const bool bIsFocused = FocusWindow == WindowHandle;
|
|
const bool bIsIconic = !!::IsIconic(WindowHandle);
|
|
if (bIgnoreFocus || (bIsFocused && !bIsIconic))
|
|
{
|
|
FlushRenderingCommands();
|
|
|
|
HRESULT Result = SwapChain1->SetFullscreenState(bIsFullscreen, nullptr);
|
|
if (SUCCEEDED(Result))
|
|
{
|
|
bIsValid = true;
|
|
}
|
|
else if (Result != DXGI_ERROR_NOT_CURRENTLY_AVAILABLE && Result != DXGI_STATUS_MODE_CHANGE_IN_PROGRESS)
|
|
{
|
|
const TCHAR* Name = nullptr;
|
|
switch (Result)
|
|
{
|
|
#define CASE_ERROR_NAME(x) case x: Name = TEXT(#x); break
|
|
EMBED_DXGI_ERROR_LIST(CASE_ERROR_NAME, ;)
|
|
#undef CASE_ERROR_NAME
|
|
default:
|
|
Name = TEXT("unknown error status");
|
|
break;
|
|
}
|
|
UE_LOG(LogD3D12RHI, Error, TEXT("IDXGISwapChain::SetFullscreenState returned 0x%08x, %s."), Result, Name);
|
|
|
|
if (bIsFullscreen)
|
|
{
|
|
// Something went wrong, attempt to proceed in windowed mode.
|
|
Result = SwapChain1->SetFullscreenState(FALSE, nullptr);
|
|
if (SUCCEEDED(Result))
|
|
{
|
|
bIsValid = true;
|
|
bIsFullscreen = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#if D3D12RHI_USE_DUMMY_BACKBUFFER
|
|
/**
|
|
* Create the dummy back buffer textures - They don't have actual D3D resource but are used to always reference the current back buffer index on the RHI thread
|
|
*/
|
|
FD3D12Texture* FD3D12Viewport::CreateDummyBackBufferTextures(FD3D12Adapter* InAdapter, EPixelFormat InPixelFormat, uint32 InSizeX, uint32 InSizeY)
|
|
{
|
|
FRHITextureCreateDesc CreateDesc =
|
|
FRHITextureCreateDesc::Create2D(TEXT("BackBufferReference"))
|
|
.SetExtent(FIntPoint(InSizeX, InSizeY))
|
|
.SetFormat(InPixelFormat)
|
|
.SetFlags(ETextureCreateFlags::RenderTargetable | ETextureCreateFlags::Presentable | ETextureCreateFlags::ResolveTargetable)
|
|
.SetInitialState(ERHIAccess::Present);
|
|
|
|
FD3D12Texture* Result = InAdapter->CreateLinkedObject<FD3D12Texture>(FRHIGPUMask::All(), [this, &CreateDesc](FD3D12Device* Device, FD3D12Texture* FirstLinkedObject)
|
|
{
|
|
return new FD3D12BackBufferReferenceTexture2D(CreateDesc, this, Device);
|
|
});
|
|
return Result;
|
|
}
|
|
#endif
|
|
|
|
void FD3D12Viewport::ResizeInternal()
|
|
{
|
|
FD3D12Adapter* Adapter = GetParentAdapter();
|
|
|
|
InitializeBackBufferArrays();
|
|
|
|
UINT SwapChainFlags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
|
if (bAllowTearing)
|
|
{
|
|
SwapChainFlags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
|
}
|
|
|
|
#if WITH_MGPU
|
|
if (GNumExplicitGPUsForRendering > 1)
|
|
{
|
|
TArray<ID3D12CommandQueue*> CommandQueues;
|
|
TArray<uint32> NodeMasks;
|
|
|
|
for (uint32 Index = 0; Index < NumBackBuffers; Index++)
|
|
{
|
|
// When BackbufferMultiGPUBinding == INDEX_NONE, cycle through each GPU.
|
|
BackBuffers[Index].GPUIndex = BackbufferMultiGPUBinding == INDEX_NONE ? (Index % GNumExplicitGPUsForRendering) : BackbufferMultiGPUBinding;
|
|
}
|
|
|
|
// Select the GPU for each element in the swapchain
|
|
for (uint32 Index = 0; Index < NumBackBuffers; Index++)
|
|
{
|
|
FD3D12Device* Device = Adapter->GetDevice(BackBuffers[Index].GPUIndex);
|
|
|
|
CommandQueues.Add(Device->GetQueue(ED3D12QueueType::Direct).D3DCommandQueue);
|
|
NodeMasks.Add(Device->GetGPUMask().GetNative());
|
|
}
|
|
|
|
if (SwapChain3)
|
|
{
|
|
VERIFYD3D12RESULT_EX(SwapChain3->ResizeBuffers1(NumBackBuffers, SizeX, SizeY, UE::DXGIUtilities::GetSwapChainFormat(PixelFormat), SwapChainFlags, NodeMasks.GetData(), (IUnknown**)CommandQueues.GetData()), Adapter->GetD3DDevice());
|
|
}
|
|
|
|
for (uint32 Index = 0; Index < NumBackBuffers; Index++)
|
|
{
|
|
FD3D12Device* Device = Adapter->GetDevice(BackBuffers[Index].GPUIndex);
|
|
|
|
check(BackBuffers[Index].Texture.GetReference() == nullptr);
|
|
BackBuffers[Index].Texture = GetSwapChainSurface(Device, PixelFormat, SizeX, SizeY, SwapChain1, Index, nullptr);
|
|
}
|
|
}
|
|
else
|
|
#endif // WITH_MGPU
|
|
{
|
|
if (SwapChain1)
|
|
{
|
|
VERIFYD3D12RESULT_LAMBDA(SwapChain1->ResizeBuffers(NumBackBuffers, SizeX, SizeY, UE::DXGIUtilities::GetSwapChainFormat(PixelFormat), SwapChainFlags), Adapter->GetD3DDevice(), [this]
|
|
{
|
|
return GetStateString();
|
|
});
|
|
}
|
|
|
|
FD3D12Device* Device = Adapter->GetDevice(0);
|
|
for (uint32 i = 0; i < NumBackBuffers; ++i)
|
|
{
|
|
check(BackBuffers[i].Texture.GetReference() == nullptr);
|
|
BackBuffers[i].Texture = GetSwapChainSurface(Device, PixelFormat, SizeX, SizeY, SwapChain1, i, nullptr);
|
|
}
|
|
}
|
|
|
|
uint32 CurrentBackBufferIndex = 0;
|
|
|
|
#if DXGI_MAX_SWAPCHAIN_INTERFACE >= 3
|
|
if (SwapChain3)
|
|
{
|
|
CurrentBackBufferIndex = SwapChain3->GetCurrentBackBufferIndex();
|
|
}
|
|
#endif
|
|
|
|
SetBackBufferIndex_RHIThread(CurrentBackBufferIndex);
|
|
|
|
#if D3D12RHI_USE_DUMMY_BACKBUFFER
|
|
// Create dummy back buffer which always reference to the actual RHI thread back buffer - can't be bound directly to D3D12
|
|
DummyBackBuffer_RenderThread = CreateDummyBackBufferTextures(Adapter, PixelFormat, SizeX, SizeY);
|
|
#else
|
|
SetBackBufferIndex_RenderThread(CurrentBackBufferIndex);
|
|
#endif
|
|
}
|
|
|
|
HRESULT FD3D12Viewport::PresentInternal(int32 SyncInterval)
|
|
{
|
|
UINT Flags = 0;
|
|
|
|
if(!SyncInterval && !bIsFullscreen && bAllowTearing)
|
|
{
|
|
Flags |= DXGI_PRESENT_ALLOW_TEARING;
|
|
}
|
|
|
|
FThreadHeartBeat::Get().PresentFrame();
|
|
|
|
if (SwapChain1)
|
|
{
|
|
// Ignore time spent waiting in Present. This function blocks based on GPU progress and space in the swap chain.
|
|
FRenderThreadIdleScope Scope(ERenderThreadIdleTypes::WaitingForGPUPresent);
|
|
#if !UE_BUILD_SHIPPING && PLATFORM_SUPPORTS_FLIP_TRACKING
|
|
static FRHIFlipDetails LastFlipFrame;
|
|
static DXGI_FRAME_STATISTICS LastStats = { 0 };
|
|
HRESULT GetStatHR;
|
|
DXGI_FRAME_STATISTICS stats = { 0 };
|
|
while (SUCCEEDED(GetStatHR = SwapChain1->GetFrameStatistics(&stats)) && (stats.PresentCount > LastFlipFrame.PresentIndex))
|
|
{
|
|
FRHIFlipDetails NewFlipFrame;
|
|
NewFlipFrame.PresentIndex = stats.PresentCount;
|
|
NewFlipFrame.VBlankTimeInCycles = stats.SyncQPCTime.QuadPart;
|
|
|
|
RHISetVsyncDebugInfo(NewFlipFrame);
|
|
|
|
LastFlipFrame = NewFlipFrame;
|
|
LastStats = stats;
|
|
}
|
|
#endif
|
|
|
|
HRESULT PresentHR;
|
|
{
|
|
TRACE_CPUPROFILER_EVENT_SCOPE(D3D12_Present);
|
|
PresentHR = SwapChain1->Present(SyncInterval, Flags);
|
|
}
|
|
|
|
UINT PresentID;
|
|
if (SUCCEEDED(SwapChain1->GetLastPresentCount(&PresentID)))
|
|
{
|
|
GRHIPresentCounter = PresentID;
|
|
}
|
|
else
|
|
{
|
|
GRHIPresentCounter++;
|
|
}
|
|
|
|
return PresentHR;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void FD3D12Viewport::EnableHDR()
|
|
{
|
|
if ( GRHISupportsHDROutput && IsHDREnabled() )
|
|
{
|
|
// Ensure we have the correct color space set.
|
|
EnsureColorSpace(DisplayColorGamut, DisplayOutputFormat);
|
|
}
|
|
}
|
|
|
|
void FD3D12Viewport::ShutdownHDR()
|
|
{
|
|
// Make sure to set the appropriate color space even if GRHISupportsHDROutput is false because we
|
|
// might have toggled HDR on and off in the windows settings
|
|
{
|
|
// Ensure we have the correct color space set.
|
|
EnsureColorSpace(EDisplayColorGamut::sRGB_D65, EDisplayOutputFormat::SDR_sRGB);
|
|
}
|
|
}
|
|
|
|
bool FD3D12Viewport::CurrentOutputSupportsHDR() const
|
|
{
|
|
// Default to no support.
|
|
bool bOutputSupportsHDR = false;
|
|
|
|
if (SwapChain4.GetReference())
|
|
{
|
|
// Output information is cached on the DXGI Factory. If it is stale we need to create
|
|
// a new factory which will re-enumerate the displays.
|
|
FD3D12Adapter* Adapter = GetParentAdapter();
|
|
IDXGIFactory2* DxgiFactory2 = Adapter->GetDXGIFactory2();
|
|
if (DxgiFactory2)
|
|
{
|
|
if (!DxgiFactory2->IsCurrent())
|
|
{
|
|
Adapter->CreateDXGIFactory(false);
|
|
}
|
|
|
|
check(Adapter->GetDXGIFactory2()->IsCurrent());
|
|
|
|
// Get information about the display we are presenting to.
|
|
TRefCountPtr<IDXGIOutput> Output;
|
|
VERIFYD3D12RESULT(SwapChain4->GetContainingOutput(Output.GetInitReference()));
|
|
|
|
TRefCountPtr<IDXGIOutput6> Output6;
|
|
if (SUCCEEDED(Output->QueryInterface(IID_PPV_ARGS(Output6.GetInitReference()))))
|
|
{
|
|
DXGI_OUTPUT_DESC1 OutputDesc;
|
|
VERIFYD3D12RESULT(Output6->GetDesc1(&OutputDesc));
|
|
|
|
// Check for HDR support on the display.
|
|
bOutputSupportsHDR = (OutputDesc.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020);
|
|
}
|
|
}
|
|
}
|
|
|
|
return bOutputSupportsHDR;
|
|
}
|
|
|
|
static const FString GetDXGIColorSpaceString(DXGI_COLOR_SPACE_TYPE ColorSpace)
|
|
{
|
|
switch (ColorSpace)
|
|
{
|
|
case DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709:
|
|
return TEXT("RGB_FULL_G22_NONE_P709");
|
|
case DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709:
|
|
return TEXT("RGB_FULL_G10_NONE_P709");
|
|
case DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020:
|
|
return TEXT("RGB_FULL_G2084_NONE_P2020");
|
|
case DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020:
|
|
return TEXT("RGB_FULL_G22_NONE_P2020");
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return FString::FromInt(ColorSpace);
|
|
};
|
|
|
|
void FD3D12Viewport::EnsureColorSpace(EDisplayColorGamut DisplayGamut, EDisplayOutputFormat OutputDevice)
|
|
{
|
|
if (!SwapChain4.GetReference())
|
|
{
|
|
return;
|
|
}
|
|
|
|
DXGI_COLOR_SPACE_TYPE NewColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709; // sRGB;
|
|
const bool bPrimaries2020 = (DisplayGamut == EDisplayColorGamut::Rec2020_D65);
|
|
|
|
// See console variable r.HDR.Display.OutputDevice.
|
|
switch (OutputDevice)
|
|
{
|
|
// Gamma 2.2
|
|
case EDisplayOutputFormat::SDR_sRGB:
|
|
case EDisplayOutputFormat::SDR_Rec709:
|
|
NewColorSpace = bPrimaries2020 ? DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020 : DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;
|
|
break;
|
|
|
|
// Gamma ST.2084
|
|
case EDisplayOutputFormat::HDR_ACES_1000nit_ST2084:
|
|
case EDisplayOutputFormat::HDR_ACES_2000nit_ST2084:
|
|
NewColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
|
|
break;
|
|
|
|
// Gamma 1.0 (Linear)
|
|
case EDisplayOutputFormat::HDR_ACES_1000nit_ScRGB:
|
|
case EDisplayOutputFormat::HDR_ACES_2000nit_ScRGB:
|
|
// Linear. Still supports expanded color space with values >1.0f and <0.0f.
|
|
// The actual range is determined by the pixel format (e.g. a UNORM format can only ever have 0-1).
|
|
NewColorSpace = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;
|
|
break;
|
|
}
|
|
|
|
if (ColorSpace != NewColorSpace)
|
|
{
|
|
uint32 ColorSpaceSupport = 0;
|
|
HRESULT hr = SwapChain4->CheckColorSpaceSupport(NewColorSpace, &ColorSpaceSupport);
|
|
FString NewColorSpaceName = GetDXGIColorSpaceString(NewColorSpace);
|
|
if (SUCCEEDED(hr) && ((ColorSpaceSupport & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT) != 0))
|
|
{
|
|
VERIFYD3D12RESULT(SwapChain4->SetColorSpace1(NewColorSpace));
|
|
UE_LOG(LogD3D12RHI, Verbose, TEXT("Setting color space on swap chain (%#016llx): %s"), SwapChain4.GetReference(), *NewColorSpaceName);
|
|
ColorSpace = NewColorSpace;
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogD3D12RHI, Error, TEXT("Warning: unabled to set color space %s to the swapchain: verify EDisplayOutputFormat / swapchain format"), *NewColorSpaceName);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FD3D12Viewport::OnResumeRendering()
|
|
{}
|
|
|
|
|
|
void FD3D12Viewport::OnSuspendRendering()
|
|
{}
|
|
|
|
bool FD3D12Viewport::IsPresentAllowed()
|
|
{
|
|
return !FD3D12DynamicRHI::GetD3DRHI()->RHIIsRenderingSuspended();
|
|
}
|
|
|
|
void FD3D12DynamicRHI::RHIGetDisplaysInformation(FDisplayInformationArray& OutDisplayInformation)
|
|
{
|
|
OutDisplayInformation.Append(DisplayList);
|
|
}
|
|
|
|
#include "Windows/HideWindowsPlatformTypes.h"
|