You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb none #codereview Ryan.Vance #lockdown Nick.Penwarden [CL 3385532 by Arciel Rekman in Main branch]
1141 lines
30 KiB
C++
1141 lines
30 KiB
C++
// Copyright 1998-2017 Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
OpenGLWindowsLoader.cpp: Manual loading of OpenGL functions from DLL.
|
|
=============================================================================*/
|
|
|
|
#include "Linux/OpenGLLinux.h"
|
|
#include "Misc/ScopeLock.h"
|
|
#include "OpenGLDrv.h"
|
|
#include "SDL.h"
|
|
#include "OpenGLDrvPrivate.h"
|
|
#include "ComponentReregisterContext.h"
|
|
|
|
/*------------------------------------------------------------------------------
|
|
OpenGL function pointers.
|
|
------------------------------------------------------------------------------*/
|
|
#define DEFINE_GL_ENTRYPOINTS(Type,Func) Type Func = NULL;
|
|
namespace GLFuncPointers // see explanation in OpenGLLinux.h why we need the namespace
|
|
{
|
|
ENUM_GL_ENTRYPOINTS_ALL(DEFINE_GL_ENTRYPOINTS);
|
|
};
|
|
|
|
typedef SDL_Window* SDL_HWindow;
|
|
typedef SDL_GLContext SDL_HGLContext;
|
|
|
|
/*------------------------------------------------------------------------------
|
|
OpenGL context management.
|
|
------------------------------------------------------------------------------*/
|
|
static void _ContextMakeCurrent( SDL_HWindow hWnd, SDL_HGLContext hGLDC )
|
|
{
|
|
GLint Result = SDL_GL_MakeCurrent( hWnd, hGLDC );
|
|
if (Result != 0)
|
|
{
|
|
// this is a warning and not error, since Slate sometimes destroys windows before
|
|
// releasing RHI resources associated with them. This code can result in leaks - proper resolution is tracked as UE-7388
|
|
FString SdlError(ANSI_TO_TCHAR(SDL_GetError()));
|
|
UE_LOG(LogLinux, Warning, TEXT("SDL_GL_MakeCurrent() failed, SDL error: '%s'"), *SdlError );
|
|
}
|
|
}
|
|
|
|
static SDL_HGLContext _GetCurrentContext()
|
|
{
|
|
return SDL_GL_GetCurrentContext();
|
|
}
|
|
|
|
/** Platform specific OpenGL context. */
|
|
struct FPlatformOpenGLContext
|
|
{
|
|
SDL_HWindow hWnd;
|
|
SDL_HGLContext hGLContext; // this is a (void*) pointer
|
|
|
|
bool bReleaseWindowOnDestroy;
|
|
int32 SyncInterval;
|
|
GLuint ViewportFramebuffer;
|
|
GLuint VertexArrayObject; // one has to be generated and set for each context (OpenGL 3.2 Core requirements)
|
|
};
|
|
|
|
|
|
class FScopeContext
|
|
{
|
|
public:
|
|
FScopeContext( FPlatformOpenGLContext* Context )
|
|
{
|
|
check(Context);
|
|
hPreWnd = SDL_GL_GetCurrentWindow();
|
|
hPreGLContext = SDL_GL_GetCurrentContext();
|
|
|
|
bSameDCAndContext = ( hPreGLContext == Context->hGLContext );
|
|
|
|
if ( !bSameDCAndContext )
|
|
{
|
|
if (hPreGLContext)
|
|
{
|
|
glFlush();
|
|
}
|
|
// no need to glFlush() on Windows, it does flush by itself before switching contexts
|
|
|
|
_ContextMakeCurrent( Context->hWnd, Context->hGLContext );
|
|
}
|
|
}
|
|
|
|
~FScopeContext( void )
|
|
{
|
|
if (!bSameDCAndContext)
|
|
{
|
|
glFlush(); // not needed on Windows, it does flush by itself before switching contexts
|
|
if ( hPreGLContext )
|
|
{
|
|
_ContextMakeCurrent( hPreWnd, hPreGLContext );
|
|
}
|
|
else
|
|
{
|
|
_ContextMakeCurrent( NULL, NULL );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private:
|
|
SDL_HWindow hPreWnd;
|
|
SDL_HGLContext hPreGLContext; // this is a pointer, (void*)
|
|
bool bSameDCAndContext;
|
|
};
|
|
|
|
|
|
|
|
void _DeleteQueriesForCurrentContext( SDL_HGLContext hGLContext );
|
|
|
|
/**
|
|
* Create a dummy window used to construct OpenGL contexts.
|
|
*/
|
|
static void _PlatformCreateDummyGLWindow( FPlatformOpenGLContext *OutContext )
|
|
{
|
|
static bool bInitializedWindowClass = false;
|
|
|
|
// Create a dummy window.
|
|
SDL_HWindow DummyWindow = SDL_CreateWindow( NULL,
|
|
0, 0, 1, 1,
|
|
SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_HIDDEN | SDL_WINDOW_SKIP_TASKBAR );
|
|
if (DummyWindow == nullptr)
|
|
{
|
|
UE_LOG(LogLinux, Fatal, TEXT("Cannot create dummy GL window for shared context."));
|
|
// unreachable
|
|
}
|
|
else
|
|
{
|
|
SDL_SetWindowTitle(DummyWindow, "UE4 Dummy GL window");
|
|
}
|
|
|
|
OutContext->hWnd = DummyWindow;
|
|
OutContext->bReleaseWindowOnDestroy = true;
|
|
}
|
|
|
|
/**
|
|
* Determine OpenGL Context version based on command line arguments
|
|
*/
|
|
|
|
static bool IsOpenGL3Forced()
|
|
{
|
|
return FParse::Param(FCommandLine::Get(),TEXT("opengl3"));
|
|
}
|
|
|
|
static bool IsOpenGL4Forced()
|
|
{
|
|
return FParse::Param(FCommandLine::Get(),TEXT("opengl4"));
|
|
}
|
|
|
|
static void PlatformOpenGLVersionFromCommandLine(int& OutMajorVersion, int& OutMinorVersion)
|
|
{
|
|
bool bGL3 = IsOpenGL3Forced();
|
|
bool bGL4 = IsOpenGL4Forced();
|
|
if (!bGL3 && !bGL4)
|
|
{
|
|
// if neither is forced, go with the first RHI in the list.
|
|
if (GRequestedFeatureLevel == ERHIFeatureLevel::SM4)
|
|
{
|
|
bGL3 = true;
|
|
}
|
|
else
|
|
{
|
|
bGL4 = true;
|
|
}
|
|
}
|
|
|
|
// between GL3 and GL4, prefer GL3 since it might have been forced as a safety measure.
|
|
if (bGL4)
|
|
{
|
|
OutMajorVersion = 4;
|
|
OutMinorVersion = 3;
|
|
}
|
|
else
|
|
{
|
|
OutMajorVersion = 3;
|
|
OutMinorVersion = 2;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
check( OutContext );
|
|
|
|
SDL_HWindow prevWindow = SDL_GL_GetCurrentWindow();
|
|
SDL_HGLContext prevContext = SDL_GL_GetCurrentContext();
|
|
|
|
OutContext->SyncInterval = -1; // invalid value to enforce setup on first buffer swap
|
|
OutContext->ViewportFramebuffer = 0;
|
|
|
|
OutContext->hGLContext = SDL_GL_CreateContext( OutContext->hWnd );
|
|
if (OutContext->hGLContext == nullptr)
|
|
{
|
|
FString SdlError(ANSI_TO_TCHAR(SDL_GetError()));
|
|
|
|
// ignore errors getting version, it will be clear from the logs
|
|
int OpenGLMajorVersion = -1;
|
|
int OpenGLMinorVersion = -1;
|
|
SDL_GL_GetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, &OpenGLMajorVersion );
|
|
SDL_GL_GetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, &OpenGLMinorVersion );
|
|
|
|
UE_LOG(LogInit, Error, TEXT("_PlatformCreateOpenGLContextCore - Could not create OpenGL %d.%d context, SDL error: '%s'"),
|
|
OpenGLMajorVersion, OpenGLMinorVersion,
|
|
*SdlError
|
|
);
|
|
// unreachable
|
|
return;
|
|
}
|
|
|
|
SDL_GL_MakeCurrent( prevWindow, prevContext );
|
|
}
|
|
|
|
void PlatformReleaseOpenGLContext(FPlatformOpenGLDevice* Device, FPlatformOpenGLContext* Context);
|
|
|
|
extern void OnQueryInvalidation( void );
|
|
|
|
/** Platform specific OpenGL device. */
|
|
struct FPlatformOpenGLDevice
|
|
{
|
|
FPlatformOpenGLContext SharedContext;
|
|
FPlatformOpenGLContext RenderingContext;
|
|
int32 NumUsedContexts;
|
|
|
|
/** Guards against operating on viewport contexts from more than one thread at the same time. */
|
|
FCriticalSection* ContextUsageGuard;
|
|
|
|
FPlatformOpenGLDevice() : NumUsedContexts( 0 )
|
|
{
|
|
extern void InitDebugContext();
|
|
|
|
ContextUsageGuard = new FCriticalSection;
|
|
|
|
verifyf(SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0) == 0, TEXT("SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 0) failed: %s\n."), UTF8_TO_TCHAR(SDL_GetError()));
|
|
|
|
_PlatformCreateDummyGLWindow( &SharedContext );
|
|
_PlatformCreateOpenGLContextCore( &SharedContext );
|
|
|
|
check ( SharedContext.hGLContext )
|
|
{
|
|
FScopeContext ScopeContext( &SharedContext );
|
|
InitDebugContext();
|
|
glGenVertexArrays(1,&SharedContext.VertexArrayObject);
|
|
glBindVertexArray(SharedContext.VertexArrayObject);
|
|
InitDefaultGLContextState();
|
|
}
|
|
|
|
verifyf(SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1) == 0, TEXT("SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1) failed: %s\n."), UTF8_TO_TCHAR(SDL_GetError()));
|
|
|
|
_ContextMakeCurrent( SharedContext.hWnd, SharedContext.hGLContext );
|
|
|
|
_PlatformCreateDummyGLWindow( &RenderingContext );
|
|
_PlatformCreateOpenGLContextCore( &RenderingContext );
|
|
|
|
check ( RenderingContext.hGLContext )
|
|
{
|
|
FScopeContext ScopeContext( &RenderingContext );
|
|
InitDebugContext();
|
|
glGenVertexArrays(1,&RenderingContext.VertexArrayObject);
|
|
glBindVertexArray(RenderingContext.VertexArrayObject);
|
|
InitDefaultGLContextState();
|
|
}
|
|
}
|
|
|
|
~FPlatformOpenGLDevice()
|
|
{
|
|
check( NumUsedContexts==0 );
|
|
|
|
_ContextMakeCurrent( NULL, NULL );
|
|
|
|
OnQueryInvalidation();
|
|
PlatformReleaseOpenGLContext( this,&RenderingContext );
|
|
PlatformReleaseOpenGLContext( this,&SharedContext );
|
|
|
|
delete ContextUsageGuard;
|
|
}
|
|
};
|
|
|
|
FPlatformOpenGLDevice* PlatformCreateOpenGLDevice()
|
|
{
|
|
return new FPlatformOpenGLDevice;
|
|
}
|
|
|
|
bool PlatformCanEnableGPUCapture()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void PlatformDestroyOpenGLDevice( FPlatformOpenGLDevice* Device )
|
|
{
|
|
delete Device;
|
|
}
|
|
|
|
/**
|
|
* Create an OpenGL context.
|
|
*/
|
|
FPlatformOpenGLContext* PlatformCreateOpenGLContext(FPlatformOpenGLDevice* Device, void* InWindowHandle)
|
|
{
|
|
check(InWindowHandle);
|
|
FPlatformOpenGLContext* Context = new FPlatformOpenGLContext;
|
|
|
|
Context->hWnd = (SDL_HWindow)InWindowHandle;
|
|
Context->bReleaseWindowOnDestroy = false;
|
|
|
|
check( Device->SharedContext.hGLContext )
|
|
{
|
|
FScopeContext Scope(&(Device->SharedContext));
|
|
verifyf(SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1) == 0, TEXT("SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1) failed: %s\n."), UTF8_TO_TCHAR(SDL_GetError()));
|
|
_PlatformCreateOpenGLContextCore( Context );
|
|
}
|
|
|
|
check( Context->hGLContext );
|
|
{
|
|
FScopeContext Scope(Context);
|
|
InitDefaultGLContextState();
|
|
}
|
|
|
|
return Context;
|
|
}
|
|
|
|
/**
|
|
* Release an OpenGL context.
|
|
*/
|
|
void PlatformReleaseOpenGLContext( FPlatformOpenGLDevice* Device, FPlatformOpenGLContext* Context )
|
|
{
|
|
check( Context && Context->hGLContext );
|
|
|
|
{
|
|
FScopeLock ScopeLock( Device->ContextUsageGuard );
|
|
{
|
|
FScopeContext ScopeContext( Context );
|
|
|
|
_DeleteQueriesForCurrentContext( Context->hGLContext );
|
|
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;
|
|
}
|
|
}
|
|
|
|
SDL_GL_DeleteContext( Context->hGLContext );
|
|
Context->hGLContext = NULL;
|
|
}
|
|
|
|
check( Context->hWnd );
|
|
|
|
if ( Context->bReleaseWindowOnDestroy )
|
|
{
|
|
SDL_DestroyWindow( Context->hWnd );
|
|
}
|
|
|
|
Context->hWnd = NULL;
|
|
}
|
|
|
|
/**
|
|
* Destroy an OpenGL context.
|
|
*/
|
|
void PlatformDestroyOpenGLContext(FPlatformOpenGLDevice* Device, FPlatformOpenGLContext* Context)
|
|
{
|
|
PlatformReleaseOpenGLContext( Device, Context );
|
|
delete Context;
|
|
}
|
|
|
|
void* PlatformGetWindow(FPlatformOpenGLContext* Context, void** AddParam)
|
|
{
|
|
check(Context && Context->hWnd);
|
|
|
|
if (AddParam)
|
|
{
|
|
*AddParam = &Context->hGLContext;
|
|
}
|
|
|
|
return (void*)&Context->hWnd;
|
|
}
|
|
|
|
const TCHAR* PlatformDescribeSyncInterval(int32 SyncInterval)
|
|
{
|
|
switch (SyncInterval)
|
|
{
|
|
case -1:
|
|
return TEXT("Late swap");
|
|
case 0:
|
|
return TEXT("Immediate");
|
|
case 1:
|
|
return TEXT("Synchronized with retrace");
|
|
default: break;
|
|
}
|
|
return TEXT("Unknown");
|
|
}
|
|
|
|
/**
|
|
* 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 )
|
|
{
|
|
FPlatformOpenGLContext* const Context = Viewport.GetGLContext();
|
|
|
|
check( Context && Context->hWnd );
|
|
|
|
FScopeLock ScopeLock( Device->ContextUsageGuard );
|
|
{
|
|
FScopeContext ScopeContext( Context );
|
|
|
|
if (Viewport.GetCustomPresent())
|
|
{
|
|
glDisable(GL_FRAMEBUFFER_SRGB);
|
|
bool bShouldPresent = Viewport.GetCustomPresent()->Present(SyncInterval);
|
|
glEnable(GL_FRAMEBUFFER_SRGB);
|
|
if (!bShouldPresent)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
glBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 );
|
|
glDrawBuffer( GL_BACK );
|
|
glBindFramebuffer( GL_READ_FRAMEBUFFER, Context->ViewportFramebuffer );
|
|
glReadBuffer( GL_COLOR_ATTACHMENT0 );
|
|
glDisable(GL_FRAMEBUFFER_SRGB);
|
|
|
|
int WinW = 0;
|
|
int WinH = 0;
|
|
SDL_GL_GetDrawableSize(Context->hWnd, &WinW, &WinH);
|
|
GLenum BlitFilter;
|
|
GLint DestX0, DestY0, DestX1, DestY1;
|
|
|
|
if ( WinH == 0 || WinW == 0 )
|
|
{
|
|
// Nothing to blit
|
|
return false;
|
|
}
|
|
|
|
if ( ( WinW == BackbufferSizeX ) && ( WinH == BackbufferSizeY ) )
|
|
{
|
|
// We match up. We're probably in windowed mode, or an exact
|
|
// match for FULLSCREEN_DESKTOP mode. Use a NEAREST blit and
|
|
// don't clear the window system's framebuffer first.
|
|
BlitFilter = GL_NEAREST;
|
|
DestX0 = 0;
|
|
DestY0 = BackbufferSizeY; // flip vertically.
|
|
DestX1 = BackbufferSizeX;
|
|
DestY1 = 0;
|
|
}
|
|
else
|
|
{
|
|
// we need to scale to match the size of the window system's
|
|
// framebuffer, so scale linearly, and adjust for letterboxing.
|
|
BlitFilter = GL_LINEAR;
|
|
|
|
const uint32 w = BackbufferSizeX;
|
|
const uint32 h = BackbufferSizeY;
|
|
const float WantedAspect = (w > h) ? (((float) w) / ((float) h)) : (((float) h) / ((float) w));
|
|
const float PhysicalAspect = (((float) WinW) / ((float) WinH));
|
|
|
|
bool bMustClear; // have to clear the window framebuffer if letterboxing.
|
|
if ( PhysicalAspect == WantedAspect ) // Perfect aspect ratio; no letterboxing needed?
|
|
{
|
|
bMustClear = false;
|
|
DestX0 = 0;
|
|
DestY0 = WinH; // flip vertically.
|
|
DestX1 = WinW;
|
|
DestY1 = 0;
|
|
}
|
|
else if ( PhysicalAspect > WantedAspect ) // view is wider than wanted aspect?
|
|
{
|
|
bMustClear = true;
|
|
const float ScaledW = WinH * WantedAspect;
|
|
const float ScaledX = (((float)WinW) - ScaledW) / 2.0f;
|
|
DestX0 = ScaledX;
|
|
DestY0 = WinH; // flip vertically.
|
|
DestX1 = ScaledX + ScaledW;
|
|
DestY1 = 0;
|
|
}
|
|
else // view is taller than wanted aspect?
|
|
{
|
|
bMustClear = true;
|
|
const float ScaledH = WinW / WantedAspect;
|
|
const float ScaledY = (((float)WinH) - ScaledH) / 2.0f;
|
|
DestX0 = 0;
|
|
DestY0 = ScaledY + ScaledH; // flip vertically.
|
|
DestX1 = WinW;
|
|
DestY1 = ScaledY;
|
|
}
|
|
|
|
// if the Steam Overlay is running, it might write garbage into our
|
|
// letterbox area, so if we have a letterbox, clear the framebuffer.
|
|
if ( bMustClear )
|
|
{
|
|
glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
|
|
glClear( GL_COLOR_BUFFER_BIT );
|
|
}
|
|
}
|
|
|
|
// Get it to the window system's framebuffer.
|
|
glBlitFramebuffer( 0, 0, BackbufferSizeX, BackbufferSizeY,
|
|
DestX0, DestY0, DestX1, DestY1,
|
|
GL_COLOR_BUFFER_BIT,
|
|
BlitFilter );
|
|
|
|
if ( bPresent )
|
|
{
|
|
int32 RealSyncInterval = bLockToVsync ? SyncInterval : 0;
|
|
|
|
if (Context->SyncInterval != RealSyncInterval)
|
|
{
|
|
// 0 for immediate updates
|
|
// 1 for updates synchronized with the vertical retrace
|
|
// -1 for late swap tearing;
|
|
|
|
UE_LOG(LogLinux, Log, TEXT("Setting swap interval to '%s'"), PlatformDescribeSyncInterval(RealSyncInterval));
|
|
int SetSwapResult = SDL_GL_SetSwapInterval(RealSyncInterval);
|
|
// if late tearing is not supported, this needs to be retried with a valid value.
|
|
if (SetSwapResult == -1)
|
|
{
|
|
if (RealSyncInterval == -1)
|
|
{
|
|
// fallback to synchronized
|
|
int FallbackInterval = 1;
|
|
UE_LOG(LogLinux, Log, TEXT("Unable to set desired swap interval, falling back to '%s'"), PlatformDescribeSyncInterval(FallbackInterval));
|
|
SetSwapResult = SDL_GL_SetSwapInterval(FallbackInterval);
|
|
}
|
|
}
|
|
|
|
if (SetSwapResult == -1)
|
|
{
|
|
UE_LOG(LogLinux, Warning, TEXT("Unable to set desired swap interval '%s'"), PlatformDescribeSyncInterval(RealSyncInterval));
|
|
}
|
|
|
|
// even if we failed to set it, update the context value to prevent further attempts
|
|
Context->SyncInterval = RealSyncInterval;
|
|
}
|
|
|
|
SDL_GL_SwapWindow( Context->hWnd );
|
|
|
|
glEnable(GL_FRAMEBUFFER_SRGB);
|
|
REPORT_GL_END_BUFFER_EVENT_FOR_FRAME_DUMP();
|
|
// INITIATE_GL_FRAME_DUMP_EVERY_X_CALLS( 1000 );
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void PlatformFlushIfNeeded()
|
|
{
|
|
glFinish();
|
|
}
|
|
|
|
void PlatformRebindResources(FPlatformOpenGLDevice* Device)
|
|
{
|
|
// @todo: Figure out if we need to rebind frame & renderbuffers after switching contexts
|
|
}
|
|
|
|
void PlatformRenderingContextSetup(FPlatformOpenGLDevice* Device)
|
|
{
|
|
check( Device && Device->RenderingContext.hWnd && Device->RenderingContext.hGLContext );
|
|
|
|
if ( _GetCurrentContext() )
|
|
{
|
|
glFlush();
|
|
}
|
|
|
|
_ContextMakeCurrent( Device->RenderingContext.hWnd, Device->RenderingContext.hGLContext );
|
|
}
|
|
|
|
void PlatformSharedContextSetup(FPlatformOpenGLDevice* Device)
|
|
{
|
|
check( Device && Device->SharedContext.hWnd && Device->SharedContext.hGLContext );
|
|
|
|
// no need to glFlush() on Windows, it does flush by itself before switching contexts
|
|
if ( _GetCurrentContext() )
|
|
{
|
|
glFlush();
|
|
}
|
|
|
|
_ContextMakeCurrent( Device->SharedContext.hWnd, Device->SharedContext.hGLContext );
|
|
}
|
|
|
|
|
|
void PlatformNULLContextSetup()
|
|
{
|
|
if ( _GetCurrentContext() )
|
|
{
|
|
glFlush();
|
|
}
|
|
|
|
_ContextMakeCurrent( NULL, NULL );
|
|
}
|
|
|
|
|
|
#if 0
|
|
static void __GetBestFullscreenResolution( SDL_HWindow hWnd, int32 *pWidth, int32 *pHeight )
|
|
{
|
|
uint32 InitializedMode = false;
|
|
uint32 BestWidth = 0;
|
|
uint32 BestHeight = 0;
|
|
uint32 ModeIndex = 0;
|
|
|
|
int32 dsp_idx = SDL_GetWindowDisplayIndex( hWnd );
|
|
if ( dsp_idx < 0 )
|
|
{ dsp_idx = 0; }
|
|
|
|
SDL_DisplayMode dsp_mode;
|
|
FMemory::Memzero( &dsp_mode, sizeof(SDL_DisplayMode) );
|
|
|
|
while ( !SDL_GetDisplayMode( dsp_idx, ModeIndex++, &dsp_mode ) )
|
|
{
|
|
bool IsEqualOrBetterWidth = FMath::Abs((int32)dsp_mode.w - (int32)(*pWidth)) <= FMath::Abs((int32)BestWidth - (int32)(*pWidth ));
|
|
bool IsEqualOrBetterHeight = FMath::Abs((int32)dsp_mode.h - (int32)(*pHeight)) <= FMath::Abs((int32)BestHeight - (int32)(*pHeight));
|
|
if (!InitializedMode || (IsEqualOrBetterWidth && IsEqualOrBetterHeight))
|
|
{
|
|
BestWidth = dsp_mode.w;
|
|
BestHeight = dsp_mode.h;
|
|
InitializedMode = true;
|
|
}
|
|
}
|
|
|
|
check(InitializedMode);
|
|
|
|
*pWidth = BestWidth;
|
|
*pHeight = BestHeight;
|
|
}
|
|
|
|
|
|
static void __SetBestFullscreenDisplayMode( SDL_HWindow hWnd, int32 *pWidth, int32 *pHeight )
|
|
{
|
|
SDL_DisplayMode dsp_mode;
|
|
|
|
dsp_mode.w = *pWidth;
|
|
dsp_mode.h = *pHeight;
|
|
dsp_mode.format = SDL_PIXELFORMAT_ARGB8888;
|
|
dsp_mode.refresh_rate = 60;
|
|
dsp_mode.driverdata = NULL;
|
|
|
|
__GetBestFullscreenResolution( hWnd, &dsp_mode.w, &dsp_mode.h );
|
|
SDL_SetWindowDisplayMode( hWnd, &dsp_mode );
|
|
|
|
*pWidth = dsp_mode.w;
|
|
*pHeight = dsp_mode.h;
|
|
}
|
|
#endif
|
|
|
|
|
|
/**
|
|
* Resize the GL context.
|
|
*/
|
|
static int gResizeC = 0;
|
|
|
|
void PlatformResizeGLContext( FPlatformOpenGLDevice* Device,
|
|
FPlatformOpenGLContext* Context,
|
|
uint32 SizeX, uint32 SizeY,
|
|
bool bFullscreen,
|
|
bool bWasFullscreen,
|
|
GLenum BackBufferTarget,
|
|
GLuint BackBufferResource)
|
|
{
|
|
// printf( "--------------------------------------------------------\n" );
|
|
// printf("Ariel - PlatformResizeGLContext - Start - %03d\n", gResizeC );
|
|
|
|
{
|
|
FScopeLock ScopeLock(Device->ContextUsageGuard);
|
|
|
|
#if 0
|
|
SDL_HWindow h_wnd = Context->hWnd;
|
|
|
|
int32 closest_w = SizeX;
|
|
int32 closest_h = SizeY;
|
|
|
|
if ( bFullscreen )
|
|
{
|
|
|
|
SDL_SetWindowPosition( h_wnd, 0, 0 );
|
|
__SetBestFullscreenDisplayMode( h_wnd, &closest_w, &closest_h );
|
|
|
|
SDL_SetWindowBordered( h_wnd, SDL_FALSE );
|
|
SDL_SetWindowFullscreen( h_wnd, SDL_WINDOW_FULLSCREEN );
|
|
|
|
}
|
|
else
|
|
if ( bWasFullscreen )
|
|
{
|
|
SDL_SetWindowFullscreen( h_wnd, 0 );
|
|
SDL_SetWindowBordered( h_wnd, SDL_TRUE );
|
|
SDL_SetWindowSize( h_wnd, SizeX, SizeY );
|
|
}
|
|
#endif
|
|
|
|
{
|
|
FScopeContext ScopeContext(Context);
|
|
|
|
if (Context->ViewportFramebuffer == 0)
|
|
{
|
|
glGenFramebuffers(1, &Context->ViewportFramebuffer);
|
|
}
|
|
glBindFramebuffer(GL_FRAMEBUFFER, Context->ViewportFramebuffer);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, BackBufferTarget, BackBufferResource, 0);
|
|
#if UE_BUILD_DEBUG
|
|
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
|
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
|
#endif
|
|
FOpenGL::CheckFrameBuffer();
|
|
|
|
glViewport( 0, 0, SizeX, SizeY );
|
|
// printf( " - NewVPW = %d, NewVPH = %d\n", SizeX, SizeY );
|
|
|
|
static GLfloat ZeroColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
|
glClearBufferfv(GL_COLOR, 0, ZeroColor );
|
|
}
|
|
|
|
|
|
// bool b_need_reattach = bFullscreen || bWasFullscreen;
|
|
if ( bFullscreen )
|
|
{
|
|
// Deregister all components
|
|
// this line will fix the geometry missing and color distortion bug on Linux/Nvidia machine
|
|
// it detach all the components and re attach all the components
|
|
FGlobalComponentReregisterContext RecreateComponents;
|
|
}
|
|
else
|
|
if ( bWasFullscreen )
|
|
{
|
|
FGlobalComponentReregisterContext RecreateComponents;
|
|
}
|
|
}
|
|
|
|
// printf("Ariel - PlatformResizeGLContext - End - %03d\n", gResizeC );
|
|
gResizeC++;
|
|
}
|
|
|
|
// int dip_num = SDL_GetNumVideoDisplays();
|
|
void PlatformGetSupportedResolution(uint32 &Width, uint32 &Height)
|
|
{
|
|
uint32 InitializedMode = false;
|
|
uint32 BestWidth = 0;
|
|
uint32 BestHeight = 0;
|
|
uint32 ModeIndex = 0;
|
|
|
|
SDL_DisplayMode dsp_mode;
|
|
FMemory::Memzero( &dsp_mode, sizeof(SDL_DisplayMode) );
|
|
|
|
while ( !SDL_GetDisplayMode( 0, ModeIndex++, &dsp_mode ) )
|
|
{
|
|
bool IsEqualOrBetterWidth = FMath::Abs((int32)dsp_mode.w - (int32)Width) <= FMath::Abs((int32)BestWidth - (int32)Width);
|
|
bool IsEqualOrBetterHeight = FMath::Abs((int32)dsp_mode.h - (int32)Height) <= FMath::Abs((int32)BestHeight - (int32)Height);
|
|
if (!InitializedMode || (IsEqualOrBetterWidth && IsEqualOrBetterHeight))
|
|
{
|
|
BestWidth = dsp_mode.w;
|
|
BestHeight = dsp_mode.h;
|
|
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)
|
|
{
|
|
MaxAllowableResolutionX = 10480;
|
|
}
|
|
if (MaxAllowableResolutionY == 0)
|
|
{
|
|
MaxAllowableResolutionY = 10480;
|
|
}
|
|
if (MaxAllowableRefreshRate == 0)
|
|
{
|
|
MaxAllowableRefreshRate = 10480;
|
|
}
|
|
|
|
uint32 ModeIndex = 0;
|
|
SDL_DisplayMode DisplayMode;
|
|
FMemory::Memzero(&DisplayMode, sizeof(SDL_DisplayMode));
|
|
|
|
while ( !SDL_GetDisplayMode( 0, ModeIndex++, &DisplayMode ) )
|
|
{
|
|
if (((int32)DisplayMode.w >= MinAllowableResolutionX) &&
|
|
((int32)DisplayMode.w <= MaxAllowableResolutionX) &&
|
|
((int32)DisplayMode.h >= MinAllowableResolutionY) &&
|
|
((int32)DisplayMode.h <= MaxAllowableResolutionY)
|
|
)
|
|
{
|
|
bool bAddIt = true;
|
|
if (bIgnoreRefreshRate == false)
|
|
{
|
|
if (((int32)DisplayMode.refresh_rate < MinAllowableRefreshRate) ||
|
|
((int32)DisplayMode.refresh_rate > 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.w) &&
|
|
(CheckResolution.Height == DisplayMode.h))
|
|
{
|
|
// 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.w;
|
|
ScreenResolution.Height = DisplayMode.h;
|
|
ScreenResolution.RefreshRate = DisplayMode.refresh_rate;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void PlatformRestoreDesktopDisplayMode()
|
|
{
|
|
// printf( "ArielPlatform - RestoreDesktopDisplayMode\n" );
|
|
}
|
|
|
|
|
|
bool PlatformInitOpenGL()
|
|
{
|
|
static bool bInitialized = false;
|
|
static bool bOpenGLSupported = false;
|
|
|
|
if (!FPlatformMisc::PlatformInitMultimedia()) // will not initialize more than once
|
|
{
|
|
UE_LOG(LogInit, Error, TEXT("PlatformInitOpenGL() : PlatformInitMultimedia() failed, cannot initialize OpenGL."));
|
|
// unreachable
|
|
return false;
|
|
}
|
|
|
|
#if DO_CHECK
|
|
uint32 InitializedSubsystems = SDL_WasInit(SDL_INIT_EVERYTHING);
|
|
check(InitializedSubsystems & SDL_INIT_VIDEO);
|
|
#endif // DO_CHECK
|
|
|
|
if (!bInitialized)
|
|
{
|
|
if (SDL_GL_LoadLibrary(NULL))
|
|
{
|
|
UE_LOG(LogLinux, Fatal, TEXT("Unable to dynamically load libGL: %s\n."), UTF8_TO_TCHAR(SDL_GetError()));
|
|
}
|
|
|
|
int MajorVersion = 0;
|
|
int MinorVersion = 0;
|
|
|
|
int DebugFlag = 0;
|
|
|
|
if ( _PlatformOpenGLDebugCtx() )
|
|
{
|
|
DebugFlag = SDL_GL_CONTEXT_DEBUG_FLAG;
|
|
}
|
|
|
|
PlatformOpenGLVersionFromCommandLine(MajorVersion, MinorVersion);
|
|
if (SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, MajorVersion) != 0)
|
|
{
|
|
UE_LOG(LogLinux, Fatal, TEXT("SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, %d) failed: %s"), MajorVersion, UTF8_TO_TCHAR(SDL_GetError()));
|
|
}
|
|
|
|
if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, MinorVersion) != 0)
|
|
{
|
|
UE_LOG(LogLinux, Fatal, TEXT("SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, %d) failed: %s"), MinorVersion, UTF8_TO_TCHAR(SDL_GetError()));
|
|
}
|
|
|
|
if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, DebugFlag) != 0)
|
|
{
|
|
UE_LOG(LogLinux, Fatal, TEXT("SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, %d) failed: %s"), DebugFlag, UTF8_TO_TCHAR(SDL_GetError()));
|
|
}
|
|
|
|
if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE) != 0)
|
|
{
|
|
UE_LOG(LogLinux, Fatal, TEXT("SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE) failed: %s"), UTF8_TO_TCHAR(SDL_GetError()));
|
|
}
|
|
|
|
if (FParse::Param(FCommandLine::Get(), TEXT("quad_buffer_stereo")))
|
|
{
|
|
if (SDL_GL_SetAttribute(SDL_GL_STEREO, 1) != 0)
|
|
{
|
|
UE_LOG(LogLinux, Fatal, TEXT("SDL_GL_SetAttribute(SDL_GL_STEREO, 1) failed: %s"), UTF8_TO_TCHAR(SDL_GetError()));
|
|
}
|
|
}
|
|
|
|
// Create a dummy context to verify opengl support.
|
|
FPlatformOpenGLContext DummyContext;
|
|
_PlatformCreateDummyGLWindow(&DummyContext);
|
|
_PlatformCreateOpenGLContextCore(&DummyContext );
|
|
|
|
if (DummyContext.hGLContext)
|
|
{
|
|
bOpenGLSupported = true;
|
|
_ContextMakeCurrent(DummyContext.hWnd, DummyContext.hGLContext);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogRHI,Error,TEXT("OpenGL %d.%d not supported by driver"),MajorVersion,MinorVersion);
|
|
}
|
|
|
|
if (bOpenGLSupported)
|
|
{
|
|
// Initialize all entry points required by Unreal.
|
|
#define GET_GL_ENTRYPOINTS(Type,Func) GLFuncPointers::Func = reinterpret_cast<Type>(SDL_GL_GetProcAddress(#Func));
|
|
ENUM_GL_ENTRYPOINTS(GET_GL_ENTRYPOINTS);
|
|
ENUM_GL_ENTRYPOINTS_OPTIONAL(GET_GL_ENTRYPOINTS);
|
|
|
|
// 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, Fatal, TEXT("Failed to find entry point for %s"), TEXT(#Func)); }
|
|
ENUM_GL_ENTRYPOINTS(CHECK_GL_ENTRYPOINTS);
|
|
checkf(bFoundAllEntryPoints, TEXT("Failed to find all OpenGL entry points."));
|
|
}
|
|
|
|
// The dummy context can now be released.
|
|
if (DummyContext.hGLContext)
|
|
{
|
|
_ContextMakeCurrent(NULL,NULL);
|
|
SDL_GL_DeleteContext(DummyContext.hGLContext);
|
|
}
|
|
check(DummyContext.bReleaseWindowOnDestroy);
|
|
SDL_DestroyWindow(DummyContext.hWnd);
|
|
|
|
bInitialized = true;
|
|
}
|
|
|
|
return bOpenGLSupported;
|
|
}
|
|
|
|
bool PlatformOpenGLContextValid()
|
|
{
|
|
return ( _GetCurrentContext() != NULL );
|
|
}
|
|
|
|
int32 PlatformGlGetError()
|
|
{
|
|
return glGetError();
|
|
}
|
|
|
|
EOpenGLCurrentContext PlatformOpenGLCurrentContext( FPlatformOpenGLDevice* Device )
|
|
{
|
|
SDL_HGLContext hGLContext = _GetCurrentContext();
|
|
|
|
if ( hGLContext == Device->RenderingContext.hGLContext ) // most common case
|
|
{
|
|
return CONTEXT_Rendering;
|
|
}
|
|
else if ( hGLContext == Device->SharedContext.hGLContext )
|
|
{
|
|
return CONTEXT_Shared;
|
|
}
|
|
else if ( hGLContext )
|
|
{
|
|
return CONTEXT_Other;
|
|
}
|
|
return CONTEXT_Invalid;
|
|
}
|
|
|
|
|
|
void PlatformGetBackbufferDimensions( uint32& OutWidth, uint32& OutHeight )
|
|
{
|
|
SDL_HWindow CurrentWindow = SDL_GL_GetCurrentWindow();
|
|
|
|
int Width = 0;
|
|
int Height = 0;
|
|
|
|
if (CurrentWindow)
|
|
{
|
|
SDL_GL_GetDrawableSize(CurrentWindow, &Width, &Height);
|
|
}
|
|
|
|
OutWidth = Width;
|
|
OutHeight = Height;
|
|
}
|
|
|
|
// =============================================================
|
|
|
|
struct FOpenGLReleasedQuery
|
|
{
|
|
SDL_HGLContext hGLContext;
|
|
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
|
|
|
|
SDL_HGLContext hGLContext = _GetCurrentContext();
|
|
check( hGLContext );
|
|
|
|
GLuint NewQuery = 0;
|
|
|
|
// Check for possible query reuse
|
|
const int32 ArraySize = ReleasedQueries.Num();
|
|
for( int32 Index = 0; Index < ArraySize; ++Index )
|
|
{
|
|
if( ReleasedQueries[Index].hGLContext == hGLContext )
|
|
{
|
|
NewQuery = ReleasedQueries[Index].Query;
|
|
ReleasedQueries.RemoveAtSwap(Index);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !NewQuery )
|
|
{
|
|
FOpenGL::GenQueries( 1, &NewQuery );
|
|
}
|
|
|
|
*OutQuery = NewQuery;
|
|
*OutQueryContext = (uint64)hGLContext;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PlatformReleaseRenderQuery( GLuint Query, uint64 QueryContext )
|
|
{
|
|
SDL_HGLContext hGLContext = _GetCurrentContext();
|
|
if( (uint64)hGLContext == QueryContext )
|
|
{
|
|
FOpenGL::DeleteQueries(1, &Query );
|
|
}
|
|
else
|
|
{
|
|
FScopeLock Lock(ReleasedQueriesGuard);
|
|
#ifdef UE_BUILD_DEBUG
|
|
check( Query && QueryContext && ReleasedQueriesGuard );
|
|
#endif
|
|
FOpenGLReleasedQuery ReleasedQuery;
|
|
ReleasedQuery.hGLContext = (SDL_HGLContext)QueryContext;
|
|
ReleasedQuery.Query = Query;
|
|
ReleasedQueries.Add(ReleasedQuery);
|
|
}
|
|
|
|
}
|
|
|
|
bool PlatformContextIsCurrent( uint64 QueryContext )
|
|
{
|
|
return (uint64)_GetCurrentContext() == QueryContext;
|
|
}
|
|
|
|
FRHITexture* PlatformCreateBuiltinBackBuffer( FOpenGLDynamicRHI* OpenGLRHI, uint32 SizeX, uint32 SizeY )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
void _DeleteQueriesForCurrentContext( SDL_HGLContext hGLContext )
|
|
{
|
|
if( !ReleasedQueriesGuard )
|
|
{
|
|
ReleasedQueriesGuard = new FCriticalSection;
|
|
}
|
|
|
|
{
|
|
FScopeLock Lock(ReleasedQueriesGuard);
|
|
for( int32 Index = 0; Index < ReleasedQueries.Num(); ++Index )
|
|
{
|
|
if( ReleasedQueries[Index].hGLContext == hGLContext )
|
|
{
|
|
FOpenGL::DeleteQueries(1,&ReleasedQueries[Index].Query);
|
|
ReleasedQueries.RemoveAtSwap(Index);
|
|
--Index;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void FLinuxOpenGL::ProcessExtensions( const FString& ExtensionsString )
|
|
{
|
|
FOpenGL4::ProcessExtensions(ExtensionsString);
|
|
|
|
FString VendorName( ANSI_TO_TCHAR((const ANSICHAR*)glGetString(GL_VENDOR) ) );
|
|
|
|
if ( VendorName.Contains(TEXT("ATI ")) )
|
|
{
|
|
// Workaround for AMD driver not handling GL_SRGB8_ALPHA8 in glTexStorage2D() properly (gets treated as non-sRGB)
|
|
glTexStorage1D = NULL;
|
|
glTexStorage2D = NULL;
|
|
glTexStorage3D = NULL;
|
|
|
|
FOpenGLBase::bSupportsCopyImage = false;
|
|
}
|
|
}
|
|
|
|
|
|
|