Files
UnrealEngineUWP/Engine/Source/Runtime/OpenGLDrv/Private/OpenGLViewport.cpp
christopher waters 3b18bce7f0 Fixing initial state issues after changing the FRHITextureCreateDesc creation.
#jira UE-151907
#rb mihnea.balta
#preflight 627e751b7c26e2477354c142

[CL 20182229 by christopher waters in ue5-main branch]
2022-05-13 11:45:30 -04:00

339 lines
9.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
OpenGLViewport.cpp: OpenGL viewport RHI implementation.
=============================================================================*/
#include "CoreMinimal.h"
#include "Stats/Stats.h"
#include "HAL/IConsoleManager.h"
#include "RHI.h"
#include "OpenGLDrv.h"
#include "OpenGLDrvPrivate.h"
void FOpenGLDynamicRHI::RHIGetSupportedResolution(uint32 &Width, uint32 &Height)
{
PlatformGetSupportedResolution(Width, Height);
}
bool FOpenGLDynamicRHI::RHIGetAvailableResolutions(FScreenResolutionArray& Resolutions, bool bIgnoreRefreshRate)
{
const bool Result = PlatformGetAvailableResolutions(Resolutions, bIgnoreRefreshRate);
if (Result)
{
Resolutions.Sort([](const FScreenResolutionRHI& L, const FScreenResolutionRHI& R)
{
if (L.Width != R.Width)
{
return L.Width < R.Width;
}
else if (L.Height != R.Height)
{
return L.Height < R.Height;
}
else
{
return L.RefreshRate < R.RefreshRate;
}
});
}
return Result;
}
/*=============================================================================
* The following RHI functions must be called from the main thread.
*=============================================================================*/
FViewportRHIRef FOpenGLDynamicRHI::RHICreateViewport(void* WindowHandle,uint32 SizeX,uint32 SizeY,bool bIsFullscreen,EPixelFormat PreferredPixelFormat)
{
check(IsInGameThread());
// SCOPED_SUSPEND_RENDERING_THREAD(true);
// Use a default pixel format if none was specified
PreferredPixelFormat = RHIPreferredPixelFormatHint(PreferredPixelFormat);
return new FOpenGLViewport(this,WindowHandle,SizeX,SizeY,bIsFullscreen,PreferredPixelFormat);
}
void FOpenGLDynamicRHI::RHIResizeViewport(FRHIViewport* ViewportRHI,uint32 SizeX,uint32 SizeY,bool bIsFullscreen)
{
FOpenGLViewport* Viewport = ResourceCast(ViewportRHI);
check( IsInGameThread() );
// SCOPED_SUSPEND_RENDERING_THREAD(true);
Viewport->Resize(SizeX,SizeY,bIsFullscreen);
}
EPixelFormat FOpenGLDynamicRHI::RHIPreferredPixelFormatHint(EPixelFormat PreferredPixelFormat)
{
return FOpenGL::PreferredPixelFormatHint(PreferredPixelFormat);
}
void FOpenGLDynamicRHI::RHITick( float DeltaTime )
{
}
/*=============================================================================
* Viewport functions.
*=============================================================================*/
void FOpenGLDynamicRHI::RHIBeginDrawingViewport(FRHIViewport* ViewportRHI, FRHITexture* RenderTarget)
{
VERIFY_GL_SCOPE();
FOpenGLViewport* Viewport = ResourceCast(ViewportRHI);
SCOPE_CYCLE_COUNTER(STAT_OpenGLPresentTime);
check(!DrawingViewport);
DrawingViewport = Viewport;
bRevertToSharedContextAfterDrawingViewport = false;
EOpenGLCurrentContext CurrentContext = PlatformOpenGLCurrentContext( PlatformDevice );
if( CurrentContext != CONTEXT_Rendering )
{
check(CurrentContext == CONTEXT_Shared);
check(!bIsRenderingContextAcquired || !GUseThreadedRendering);
bRevertToSharedContextAfterDrawingViewport = true;
PlatformRenderingContextSetup(PlatformDevice);
}
// Set the render target and viewport.
if( RenderTarget )
{
FRHIRenderTargetView RTV(RenderTarget, ERenderTargetLoadAction::ELoad);
SetRenderTargets(1, &RTV, nullptr);
}
else
{
FRHIRenderTargetView RTV(DrawingViewport->GetBackBuffer(), ERenderTargetLoadAction::ELoad);
SetRenderTargets(1, &RTV, nullptr);
}
if (IsValidRef(CustomPresent))
{
CustomPresent->BeginDrawing();
}
}
void FOpenGLDynamicRHI::RHIEndDrawingViewport(FRHIViewport* ViewportRHI,bool bPresent,bool bLockToVsync)
{
VERIFY_GL_SCOPE();
FOpenGLViewport* Viewport = ResourceCast(ViewportRHI);
SCOPE_CYCLE_COUNTER(STAT_OpenGLPresentTime);
uint32 IdleStart = FPlatformTime::Cycles();
check(DrawingViewport.GetReference() == Viewport);
FOpenGLTexture* BackBuffer = Viewport->GetBackBuffer();
FOpenGLContextState& ContextState = GetContextStateForCurrentContext();
if (ContextState.bScissorEnabled)
{
ContextState.bScissorEnabled = false;
glDisable(GL_SCISSOR_TEST);
}
bool bNeedFinishFrame = PlatformBlitToViewport(PlatformDevice,
*Viewport,
BackBuffer->GetSizeX(),
BackBuffer->GetSizeY(),
bPresent,
bLockToVsync
);
// Always consider the Framebuffer in the rendering context dirty after the blit
RenderingContextState.Framebuffer = -1;
DrawingViewport = NULL;
// Don't wait on the GPU when using SLI, let the driver determine how many frames behind the GPU should be allowed to get
if (GNumAlternateFrameRenderingGroups == 1)
{
if (bNeedFinishFrame)
{
static const auto CFinishFrameVar = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.FinishCurrentFrame"));
if (!CFinishFrameVar->GetValueOnRenderThread())
{
// Wait for the GPU to finish rendering the previous frame before finishing this frame.
Viewport->WaitForFrameEventCompletion();
Viewport->IssueFrameEvent();
}
else
{
// Finish current frame immediately to reduce latency
Viewport->IssueFrameEvent();
Viewport->WaitForFrameEventCompletion();
}
}
// If the input latency timer has been triggered, block until the GPU is completely
// finished displaying this frame and calculate the delta time.
if ( GInputLatencyTimer.RenderThreadTrigger )
{
Viewport->WaitForFrameEventCompletion();
uint32 EndTime = FPlatformTime::Cycles();
GInputLatencyTimer.DeltaTime = EndTime - GInputLatencyTimer.StartTime;
GInputLatencyTimer.RenderThreadTrigger = false;
}
}
if (bRevertToSharedContextAfterDrawingViewport)
{
PlatformSharedContextSetup(PlatformDevice);
bRevertToSharedContextAfterDrawingViewport = false;
}
uint32 ThisCycles = FPlatformTime::Cycles() - IdleStart;
if (IsInRHIThread())
{
GWorkingRHIThreadStallTime += ThisCycles;
}
else if (IsInActualRenderingThread())
{
GRenderThreadIdle[ERenderThreadIdleTypes::WaitingForGPUPresent] += ThisCycles;
GRenderThreadNumIdle[ERenderThreadIdleTypes::WaitingForGPUPresent]++;
}
// TODO: find better location to poll this, or create programs on separate thread. Gil had a prototype of this.
FOpenGLProgramBinaryCache::CheckPendingGLProgramCreateRequests();
FTextureEvictionLRU::Get().TickEviction();
}
FTexture2DRHIRef FOpenGLDynamicRHI::RHIGetViewportBackBuffer(FRHIViewport* ViewportRHI)
{
FOpenGLViewport* Viewport = ResourceCast(ViewportRHI);
return Viewport->GetBackBuffer();
}
void FOpenGLDynamicRHI::RHIAdvanceFrameForGetViewportBackBuffer(FRHIViewport* Viewport)
{
}
FOpenGLViewport::FOpenGLViewport(FOpenGLDynamicRHI* InOpenGLRHI,void* InWindowHandle,uint32 InSizeX,uint32 InSizeY,bool bInIsFullscreen,EPixelFormat PreferredPixelFormat)
: OpenGLRHI(InOpenGLRHI)
, OpenGLContext(NULL)
, SizeX(0)
, SizeY(0)
, bIsFullscreen(false)
, PixelFormat(PreferredPixelFormat)
, bIsValid(true)
{
check(OpenGLRHI);
// @todo lumin: Add a "PLATFORM_HAS_NO_NATIVE_WINDOW" or something
#if !PLATFORM_ANDROID
check(InWindowHandle);
#endif
check(IsInGameThread());
// flush out old errors.
PlatformGlGetError();
OpenGLRHI->Viewports.Add(this);
check(PlatformOpenGLCurrentContext(OpenGLRHI->PlatformDevice) == CONTEXT_Shared);
OpenGLContext = PlatformCreateOpenGLContext(OpenGLRHI->PlatformDevice, InWindowHandle);
Resize(InSizeX, InSizeY, bInIsFullscreen);
check(PlatformOpenGLCurrentContext(OpenGLRHI->PlatformDevice) == CONTEXT_Shared);
ENQUEUE_RENDER_COMMAND(CreateFrameSyncEvent)([this](FRHICommandListImmediate& RHICmdList)
{
RunOnGLRenderContextThread([this]()
{
FrameSyncEvent = MakeUnique<FOpenGLEventQuery>();
});
});
}
FOpenGLViewport::~FOpenGLViewport()
{
check(IsInRenderingThread() || IsInRHIThread());
if (bIsFullscreen)
{
PlatformRestoreDesktopDisplayMode();
}
// Release back buffer, before OpenGL context becomes invalid, making it impossible
BackBuffer.SafeRelease();
check(!IsValidRef(BackBuffer));
RunOnGLRenderContextThread([&]()
{
FrameSyncEvent = nullptr;
PlatformDestroyOpenGLContext(OpenGLRHI->PlatformDevice, OpenGLContext);
}, true);
OpenGLContext = NULL;
OpenGLRHI->Viewports.Remove(this);
}
void FOpenGLViewport::WaitForFrameEventCompletion()
{
VERIFY_GL_SCOPE();
FrameSyncEvent->WaitForCompletion();
}
void FOpenGLViewport::IssueFrameEvent()
{
VERIFY_GL_SCOPE();
FrameSyncEvent->IssueEvent();
}
void FOpenGLViewport::Resize(uint32 InSizeX,uint32 InSizeY,bool bInIsFullscreen)
{
check(IsInGameThread());
if ((InSizeX == SizeX) && (InSizeY == SizeY) && (bInIsFullscreen == bIsFullscreen))
{
return;
}
SizeX = InSizeX;
SizeY = InSizeY;
bool bWasFullscreen = bIsFullscreen;
bIsFullscreen = bInIsFullscreen;
ENQUEUE_RENDER_COMMAND(ResizeViewport)([this, InSizeX, InSizeY, bInIsFullscreen, bWasFullscreen](FRHICommandListImmediate& RHICmdList)
{
if (IsValidRef(CustomPresent))
{
CustomPresent->OnBackBufferResize();
}
BackBuffer.SafeRelease(); // when the rest of the engine releases it, its framebuffers will be released too (those the engine knows about)
BackBuffer = PlatformCreateBuiltinBackBuffer(OpenGLRHI, InSizeX, InSizeY);
if (!BackBuffer)
{
const FRHITextureCreateDesc Desc =
FRHITextureCreateDesc::Create2D(TEXT("FOpenGLViewport"), InSizeX, InSizeY, PixelFormat)
.SetClearValue(FClearValueBinding::Transparent)
.SetFlags(ETextureCreateFlags::RenderTargetable | ETextureCreateFlags::ResolveTargetable)
.DetermineInititialState();
BackBuffer = new FOpenGLTexture(Desc);
}
RHICmdList.EnqueueLambda([=](FRHICommandListImmediate&)
{
PlatformResizeGLContext(OpenGLRHI->PlatformDevice, OpenGLContext, InSizeX, InSizeY, bInIsFullscreen, bWasFullscreen, BackBuffer->Target, BackBuffer->GetResource());
});
});
}
void* FOpenGLViewport::GetNativeWindow(void** AddParam) const
{
return PlatformGetWindow(OpenGLContext, AddParam);
}