// Copyright Epic Games, Inc. All Rights Reserved. #include "LinuxOpenGLPlatform.h" #include "Misc/ScopeLock.h" #include "OpenGLDrv.h" #include #include "OpenGLDrvPrivate.h" #include "ComponentReregisterContext.h" #include "Linux/LinuxPlatformApplicationMisc.h" #include "GenericPlatform/GenericPlatformFramePacer.h" #include "RHIUtilities.h" typedef SDL_Window* SDL_HWindow; typedef SDL_GLContext SDL_HGLContext; /*------------------------------------------------------------------------------ OpenGL context management. ------------------------------------------------------------------------------*/ void Linux_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(UTF8_TO_TCHAR(SDL_GetError())); UE_LOG(LogLinux, Warning, TEXT("SDL_GL_MakeCurrent() failed, SDL error: '%s'"), *SdlError); } } SDL_HGLContext Linux_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 Linux_ContextMakeCurrent(Context->hWnd, Context->hGLContext); } } ~FScopeContext(void) { if (!bSameDCAndContext) { glFlush(); // not needed on Windows, it does flush by itself before switching contexts if (hPreGLContext) { Linux_ContextMakeCurrent(hPreWnd, hPreGLContext); } else { Linux_ContextMakeCurrent(NULL, NULL); } } } private: SDL_HWindow hPreWnd; SDL_HGLContext hPreGLContext; // this is a pointer, (void*) bool bSameDCAndContext; }; /** * Create a dummy window used to construct OpenGL contexts. */ void Linux_PlatformCreateDummyGLWindow(FPlatformOpenGLContext* OutContext) { static bool bInitializedWindowClass = false; // Create a dummy window. SDL_HWindow DummyWindow = SDL_CreateWindow(NULL, 1, 1, SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS | SDL_WINDOW_HIDDEN | SDL_WINDOW_UTILITY ); if (DummyWindow == nullptr) { FString SdlError(UTF8_TO_TCHAR(SDL_GetError())); FText ErrorMessage = FText::Format(NSLOCTEXT("Renderer", "LinuxCannotCreatePlatformWindow", "Cannot create OpenGL-enabled SDL window. SDL error: '{0}'."), FText::FromString(SdlError)); FPlatformMisc::MessageBoxExt(EAppMsgType::Ok, *ErrorMessage.ToString(), *(NSLOCTEXT("Renderer", "LinuxCannotCreatePlatformWindowTitle", "Cannot create SDL window.").ToString())); FPlatformMisc::RequestExit(true, TEXT("Linux_PlatformCreateDummyGLWindow")); // unreachable return; } else { SDL_SetWindowTitle(DummyWindow, "UnrealEditor Dummy GL window"); } OutContext->hWnd = DummyWindow; OutContext->bReleaseWindowOnDestroy = true; } /** * Enable/Disable debug context from the commandline */ bool Linux_PlatformOpenGLDebugCtx() { return IsOGLDebugOutputEnabled(); } /** * Create a core profile OpenGL context. */ void Linux_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(UTF8_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("Linux_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); /** Platform specific OpenGL device. */ struct FPlatformOpenGLDevice { static FPlatformOpenGLDevice* Singleton; FPlatformOpenGLContext RenderingContext; /** Guards against operating on viewport contexts from more than one thread at the same time. */ FCriticalSection* ContextUsageGuard; FPlatformOpenGLDevice() { check(!Singleton); Singleton = this; 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())); Linux_PlatformCreateDummyGLWindow(&RenderingContext); Linux_PlatformCreateOpenGLContextCore(&RenderingContext); if (RenderingContext.hGLContext == nullptr) { FPlatformMisc::MessageBoxExt(EAppMsgType::Ok, *(NSLOCTEXT("Renderer", "LinuxInsufficientDriversText", "Cannot create OpenGL context. Check that the drivers and hardware support at least OpenGL 4.3 (or re-run with -opengl3)").ToString()), *(NSLOCTEXT("Renderer", "LinuxInsufficientDriversTitle", "Insufficient drivers or hardware").ToString())); FPlatformMisc::RequestExit(true, TEXT("FPlatformOpenGLDevice()")); // unreachable return; } Linux_ContextMakeCurrent(RenderingContext.hWnd, RenderingContext.hGLContext); InitDebugContext(); glGenVertexArrays(1, &RenderingContext.VertexArrayObject); glBindVertexArray(RenderingContext.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())); } ~FPlatformOpenGLDevice() { Linux_ContextMakeCurrent(NULL, NULL); PlatformReleaseOpenGLContext(this, &RenderingContext); delete ContextUsageGuard; check(Singleton == this); Singleton = nullptr; } }; FPlatformOpenGLDevice* FPlatformOpenGLDevice::Singleton = nullptr; 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->RenderingContext.hGLContext); { FScopeContext Scope(&Device->RenderingContext); 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())); Linux_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); 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_DestroyContext(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(IRHICommandContext& RHICmdContext, FPlatformOpenGLDevice* Device, const FOpenGLViewport& Viewport, uint32 BackbufferSizeX, uint32 BackbufferSizeY, bool bPresent, bool bLockToVsync) { FPlatformOpenGLContext* const Context = Viewport.GetGLContext(); check(Context && Context->hWnd); FScopeLock ScopeLock(Device->ContextUsageGuard); int32 SyncInterval = (int32)RHIGetSyncInterval(); { FScopeContext ScopeContext(Context); if (Viewport.GetCustomPresent()) { glDisable(GL_FRAMEBUFFER_SRGB); bool bShouldPresent = Viewport.GetCustomPresent()->Present(RHICmdContext, 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_GetWindowSizeInPixels(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); } } return true; } void PlatformFlushIfNeeded() { glFinish(); } void PlatformRenderingContextSetup(FPlatformOpenGLDevice* Device) { check(Device && Device->RenderingContext.hWnd && Device->RenderingContext.hGLContext); if (Linux_GetCurrentContext()) { glFlush(); } Linux_ContextMakeCurrent(Device->RenderingContext.hWnd, Device->RenderingContext.hGLContext); } void PlatformNULLContextSetup() { if (Linux_GetCurrentContext()) { glFlush(); } Linux_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); { 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); FOpenGL::CheckFrameBuffer(); glViewport(0, 0, SizeX, SizeY); static GLfloat ZeroColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; glClearBufferfv(GL_COLOR, 0, ZeroColor); } 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; } } void PlatformGetSupportedResolution(uint32& Width, uint32& Height) { uint32 InitializedMode = false; uint32 BestWidth = 0; uint32 BestHeight = 0; uint32 ModeIndex = 0; SDL_DisplayID DisplayID = SDL_GetPrimaryDisplay(); int NumModes = 0; SDL_DisplayMode** Modes = SDL_GetFullscreenDisplayModes(DisplayID, &NumModes); if (Modes) { for (int i=0; i < NumModes; ++i) { SDL_DisplayMode* Mode = Modes[i]; bool IsEqualOrBetterWidth = FMath::Abs((int32)Mode->w - (int32)Width) <= FMath::Abs((int32)BestWidth - (int32)Width); bool IsEqualOrBetterHeight = FMath::Abs((int32)Mode->h - (int32)Height) <= FMath::Abs((int32)BestHeight - (int32)Height); if (!InitializedMode || (IsEqualOrBetterWidth && IsEqualOrBetterHeight)) { BestWidth = Mode->w; BestHeight = 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; } SDL_DisplayID Display = SDL_GetPrimaryDisplay(); int NumModes = 0; SDL_DisplayMode** Modes = SDL_GetFullscreenDisplayModes(Display, &NumModes); if (Modes) { for (int32 i = 0; i < NumModes; i++) { SDL_DisplayMode* Mode = Modes[i]; if (((int32)Mode->w >= MinAllowableResolutionX) && ((int32)Mode->w <= MaxAllowableResolutionX) && ((int32)Mode->h >= MinAllowableResolutionY) && ((int32)Mode->h <= MaxAllowableResolutionY) ) { bool bAddIt = true; if (bIgnoreRefreshRate == false) { if (((int32)Mode->refresh_rate < MinAllowableRefreshRate) || ((int32)Mode->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 == Mode->w) && (CheckResolution.Height == Mode->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 = Mode->w; ScreenResolution.Height = Mode->h; ScreenResolution.RefreshRate = Mode->refresh_rate; } } } SDL_free(Modes); } return true; } void PlatformRestoreDesktopDisplayMode() { } bool PlatformInitOpenGL() { static bool bInitialized = false; static bool bOpenGLSupported = false; if (!FLinuxPlatformApplicationMisc::InitSDL()) // will not initialize more than once { UE_LOG(LogInit, Error, TEXT("PlatformInitOpenGL() : InitSDL() failed, cannot initialize OpenGL.")); // unreachable return false; } #if DO_CHECK uint32 InitializedSubsystems = SDL_WasInit(SDL_INIT_VIDEO); check(InitializedSubsystems & SDL_INIT_VIDEO); #endif // DO_CHECK if (!bInitialized) { if (SDL_GL_LoadLibrary(NULL)) { FPlatformMisc::MessageBoxExt(EAppMsgType::Ok, *FString::Printf(TEXT("%s. SDL error: \"%s\""), *(NSLOCTEXT("Renderer", "LinuxCannotLoadLibGLText", "Unable to dynamically load libGL").ToString()), UTF8_TO_TCHAR(SDL_GetError())), *(NSLOCTEXT("Renderer", "LinuxInsufficientDriversTitle", "Insufficient drivers or hardware").ToString())); FPlatformMisc::RequestExit(true, TEXT("PlatformInitOpenGL")); // unreachable return false; } const int MajorVersion = 4; const int MinorVersion = 3; int DebugFlag = 0; if (Linux_PlatformOpenGLDebugCtx()) { DebugFlag = SDL_GL_CONTEXT_DEBUG_FLAG; } 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; Linux_PlatformCreateDummyGLWindow(&DummyContext); Linux_PlatformCreateOpenGLContextCore(&DummyContext); if (DummyContext.hGLContext) { bOpenGLSupported = true; Linux_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(SDL_GL_GetProcAddress(#Func)); ENUM_GL_ENTRYPOINTS(GET_GL_ENTRYPOINTS); ENUM_GL_ENTRYPOINTS_OPTIONAL(GET_GL_ENTRYPOINTS); #undef 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); #undef CHECK_GL_ENTRYPOINTS checkf(bFoundAllEntryPoints, TEXT("Failed to find all OpenGL entry points.")); } // The dummy context can now be released. if (DummyContext.hGLContext) { Linux_ContextMakeCurrent(NULL, NULL); SDL_GL_DestroyContext(DummyContext.hGLContext); } check(DummyContext.bReleaseWindowOnDestroy); SDL_DestroyWindow(DummyContext.hWnd); bInitialized = true; } return bOpenGLSupported; } int32 PlatformGlGetError() { return glGetError(); } bool PlatformOpenGLThreadHasRenderingContext() { return Linux_GetCurrentContext() == FPlatformOpenGLDevice::Singleton->RenderingContext.hGLContext; } // ============================================================= FOpenGLTexture* PlatformCreateBuiltinBackBuffer(FOpenGLDynamicRHI* OpenGLRHI, uint32 SizeX, uint32 SizeY) { return nullptr; }