You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#jira UE-151907 #rb mihnea.balta #preflight 627e751b7c26e2477354c142 [CL 20182229 by christopher waters in ue5-main branch]
1025 lines
28 KiB
C++
1025 lines
28 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
OpenGLWindowsLoader.cpp: Manual loading of OpenGL functions from DLL.
|
|
=============================================================================*/
|
|
|
|
#include "OpenGLDrvPrivate.h"
|
|
#include "Misc/ScopeLock.h"
|
|
|
|
/*------------------------------------------------------------------------------
|
|
OpenGL function pointers.
|
|
------------------------------------------------------------------------------*/
|
|
|
|
#define DEFINE_GL_ENTRYPOINTS(Type,Func) Type Func = NULL;
|
|
ENUM_GL_ENTRYPOINTS_ALL(DEFINE_GL_ENTRYPOINTS);
|
|
#undef DEFINE_GL_ENTRYPOINTS
|
|
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
|
|
|
|
extern PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT_ProcAddress; // set in OpenGLDevice.cpp
|
|
|
|
bool GRunningUnderRenderDoc = false;
|
|
|
|
/*------------------------------------------------------------------------------
|
|
OpenGL context management.
|
|
------------------------------------------------------------------------------*/
|
|
|
|
static void ContextMakeCurrent( HDC DC, HGLRC RC )
|
|
{
|
|
BOOL Result = wglMakeCurrent( DC, RC );
|
|
if (!Result)
|
|
{
|
|
Result = wglMakeCurrent( nullptr, nullptr );
|
|
}
|
|
check(Result);
|
|
}
|
|
|
|
static HGLRC GetCurrentContext()
|
|
{
|
|
return wglGetCurrentContext();
|
|
}
|
|
|
|
|
|
/** Platform specific OpenGL context. */
|
|
struct FPlatformOpenGLContext
|
|
{
|
|
HWND WindowHandle;
|
|
HDC DeviceContext;
|
|
HGLRC OpenGLContext;
|
|
bool bReleaseWindowOnDestroy;
|
|
int32 SyncInterval;
|
|
GLuint ViewportFramebuffer;
|
|
GLuint VertexArrayObject; // one has to be generated and set for each context (OpenGL 3.2 Core requirements)
|
|
GLuint BackBufferResource;
|
|
GLenum BackBufferTarget;
|
|
};
|
|
|
|
class FScopeContext
|
|
{
|
|
public:
|
|
FScopeContext( FPlatformOpenGLContext* Context )
|
|
{
|
|
check(Context);
|
|
PrevDC = wglGetCurrentDC();
|
|
PrevContext = GetCurrentContext();
|
|
bSameDC = (PrevDC == Context->DeviceContext);
|
|
bSameDCAndContext = (PrevContext == Context->OpenGLContext) && bSameDC;
|
|
if (!bSameDCAndContext)
|
|
{
|
|
// if (PrevContext)
|
|
// {
|
|
// glFlush();
|
|
// }
|
|
// no need to glFlush() on Windows, it does flush by itself before switching contexts
|
|
ContextMakeCurrent(Context->DeviceContext,Context->OpenGLContext);
|
|
}
|
|
}
|
|
|
|
~FScopeContext( void )
|
|
{
|
|
if (!bSameDCAndContext)
|
|
{
|
|
// glFlush(); // not needed on Windows, it does flush by itself before switching contexts
|
|
if (PrevContext)
|
|
{
|
|
ContextMakeCurrent(PrevDC,PrevContext);
|
|
}
|
|
else
|
|
{
|
|
ContextMakeCurrent(NULL,NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ContextWasAlreadyActive() const
|
|
{
|
|
return bSameDCAndContext;
|
|
}
|
|
|
|
bool ContextsShareSameDC() const
|
|
{
|
|
return bSameDC;
|
|
}
|
|
|
|
private:
|
|
HDC PrevDC;
|
|
HGLRC PrevContext;
|
|
bool bSameDCAndContext;
|
|
bool bSameDC;
|
|
};
|
|
|
|
void DeleteQueriesForCurrentContext( HGLRC Context );
|
|
|
|
/**
|
|
* A dummy wndproc.
|
|
*/
|
|
static LRESULT CALLBACK PlatformDummyGLWndproc(HWND hWnd, uint32 Message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return DefWindowProc(hWnd, Message, wParam, lParam);
|
|
}
|
|
|
|
/**
|
|
* Initialize a pixel format descriptor for the given window handle.
|
|
*/
|
|
static void PlatformInitPixelFormatForDevice(HDC DeviceContext, bool bTryIsDummyContext)
|
|
{
|
|
// Pixel format descriptor for the context.
|
|
PIXELFORMATDESCRIPTOR PixelFormatDesc;
|
|
FMemory::Memzero(PixelFormatDesc);
|
|
PixelFormatDesc.nSize = sizeof(PIXELFORMATDESCRIPTOR);
|
|
PixelFormatDesc.nVersion = 1;
|
|
PixelFormatDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
|
|
PixelFormatDesc.iPixelType = PFD_TYPE_RGBA;
|
|
PixelFormatDesc.cColorBits = 32;
|
|
PixelFormatDesc.cDepthBits = 0;
|
|
PixelFormatDesc.cStencilBits = 0;
|
|
PixelFormatDesc.iLayerType = PFD_MAIN_PLANE;
|
|
|
|
static bool bRequestedQuadBufferStereo = FParse::Param(FCommandLine::Get(), TEXT("quad_buffer_stereo"));
|
|
if (bRequestedQuadBufferStereo)
|
|
{
|
|
PixelFormatDesc.dwFlags = PixelFormatDesc.dwFlags | PFD_STEREO;
|
|
}
|
|
|
|
// Set the pixel format and create the context.
|
|
int32 PixelFormat = ChoosePixelFormat(DeviceContext, &PixelFormatDesc);
|
|
if (!PixelFormat || !SetPixelFormat(DeviceContext, PixelFormat, &PixelFormatDesc))
|
|
{
|
|
UE_LOG(LogRHI, Fatal,TEXT("Failed to set pixel format for device context."));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a dummy window used to construct OpenGL contexts.
|
|
*/
|
|
static void PlatformCreateDummyGLWindow(FPlatformOpenGLContext* OutContext)
|
|
{
|
|
const TCHAR* WindowClassName = TEXT("DummyGLWindow");
|
|
|
|
// Register a dummy window class.
|
|
static bool bInitializedWindowClass = false;
|
|
if (!bInitializedWindowClass)
|
|
{
|
|
WNDCLASS wc;
|
|
|
|
bInitializedWindowClass = true;
|
|
FMemory::Memzero(wc);
|
|
wc.style = CS_OWNDC;
|
|
wc.lpfnWndProc = PlatformDummyGLWndproc;
|
|
wc.cbClsExtra = 0;
|
|
wc.cbWndExtra = 0;
|
|
wc.hInstance = NULL;
|
|
wc.hIcon = NULL;
|
|
wc.hCursor = NULL;
|
|
wc.hbrBackground = (HBRUSH)(COLOR_MENUTEXT);
|
|
wc.lpszMenuName = NULL;
|
|
wc.lpszClassName = WindowClassName;
|
|
ATOM ClassAtom = ::RegisterClass(&wc);
|
|
check(ClassAtom);
|
|
}
|
|
|
|
int32 WinX = 0;
|
|
int32 WinY = 0;
|
|
FParse::Value(FCommandLine::Get(), TEXT("WinX="), WinX);
|
|
FParse::Value(FCommandLine::Get(), TEXT("WinY="), WinY);
|
|
|
|
// Create a dummy window.
|
|
OutContext->WindowHandle = CreateWindowEx(
|
|
WS_EX_WINDOWEDGE,
|
|
WindowClassName,
|
|
NULL,
|
|
WS_POPUP,
|
|
WinX, WinY, 1, 1,
|
|
NULL, NULL, NULL, NULL);
|
|
check(OutContext->WindowHandle);
|
|
OutContext->bReleaseWindowOnDestroy = true;
|
|
|
|
// Get the device context.
|
|
OutContext->DeviceContext = GetDC(OutContext->WindowHandle);
|
|
check(OutContext->DeviceContext);
|
|
PlatformInitPixelFormatForDevice(OutContext->DeviceContext, true);
|
|
}
|
|
|
|
/**
|
|
* Set OpenGL Context version to fixed version
|
|
*/
|
|
|
|
static void GetOpenGLVersionForCoreProfile(int& OutMajorVersion, int& OutMinorVersion)
|
|
{
|
|
// Always initialize GL context with version 4.3, it's the only GL desktop version we support now
|
|
OutMajorVersion = 4;
|
|
OutMinorVersion = 3;
|
|
}
|
|
|
|
/**
|
|
* Enable/Disable debug context from the commandline
|
|
*/
|
|
static bool PlatformOpenGLDebugCtx()
|
|
{
|
|
#if UE_BUILD_DEBUG
|
|
return ! FParse::Param(FCommandLine::Get(),TEXT("openglNoDebug"));
|
|
#else
|
|
return FParse::Param(FCommandLine::Get(),TEXT("openglDebug"));;
|
|
#endif
|
|
}
|
|
|
|
|
|
/**
|
|
* Create a core profile OpenGL context.
|
|
*/
|
|
static void PlatformCreateOpenGLContextCore(FPlatformOpenGLContext* OutContext, int MajorVersion, int MinorVersion, HGLRC InParentContext)
|
|
{
|
|
check(wglCreateContextAttribsARB);
|
|
check(OutContext);
|
|
check(OutContext->DeviceContext);
|
|
|
|
OutContext->SyncInterval = -1; // invalid value to enforce setup on first buffer swap
|
|
OutContext->ViewportFramebuffer = 0;
|
|
|
|
int DebugFlag = 0;
|
|
|
|
if ( PlatformOpenGLDebugCtx())
|
|
{
|
|
DebugFlag = WGL_CONTEXT_DEBUG_BIT_ARB;
|
|
}
|
|
|
|
#if !EMULATE_ES31
|
|
int AttribList[] =
|
|
{
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, MajorVersion,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, MinorVersion,
|
|
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB | DebugFlag,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
0
|
|
};
|
|
#else
|
|
int AttribList[] =
|
|
{
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
|
|
WGL_CONTEXT_FLAGS_ARB, DebugFlag,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_ES2_PROFILE_BIT_EXT,
|
|
0
|
|
};
|
|
#endif
|
|
|
|
OutContext->OpenGLContext = wglCreateContextAttribsARB(OutContext->DeviceContext, InParentContext, AttribList);
|
|
}
|
|
|
|
void PlatformReleaseOpenGLContext(FPlatformOpenGLDevice* Device, FPlatformOpenGLContext* Context);
|
|
|
|
extern void OnQueryInvalidation( void );
|
|
|
|
/** Platform specific OpenGL device. */
|
|
struct FPlatformOpenGLDevice
|
|
{
|
|
FPlatformOpenGLContext SharedContext;
|
|
FPlatformOpenGLContext RenderingContext;
|
|
TArray<FPlatformOpenGLContext*> ViewportContexts;
|
|
bool TargetDirty;
|
|
|
|
/** Guards against operating on viewport contexts from more than one thread at the same time. */
|
|
FCriticalSection* ContextUsageGuard;
|
|
|
|
FPlatformOpenGLDevice()
|
|
: TargetDirty(true)
|
|
{
|
|
extern void InitDebugContext();
|
|
ContextUsageGuard = new FCriticalSection;
|
|
|
|
int MajorVersion = 0;
|
|
int MinorVersion = 0;
|
|
GetOpenGLVersionForCoreProfile(MajorVersion, MinorVersion);
|
|
|
|
// Need to call this before we set the debug callback, otherwise if we're not running under RD, the debug extension will assert (invalid enum)
|
|
GRunningUnderRenderDoc = glIsEnabled(GL_DEBUG_TOOL_EXT) != GL_FALSE;
|
|
|
|
PlatformCreateDummyGLWindow(&SharedContext);
|
|
PlatformCreateOpenGLContextCore(&SharedContext, MajorVersion, MinorVersion, NULL);
|
|
check(SharedContext.OpenGLContext)
|
|
{
|
|
FScopeContext ScopeContext(&SharedContext);
|
|
InitDebugContext();
|
|
glGenVertexArrays(1,&SharedContext.VertexArrayObject);
|
|
glBindVertexArray(SharedContext.VertexArrayObject);
|
|
InitDefaultGLContextState();
|
|
glGenFramebuffers(1, &SharedContext.ViewportFramebuffer);
|
|
}
|
|
|
|
PlatformCreateDummyGLWindow(&RenderingContext);
|
|
PlatformCreateOpenGLContextCore(&RenderingContext, MajorVersion, MinorVersion, SharedContext.OpenGLContext);
|
|
check(RenderingContext.OpenGLContext);
|
|
{
|
|
FScopeContext ScopeContext(&RenderingContext);
|
|
InitDebugContext();
|
|
glGenVertexArrays(1,&RenderingContext.VertexArrayObject);
|
|
glBindVertexArray(RenderingContext.VertexArrayObject);
|
|
InitDefaultGLContextState();
|
|
glGenFramebuffers(1, &RenderingContext.ViewportFramebuffer);
|
|
}
|
|
|
|
ContextMakeCurrent(SharedContext.DeviceContext, SharedContext.OpenGLContext);
|
|
}
|
|
|
|
~FPlatformOpenGLDevice()
|
|
{
|
|
check(ViewportContexts.Num()==0);
|
|
|
|
ContextMakeCurrent(NULL,NULL);
|
|
|
|
OnQueryInvalidation();
|
|
PlatformReleaseOpenGLContext(this,&RenderingContext);
|
|
PlatformReleaseOpenGLContext(this,&SharedContext);
|
|
|
|
delete ContextUsageGuard;
|
|
}
|
|
};
|
|
|
|
FPlatformOpenGLDevice* PlatformCreateOpenGLDevice()
|
|
{
|
|
return new FPlatformOpenGLDevice;
|
|
}
|
|
|
|
bool PlatformCanEnableGPUCapture()
|
|
{
|
|
return GRunningUnderRenderDoc;
|
|
}
|
|
|
|
void PlatformDestroyOpenGLDevice(FPlatformOpenGLDevice* Device)
|
|
{
|
|
delete Device;
|
|
}
|
|
|
|
/**
|
|
* Create an OpenGL context.
|
|
*/
|
|
FPlatformOpenGLContext* PlatformCreateOpenGLContext(FPlatformOpenGLDevice* Device, void* InWindowHandle)
|
|
{
|
|
check(InWindowHandle);
|
|
|
|
Device->TargetDirty = true;
|
|
|
|
FPlatformOpenGLContext* Context = new FPlatformOpenGLContext;
|
|
Context->WindowHandle = (HWND)InWindowHandle;
|
|
Context->bReleaseWindowOnDestroy = false;
|
|
Context->DeviceContext = GetDC(Context->WindowHandle);
|
|
check(Context->DeviceContext);
|
|
PlatformInitPixelFormatForDevice(Context->DeviceContext, false);
|
|
|
|
int MajorVersion = 0;
|
|
int MinorVersion = 0;
|
|
GetOpenGLVersionForCoreProfile(MajorVersion, MinorVersion);
|
|
|
|
PlatformCreateOpenGLContextCore(Context, MajorVersion, MinorVersion, Device->SharedContext.OpenGLContext);
|
|
check(Context->OpenGLContext);
|
|
{
|
|
FScopeContext Scope(Context);
|
|
InitDefaultGLContextState();
|
|
glGenFramebuffers(1, &Context->ViewportFramebuffer);
|
|
}
|
|
|
|
Device->ViewportContexts.Add(Context);
|
|
return Context;
|
|
}
|
|
|
|
/**
|
|
* Release an OpenGL context.
|
|
*/
|
|
void PlatformReleaseOpenGLContext(FPlatformOpenGLDevice* Device, FPlatformOpenGLContext* Context)
|
|
{
|
|
check(Context && Context->OpenGLContext);
|
|
|
|
Device->ViewportContexts.RemoveSingle(Context);
|
|
Device->TargetDirty = true;
|
|
|
|
bool bActiveContextWillBeReleased = false;
|
|
bool bSharedDC = false;
|
|
|
|
{
|
|
FScopeLock ScopeLock(Device->ContextUsageGuard);
|
|
{
|
|
FScopeContext ScopeContext(Context);
|
|
|
|
bActiveContextWillBeReleased = ScopeContext.ContextWasAlreadyActive();
|
|
bSharedDC = ScopeContext.ContextsShareSameDC();
|
|
|
|
DeleteQueriesForCurrentContext(Context->OpenGLContext);
|
|
glBindVertexArray(0);
|
|
glDeleteVertexArrays(1, &Context->VertexArrayObject);
|
|
|
|
if (Context->ViewportFramebuffer)
|
|
{
|
|
glDeleteFramebuffers(1,&Context->ViewportFramebuffer); // this can be done from any context shared with ours, as long as it's not nil.
|
|
Context->ViewportFramebuffer = 0;
|
|
}
|
|
}
|
|
|
|
wglDeleteContext(Context->OpenGLContext);
|
|
Context->OpenGLContext = NULL;
|
|
}
|
|
|
|
check(Context->DeviceContext);
|
|
|
|
if (bActiveContextWillBeReleased)
|
|
{
|
|
wglMakeCurrent( NULL, NULL );
|
|
}
|
|
ReleaseDC(Context->WindowHandle, Context->DeviceContext);
|
|
Context->DeviceContext = NULL;
|
|
|
|
if (PlatformOpenGLCurrentContext(Device) == CONTEXT_Rendering && bSharedDC)
|
|
{
|
|
// The rendering context has been made current using the DC of the now destroyed context. Since this DC has been released the current context will be invalid.
|
|
// To properly set the rendering context we must make current here with it's own DC.
|
|
ContextMakeCurrent(Device->RenderingContext.DeviceContext, Device->RenderingContext.OpenGLContext);
|
|
}
|
|
|
|
check(Context->WindowHandle);
|
|
|
|
if (Context->bReleaseWindowOnDestroy)
|
|
{
|
|
DestroyWindow(Context->WindowHandle);
|
|
}
|
|
Context->WindowHandle = NULL;
|
|
}
|
|
|
|
/**
|
|
* Destroy an OpenGL context.
|
|
*/
|
|
void PlatformDestroyOpenGLContext(FPlatformOpenGLDevice* Device, FPlatformOpenGLContext* Context)
|
|
{
|
|
PlatformReleaseOpenGLContext(Device, Context);
|
|
delete Context;
|
|
}
|
|
|
|
/**
|
|
* Main function for transferring data to on-screen buffers.
|
|
* On Windows it temporarily switches OpenGL context, on Mac only context's output view.
|
|
*/
|
|
bool PlatformBlitToViewport( FPlatformOpenGLDevice* Device, const FOpenGLViewport& Viewport, uint32 BackbufferSizeX, uint32 BackbufferSizeY, bool bPresent,bool bLockToVsync)
|
|
{
|
|
int32 SyncInterval = RHIGetSyncInterval();
|
|
|
|
FPlatformOpenGLContext* const Context = Viewport.GetGLContext();
|
|
|
|
check(Context && Context->DeviceContext);
|
|
|
|
if (FOpenGL::IsAndroidGLESCompatibilityModeEnabled())
|
|
{
|
|
glDisable(GL_FRAMEBUFFER_SRGB);
|
|
|
|
int32 RealSyncInterval = bLockToVsync ? SyncInterval : 0;
|
|
if (wglSwapIntervalEXT_ProcAddress && Context->SyncInterval != RealSyncInterval)
|
|
{
|
|
wglSwapIntervalEXT_ProcAddress(RealSyncInterval);
|
|
Context->SyncInterval = RealSyncInterval;
|
|
}
|
|
|
|
::SwapBuffers(Context->DeviceContext);
|
|
return true;
|
|
}
|
|
|
|
FScopeLock ScopeLock(Device->ContextUsageGuard);
|
|
{
|
|
FPlatformOpenGLContext TempContext = *Context;
|
|
if (Device->ViewportContexts.Num() == 1)
|
|
{
|
|
TempContext.OpenGLContext = Device->RenderingContext.OpenGLContext;
|
|
TempContext.ViewportFramebuffer = Device->RenderingContext.ViewportFramebuffer;
|
|
}
|
|
FScopeContext ScopeContext(&TempContext);
|
|
|
|
GLuint vfb = TempContext.ViewportFramebuffer;
|
|
if (Viewport.GetCustomPresent())
|
|
{
|
|
Device->TargetDirty = false;
|
|
glDisable(GL_FRAMEBUFFER_SRGB);
|
|
bool bShouldPresent = Viewport.GetCustomPresent()->Present(SyncInterval);
|
|
glEnable(GL_FRAMEBUFFER_SRGB);
|
|
if (!bShouldPresent)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Device->TargetDirty = true;
|
|
}
|
|
}
|
|
|
|
if (Device->ViewportContexts.Num() == 1 && Device->TargetDirty)
|
|
{
|
|
glBindFramebuffer(GL_FRAMEBUFFER, TempContext.ViewportFramebuffer);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, Context->BackBufferTarget, Context->BackBufferResource, 0);
|
|
|
|
FOpenGL::CheckFrameBuffer();
|
|
Device->TargetDirty = false;
|
|
}
|
|
|
|
glDisable(GL_FRAMEBUFFER_SRGB);
|
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
|
FOpenGL::DrawBuffer(GL_BACK);
|
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, TempContext.ViewportFramebuffer);
|
|
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
|
|
|
glBlitFramebuffer(
|
|
0, 0, BackbufferSizeX, BackbufferSizeY,
|
|
0, BackbufferSizeY, BackbufferSizeX, 0,
|
|
GL_COLOR_BUFFER_BIT,
|
|
GL_NEAREST
|
|
);
|
|
glEnable(GL_FRAMEBUFFER_SRGB);
|
|
|
|
if (bPresent)
|
|
{
|
|
int32 RealSyncInterval = bLockToVsync ? SyncInterval : 0;
|
|
if (wglSwapIntervalEXT_ProcAddress && Context->SyncInterval != RealSyncInterval)
|
|
{
|
|
wglSwapIntervalEXT_ProcAddress(RealSyncInterval);
|
|
Context->SyncInterval = RealSyncInterval;
|
|
}
|
|
|
|
::SwapBuffers(Context->DeviceContext);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void PlatformFlushIfNeeded()
|
|
{
|
|
glFinish();
|
|
}
|
|
|
|
void PlatformRebindResources(FPlatformOpenGLDevice* Device)
|
|
{
|
|
// @todo: Figure out if we need to rebind frame & renderbuffers after switching contexts
|
|
}
|
|
|
|
FPlatformOpenGLContext* PlatformGetOpenGLRenderingContext(FPlatformOpenGLDevice* Device)
|
|
{
|
|
return &Device->RenderingContext;
|
|
}
|
|
|
|
void PlatformRenderingContextSetup(FPlatformOpenGLDevice* Device)
|
|
{
|
|
check(Device && Device->RenderingContext.DeviceContext && Device->RenderingContext.OpenGLContext);
|
|
|
|
if (GetCurrentContext())
|
|
{
|
|
glFlush();
|
|
}
|
|
if (Device->ViewportContexts.Num() == 1)
|
|
{
|
|
// use the HDC of the window, to reduce context swap overhead
|
|
ContextMakeCurrent(Device->ViewportContexts[0]->DeviceContext, Device->RenderingContext.OpenGLContext);
|
|
}
|
|
else
|
|
{
|
|
ContextMakeCurrent(Device->RenderingContext.DeviceContext, Device->RenderingContext.OpenGLContext);
|
|
}
|
|
}
|
|
|
|
void PlatformSharedContextSetup(FPlatformOpenGLDevice* Device)
|
|
{
|
|
check(Device && Device->SharedContext.DeviceContext && Device->SharedContext.OpenGLContext);
|
|
|
|
// no need to glFlush() on Windows, it does flush by itself before switching contexts
|
|
ContextMakeCurrent(Device->SharedContext.DeviceContext, Device->SharedContext.OpenGLContext);
|
|
|
|
}
|
|
|
|
void PlatformNULLContextSetup()
|
|
{
|
|
if (wglGetCurrentDC())
|
|
{
|
|
// no need to glFlush() on Windows, it does flush by itself before switching contexts
|
|
ContextMakeCurrent(NULL, NULL);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resize the GL context.
|
|
*/
|
|
void PlatformResizeGLContext( FPlatformOpenGLDevice* Device, FPlatformOpenGLContext* Context, uint32 SizeX, uint32 SizeY, bool bFullscreen, bool bWasFullscreen, GLenum BackBufferTarget, GLuint BackBufferResource)
|
|
{
|
|
FScopeLock ScopeLock(Device->ContextUsageGuard);
|
|
{
|
|
uint32 WindowStyle = WS_CAPTION | WS_SYSMENU;
|
|
uint32 WindowStyleEx = 0;
|
|
HWND InsertAfter = HWND_NOTOPMOST;
|
|
|
|
if (bFullscreen)
|
|
{
|
|
// Get the monitor info from the window handle.
|
|
HMONITOR hMonitor = MonitorFromWindow(Context->WindowHandle, MONITOR_DEFAULTTOPRIMARY);
|
|
MONITORINFOEX MonitorInfo;
|
|
memset(&MonitorInfo, 0, sizeof(MONITORINFOEX));
|
|
MonitorInfo.cbSize = sizeof(MONITORINFOEX);
|
|
GetMonitorInfo(hMonitor, &MonitorInfo);
|
|
|
|
DEVMODE Mode;
|
|
Mode.dmSize = sizeof(DEVMODE);
|
|
Mode.dmBitsPerPel = 32;
|
|
Mode.dmPelsWidth = SizeX;
|
|
Mode.dmPelsHeight = SizeY;
|
|
Mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
|
|
|
|
// Turn on fullscreen mode for the current monitor
|
|
ChangeDisplaySettingsEx(MonitorInfo.szDevice, &Mode, NULL, CDS_FULLSCREEN, NULL);
|
|
|
|
WindowStyle = WS_POPUP;
|
|
WindowStyleEx = WS_EX_APPWINDOW | WS_EX_TOPMOST;
|
|
InsertAfter = HWND_TOPMOST;
|
|
}
|
|
else if (bWasFullscreen)
|
|
{
|
|
ChangeDisplaySettings(NULL, 0);
|
|
}
|
|
|
|
Device->TargetDirty = true;
|
|
Context->BackBufferResource = BackBufferResource;
|
|
Context->BackBufferTarget = BackBufferTarget;
|
|
|
|
//SetWindowLong(Context->WindowHandle, GWL_STYLE, WindowStyle);
|
|
//SetWindowLong(Context->WindowHandle, GWL_EXSTYLE, WindowStyleEx);
|
|
|
|
if (!FOpenGL::IsAndroidGLESCompatibilityModeEnabled())
|
|
{
|
|
FScopeContext ScopeContext(Context);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, Context->ViewportFramebuffer);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, BackBufferTarget, BackBufferResource, 0);
|
|
#if UE_BUILD_DEBUG
|
|
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
|
FOpenGL::DrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
#endif
|
|
FOpenGL::CheckFrameBuffer();
|
|
|
|
glViewport(0, 0, SizeX, SizeY);
|
|
static GLfloat ZeroColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|
glClearBufferfv(GL_COLOR, 0, ZeroColor );
|
|
}
|
|
}
|
|
}
|
|
|
|
void PlatformGetSupportedResolution(uint32 &Width, uint32 &Height)
|
|
{
|
|
uint32 InitializedMode = false;
|
|
uint32 BestWidth = 0;
|
|
uint32 BestHeight = 0;
|
|
uint32 ModeIndex = 0;
|
|
DEVMODE DisplayMode;
|
|
FMemory::Memzero(&DisplayMode, sizeof(DEVMODE));
|
|
|
|
while(EnumDisplaySettings(NULL, ModeIndex++, &DisplayMode))
|
|
{
|
|
bool IsEqualOrBetterWidth = FMath::Abs((int32)DisplayMode.dmPelsWidth - (int32)Width) <= FMath::Abs((int32)BestWidth - (int32)Width);
|
|
bool IsEqualOrBetterHeight = FMath::Abs((int32)DisplayMode.dmPelsHeight - (int32)Height) <= FMath::Abs((int32)BestHeight - (int32)Height);
|
|
if(!InitializedMode || (IsEqualOrBetterWidth && IsEqualOrBetterHeight))
|
|
{
|
|
BestWidth = DisplayMode.dmPelsWidth;
|
|
BestHeight = DisplayMode.dmPelsHeight;
|
|
InitializedMode = true;
|
|
}
|
|
}
|
|
check(InitializedMode);
|
|
Width = BestWidth;
|
|
Height = BestHeight;
|
|
}
|
|
|
|
bool PlatformGetAvailableResolutions(FScreenResolutionArray& Resolutions, bool bIgnoreRefreshRate)
|
|
{
|
|
int32 MinAllowableResolutionX = 0;
|
|
int32 MinAllowableResolutionY = 0;
|
|
int32 MaxAllowableResolutionX = 10480;
|
|
int32 MaxAllowableResolutionY = 10480;
|
|
int32 MinAllowableRefreshRate = 0;
|
|
int32 MaxAllowableRefreshRate = 10480;
|
|
|
|
if (MaxAllowableResolutionX == 0) //-V547
|
|
{
|
|
MaxAllowableResolutionX = 10480;
|
|
}
|
|
if (MaxAllowableResolutionY == 0) //-V547
|
|
{
|
|
MaxAllowableResolutionY = 10480;
|
|
}
|
|
if (MaxAllowableRefreshRate == 0) //-V547
|
|
{
|
|
MaxAllowableRefreshRate = 10480;
|
|
}
|
|
|
|
uint32 ModeIndex = 0;
|
|
DEVMODE DisplayMode;
|
|
FMemory::Memzero(&DisplayMode, sizeof(DEVMODE));
|
|
|
|
while(EnumDisplaySettings(NULL, ModeIndex++, &DisplayMode))
|
|
{
|
|
if (((int32)DisplayMode.dmPelsWidth >= MinAllowableResolutionX) &&
|
|
((int32)DisplayMode.dmPelsWidth <= MaxAllowableResolutionX) &&
|
|
((int32)DisplayMode.dmPelsHeight >= MinAllowableResolutionY) &&
|
|
((int32)DisplayMode.dmPelsHeight <= MaxAllowableResolutionY)
|
|
)
|
|
{
|
|
bool bAddIt = true;
|
|
if (bIgnoreRefreshRate == false)
|
|
{
|
|
if (((int32)DisplayMode.dmDisplayFrequency < MinAllowableRefreshRate) ||
|
|
((int32)DisplayMode.dmDisplayFrequency > MaxAllowableRefreshRate)
|
|
)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// See if it is in the list already
|
|
for (int32 CheckIndex = 0; CheckIndex < Resolutions.Num(); CheckIndex++)
|
|
{
|
|
FScreenResolutionRHI& CheckResolution = Resolutions[CheckIndex];
|
|
if ((CheckResolution.Width == DisplayMode.dmPelsWidth) &&
|
|
(CheckResolution.Height == DisplayMode.dmPelsHeight))
|
|
{
|
|
// Already in the list...
|
|
bAddIt = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bAddIt)
|
|
{
|
|
// Add the mode to the list
|
|
int32 Temp2Index = Resolutions.AddZeroed();
|
|
FScreenResolutionRHI& ScreenResolution = Resolutions[Temp2Index];
|
|
|
|
ScreenResolution.Width = DisplayMode.dmPelsWidth;
|
|
ScreenResolution.Height = DisplayMode.dmPelsHeight;
|
|
ScreenResolution.RefreshRate = DisplayMode.dmDisplayFrequency;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void PlatformRestoreDesktopDisplayMode()
|
|
{
|
|
ChangeDisplaySettings(NULL, 0);
|
|
}
|
|
|
|
bool PlatformInitOpenGL()
|
|
{
|
|
static bool bInitialized = false;
|
|
static bool bOpenGLSupported = false;
|
|
if (!bInitialized)
|
|
{
|
|
// Disable warning C4191: 'type cast' : unsafe conversion from 'PROC' to 'XXX' while getting GL entry points.
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4191)
|
|
|
|
// Create a dummy context so that wglCreateContextAttribsARB can be initialized.
|
|
FPlatformOpenGLContext DummyContext;
|
|
PlatformCreateDummyGLWindow(&DummyContext);
|
|
DummyContext.OpenGLContext = wglCreateContext(DummyContext.DeviceContext);
|
|
check(DummyContext.OpenGLContext);
|
|
ContextMakeCurrent(DummyContext.DeviceContext, DummyContext.OpenGLContext);
|
|
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
|
|
if (wglCreateContextAttribsARB)
|
|
{
|
|
int MajorVersion = 0;
|
|
int MinorVersion = 0;
|
|
|
|
ContextMakeCurrent(NULL,NULL);
|
|
wglDeleteContext(DummyContext.OpenGLContext);
|
|
GetOpenGLVersionForCoreProfile(MajorVersion, MinorVersion);
|
|
PlatformCreateOpenGLContextCore(&DummyContext, MajorVersion, MinorVersion, NULL);
|
|
if (DummyContext.OpenGLContext)
|
|
{
|
|
bOpenGLSupported = true;
|
|
ContextMakeCurrent(DummyContext.DeviceContext, DummyContext.OpenGLContext);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogRHI,Error,TEXT("OpenGL %d.%d not supported by driver"),MajorVersion,MinorVersion);
|
|
}
|
|
}
|
|
|
|
if (bOpenGLSupported)
|
|
{
|
|
// Retrieve the OpenGL DLL.
|
|
void* OpenGLDLL = FPlatformProcess::GetDllHandle(TEXT("opengl32.dll"));
|
|
if (!OpenGLDLL)
|
|
{
|
|
UE_LOG(LogRHI,Fatal,TEXT("Couldn't load opengl32.dll"));
|
|
}
|
|
|
|
// Initialize entry points required by Unreal from opengl32.dll
|
|
#define GET_GL_ENTRYPOINTS_DLL(Type,Func) Func = (Type)FPlatformProcess::GetDllExport(OpenGLDLL,TEXT(#Func));
|
|
ENUM_GL_ENTRYPOINTS_DLL(GET_GL_ENTRYPOINTS_DLL);
|
|
#undef GET_GL_ENTRYPOINTS_DLL
|
|
|
|
// Release the OpenGL DLL.
|
|
FPlatformProcess::FreeDllHandle(OpenGLDLL);
|
|
|
|
// Initialize all entry points required by Unreal.
|
|
#define GET_GL_ENTRYPOINTS(Type,Func) Func = (Type)wglGetProcAddress(#Func);
|
|
ENUM_GL_ENTRYPOINTS(GET_GL_ENTRYPOINTS);
|
|
ENUM_GL_ENTRYPOINTS_OPTIONAL(GET_GL_ENTRYPOINTS);
|
|
#undef GET_GL_ENTRYPOINTS
|
|
|
|
// Restore warning C4191.
|
|
#pragma warning(pop)
|
|
|
|
// Check that all of the entry points have been initialized.
|
|
bool bFoundAllEntryPoints = true;
|
|
#define CHECK_GL_ENTRYPOINTS(Type,Func) if (Func == NULL) { bFoundAllEntryPoints = false; UE_LOG(LogRHI, Warning, TEXT("Failed to find entry point for %s"), TEXT(#Func)); }
|
|
ENUM_GL_ENTRYPOINTS_DLL(CHECK_GL_ENTRYPOINTS);
|
|
ENUM_GL_ENTRYPOINTS(CHECK_GL_ENTRYPOINTS);
|
|
#undef CHECK_GL_ENTRYPOINTS
|
|
checkf(bFoundAllEntryPoints, TEXT("Failed to find all OpenGL entry points."));
|
|
}
|
|
|
|
// The dummy context can now be released.
|
|
if (DummyContext.OpenGLContext)
|
|
{
|
|
ContextMakeCurrent(NULL,NULL);
|
|
wglDeleteContext(DummyContext.OpenGLContext);
|
|
}
|
|
ReleaseDC(DummyContext.WindowHandle, DummyContext.DeviceContext);
|
|
check(DummyContext.bReleaseWindowOnDestroy);
|
|
DestroyWindow(DummyContext.WindowHandle);
|
|
|
|
}
|
|
return bOpenGLSupported;
|
|
}
|
|
|
|
bool PlatformOpenGLContextValid()
|
|
{
|
|
return( GetCurrentContext() != NULL );
|
|
}
|
|
|
|
int32 PlatformGlGetError()
|
|
{
|
|
return glGetError();
|
|
}
|
|
|
|
EOpenGLCurrentContext PlatformOpenGLCurrentContext(FPlatformOpenGLDevice* Device)
|
|
{
|
|
HGLRC Context = GetCurrentContext();
|
|
|
|
if (Context == Device->RenderingContext.OpenGLContext) // most common case
|
|
{
|
|
return CONTEXT_Rendering;
|
|
}
|
|
else if (Context == Device->SharedContext.OpenGLContext)
|
|
{
|
|
return CONTEXT_Shared;
|
|
}
|
|
else if (Context)
|
|
{
|
|
return CONTEXT_Other;
|
|
}
|
|
else
|
|
{
|
|
return CONTEXT_Invalid;
|
|
}
|
|
}
|
|
|
|
void* PlatformOpenGLCurrentContextHandle(FPlatformOpenGLDevice* Device)
|
|
{
|
|
return GetCurrentContext();
|
|
}
|
|
|
|
void PlatformGetBackbufferDimensions( uint32& OutWidth, uint32& OutHeight )
|
|
{
|
|
OutWidth = OutHeight = 0;
|
|
HDC DeviceContext = wglGetCurrentDC();
|
|
if( DeviceContext )
|
|
{
|
|
OutWidth = GetDeviceCaps( DeviceContext, HORZRES );
|
|
OutHeight = GetDeviceCaps( DeviceContext, VERTRES );
|
|
}
|
|
}
|
|
|
|
// =============================================================
|
|
|
|
struct FOpenGLReleasedQuery
|
|
{
|
|
HGLRC Context;
|
|
GLuint Query;
|
|
};
|
|
|
|
static TArray<FOpenGLReleasedQuery> ReleasedQueries;
|
|
static FCriticalSection* ReleasedQueriesGuard;
|
|
|
|
void PlatformGetNewRenderQuery( GLuint* OutQuery, uint64* OutQueryContext )
|
|
{
|
|
if( !ReleasedQueriesGuard )
|
|
{
|
|
ReleasedQueriesGuard = new FCriticalSection;
|
|
}
|
|
|
|
{
|
|
FScopeLock Lock(ReleasedQueriesGuard);
|
|
|
|
#ifdef UE_BUILD_DEBUG
|
|
check( OutQuery && OutQueryContext );
|
|
#endif
|
|
|
|
HGLRC Context = GetCurrentContext();
|
|
check( Context );
|
|
|
|
GLuint NewQuery = 0;
|
|
|
|
// Check for possible query reuse
|
|
const int32 ArraySize = ReleasedQueries.Num();
|
|
for( int32 Index = 0; Index < ArraySize; ++Index )
|
|
{
|
|
if( ReleasedQueries[Index].Context == Context )
|
|
{
|
|
NewQuery = ReleasedQueries[Index].Query;
|
|
ReleasedQueries.RemoveAtSwap(Index);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !NewQuery )
|
|
{
|
|
FOpenGL::GenQueries( 1, &NewQuery );
|
|
}
|
|
|
|
*OutQuery = NewQuery;
|
|
*OutQueryContext = (uint64)Context;
|
|
}
|
|
}
|
|
|
|
void PlatformReleaseRenderQuery( GLuint Query, uint64 QueryContext )
|
|
{
|
|
HGLRC Context = GetCurrentContext();
|
|
if( (uint64)Context == QueryContext )
|
|
{
|
|
FOpenGL::DeleteQueries(1, &Query );
|
|
}
|
|
else
|
|
{
|
|
FScopeLock Lock(ReleasedQueriesGuard);
|
|
#ifdef UE_BUILD_DEBUG
|
|
check( Query && QueryContext && ReleasedQueriesGuard );
|
|
#endif
|
|
FOpenGLReleasedQuery ReleasedQuery;
|
|
ReleasedQuery.Context = (HGLRC)QueryContext;
|
|
ReleasedQuery.Query = Query;
|
|
ReleasedQueries.Add(ReleasedQuery);
|
|
}
|
|
}
|
|
|
|
void DeleteQueriesForCurrentContext( HGLRC Context )
|
|
{
|
|
if( !ReleasedQueriesGuard )
|
|
{
|
|
ReleasedQueriesGuard = new FCriticalSection;
|
|
}
|
|
|
|
{
|
|
FScopeLock Lock(ReleasedQueriesGuard);
|
|
for( int32 Index = 0; Index < ReleasedQueries.Num(); ++Index )
|
|
{
|
|
if( ReleasedQueries[Index].Context == Context )
|
|
{
|
|
FOpenGL::DeleteQueries(1,&ReleasedQueries[Index].Query);
|
|
ReleasedQueries.RemoveAtSwap(Index);
|
|
--Index;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool PlatformContextIsCurrent( uint64 QueryContext )
|
|
{
|
|
return (uint64)GetCurrentContext() == QueryContext;
|
|
}
|
|
|
|
FOpenGLTexture* PlatformCreateBuiltinBackBuffer(FOpenGLDynamicRHI* OpenGLRHI, uint32 SizeX, uint32 SizeY)
|
|
{
|
|
if (FOpenGL::IsAndroidGLESCompatibilityModeEnabled())
|
|
{
|
|
const FRHITextureCreateDesc Desc =
|
|
FRHITextureCreateDesc::Create2D(TEXT("PlatformCreateBuiltinBackBuffer"), SizeX, SizeY, PF_B8G8R8A8)
|
|
.SetClearValue(FClearValueBinding::Transparent)
|
|
.SetFlags(ETextureCreateFlags::RenderTargetable | ETextureCreateFlags::Presentable | ETextureCreateFlags::ResolveTargetable)
|
|
.DetermineInititialState();
|
|
|
|
return new FOpenGLTexture(Desc);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void* PlatformGetWindow(FPlatformOpenGLContext* Context, void** AddParam)
|
|
{
|
|
check(Context && Context->WindowHandle);
|
|
|
|
return (void*)&Context->WindowHandle;
|
|
}
|
|
|