You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
This change is the copy-up of work from //UE5/Dev-ParallelRendering to unify the various RHI texture types - The separate RHI texture types (FRHITexture2D, FRHITexture2DArray, FRHITexture3D, FRHITextureCube, FRHITextureCubeArray) have been replaced with a single type: FRHITexture. - Includes the required refactoring / fixes for the various platform RHIs. The old texture types are still supported via typedefs, but will soon be marked deprecated. Future rendering code should make use of the FRHITexture type instead. #jira UE-139559 #rb Mihnea.Balta #preflight 622f3af819287beb2c3047f6 #robomerge[FN_ENGINEMERGE] DEV-NC [CL 19371962 by Luke Thatcher in ue5-main branch]
731 lines
22 KiB
C++
731 lines
22 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
OpenGLDrv.cpp: Unreal OpenGL RHI library implementation.
|
|
=============================================================================*/
|
|
|
|
#include "OpenGLDrv.h"
|
|
#include "Modules/ModuleManager.h"
|
|
#include "EngineGlobals.h"
|
|
#include "StaticBoundShaderState.h"
|
|
#include "RHIStaticStates.h"
|
|
#include "Engine/Engine.h"
|
|
#include "OpenGLDrvPrivate.h"
|
|
#include "PipelineStateCache.h"
|
|
#include "Engine/GameViewportClient.h"
|
|
|
|
|
|
IMPLEMENT_MODULE(FOpenGLDynamicRHIModule, OpenGLDrv);
|
|
|
|
#include "Shader.h"
|
|
#include "OneColorShader.h"
|
|
#include "OpenGLShaders.h"
|
|
|
|
/** OpenGL Logging. */
|
|
DEFINE_LOG_CATEGORY(LogOpenGL);
|
|
|
|
#define LOCTEXT_NAMESPACE "OpenGLDrv"
|
|
|
|
ERHIFeatureLevel::Type GRequestedFeatureLevel = ERHIFeatureLevel::Num;
|
|
|
|
|
|
int32 FOpenGLDynamicRHI::RHIGetGLMajorVersion() const
|
|
{
|
|
return FOpenGL::GetMajorVersion();
|
|
}
|
|
|
|
int32 FOpenGLDynamicRHI::RHIGetGLMinorVersion() const
|
|
{
|
|
return FOpenGL::GetMinorVersion();
|
|
}
|
|
|
|
bool FOpenGLDynamicRHI::RHISupportsFramebufferSRGBEnable() const
|
|
{
|
|
return FOpenGL::SupportsFramebufferSRGBEnable();
|
|
}
|
|
|
|
GLuint FOpenGLDynamicRHI::RHIGetResource(FRHITexture* InTexture) const
|
|
{
|
|
FOpenGLTexture* GLTexture = GetOpenGLTextureFromRHITexture(InTexture);
|
|
return GLTexture->GetResource();
|
|
}
|
|
|
|
bool FOpenGLDynamicRHI::RHIIsValidTexture(GLuint InTexture) const
|
|
{
|
|
return glIsTexture(InTexture) == GL_TRUE;
|
|
}
|
|
|
|
void FOpenGLDynamicRHI::RHISetExternalGPUTime(uint32 InExternalGPUTime)
|
|
{
|
|
GetGPUProfilingData().ExternalGPUTime = InExternalGPUTime;
|
|
}
|
|
|
|
#if PLATFORM_ANDROID
|
|
|
|
EGLDisplay FOpenGLDynamicRHI::RHIGetEGLDisplay() const
|
|
{
|
|
return AndroidEGL::GetInstance()->GetDisplay();
|
|
}
|
|
|
|
EGLSurface FOpenGLDynamicRHI::RHIGetEGLSurface() const
|
|
{
|
|
return AndroidEGL::GetInstance()->GetSurface();
|
|
}
|
|
|
|
EGLConfig FOpenGLDynamicRHI::RHIGetEGLConfig() const
|
|
{
|
|
return AndroidEGL::GetInstance()->GetConfig();
|
|
}
|
|
|
|
EGLContext FOpenGLDynamicRHI::RHIGetEGLContext() const
|
|
{
|
|
return AndroidEGL::GetInstance()->GetRenderingContext()->eglContext;
|
|
}
|
|
|
|
ANativeWindow* FOpenGLDynamicRHI::RHIGetEGLNativeWindow() const
|
|
{
|
|
return AndroidEGL::GetInstance()->GetNativeWindow();
|
|
}
|
|
|
|
bool FOpenGLDynamicRHI::RHIEGLSupportsNoErrorContext() const
|
|
{
|
|
return AndroidEGL::GetInstance()->GetSupportsNoErrorContext();
|
|
}
|
|
|
|
void FOpenGLDynamicRHI::RHIInitEGLInstanceGLES2()
|
|
{
|
|
AndroidEGL::GetInstance()->Init(AndroidEGL::AV_OpenGLES, 2, 0, false);
|
|
AndroidEGL::GetInstance()->InitSurface(false, false);
|
|
}
|
|
|
|
void FOpenGLDynamicRHI::RHIInitEGLBackBuffer()
|
|
{
|
|
AndroidEGL::GetInstance()->InitBackBuffer();
|
|
}
|
|
|
|
void FOpenGLDynamicRHI::RHIEGLSetCurrentRenderingContext()
|
|
{
|
|
AndroidEGL::GetInstance()->SetCurrentRenderingContext();
|
|
}
|
|
|
|
void FOpenGLDynamicRHI::RHIEGLTerminateContext()
|
|
{
|
|
AndroidEGL::GetInstance()->Terminate();
|
|
}
|
|
#endif
|
|
|
|
|
|
void FOpenGLDynamicRHI::RHIPushEvent(const TCHAR* Name, FColor Color)
|
|
{
|
|
#if ENABLE_OPENGL_DEBUG_GROUPS
|
|
// @todo-mobile: Fix string conversion ASAP!
|
|
FOpenGL::PushGroupMarker(TCHAR_TO_ANSI(Name));
|
|
#endif
|
|
GPUProfilingData.PushEvent(Name, Color);
|
|
}
|
|
|
|
void FOpenGLGPUProfiler::PushEvent(const TCHAR* Name, FColor Color)
|
|
{
|
|
FGPUProfiler::PushEvent(Name, Color);
|
|
}
|
|
|
|
void FOpenGLDynamicRHI::RHIPopEvent()
|
|
{
|
|
#if ENABLE_OPENGL_DEBUG_GROUPS
|
|
FOpenGL::PopGroupMarker();
|
|
#endif
|
|
|
|
GPUProfilingData.PopEvent();
|
|
}
|
|
|
|
void FOpenGLGPUProfiler::PopEvent()
|
|
{
|
|
FGPUProfiler::PopEvent();
|
|
|
|
}
|
|
|
|
bool FOpenGLDynamicRHI::RHIRequiresComputeGenerateMips() const
|
|
{
|
|
return !FOpenGL::SupportsGenerateMipmap();
|
|
};
|
|
|
|
void FOpenGLGPUProfiler::BeginFrame(FOpenGLDynamicRHI* InRHI)
|
|
{
|
|
if (!bIntialized)
|
|
{
|
|
bIntialized = true;
|
|
InitResources();
|
|
}
|
|
if (NestedFrameCount++>0)
|
|
{
|
|
// guard against nested Begin/EndFrame calls.
|
|
return;
|
|
}
|
|
|
|
CurrentEventNode = NULL;
|
|
check(!bTrackingEvents);
|
|
check(!CurrentEventNodeFrame); // this should have already been cleaned up and the end of the previous frame
|
|
|
|
// latch the bools from the game thread into our private copy
|
|
bLatchedGProfilingGPU = GTriggerGPUProfile;
|
|
bLatchedGProfilingGPUHitches = GTriggerGPUHitchProfile;
|
|
if (bLatchedGProfilingGPUHitches)
|
|
{
|
|
bLatchedGProfilingGPU = false; // we do NOT permit an ordinary GPU profile during hitch profiles
|
|
}
|
|
|
|
// if we are starting a hitch profile or this frame is a gpu profile, then save off the state of the draw events
|
|
if (bLatchedGProfilingGPU || (!bPreviousLatchedGProfilingGPUHitches && bLatchedGProfilingGPUHitches))
|
|
{
|
|
bOriginalGEmitDrawEvents = GetEmitDrawEvents();
|
|
}
|
|
|
|
if (bLatchedGProfilingGPU || bLatchedGProfilingGPUHitches)
|
|
{
|
|
if (bLatchedGProfilingGPUHitches && GPUHitchDebounce)
|
|
{
|
|
// if we are doing hitches and we had a recent hitch, wait to recover
|
|
// the reasoning is that collecting the hitch report may itself hitch the GPU
|
|
GPUHitchDebounce--;
|
|
}
|
|
else
|
|
{
|
|
SetEmitDrawEvents(true); // thwart an attempt to turn this off on the game side
|
|
bTrackingEvents = true;
|
|
CurrentEventNodeFrame = new FOpenGLEventNodeFrame(InRHI);
|
|
CurrentEventNodeFrame->StartFrame();
|
|
}
|
|
}
|
|
else if (bPreviousLatchedGProfilingGPUHitches)
|
|
{
|
|
// hitch profiler is turning off, clear history and restore draw events
|
|
GPUHitchEventNodeFrames.Empty();
|
|
SetEmitDrawEvents(bOriginalGEmitDrawEvents);
|
|
}
|
|
bPreviousLatchedGProfilingGPUHitches = bLatchedGProfilingGPUHitches;
|
|
|
|
// Skip timing events when using SLI, they will not be accurate anyway
|
|
if (GNumAlternateFrameRenderingGroups == 1)
|
|
{
|
|
if (FrameTiming.IsSupported())
|
|
{
|
|
FrameTiming.StartTiming();
|
|
}
|
|
if (FOpenGLDisjointTimeStampQuery::IsSupported())
|
|
{
|
|
CurrentGPUFrameQueryIndex = (CurrentGPUFrameQueryIndex + 1) % MAX_GPUFRAMEQUERIES;
|
|
DisjointGPUFrameTimeQuery[CurrentGPUFrameQueryIndex].StartTracking();
|
|
}
|
|
}
|
|
|
|
if (GetEmitDrawEvents())
|
|
{
|
|
PushEvent(TEXT("FRAME"), FColor(0, 255, 0, 255));
|
|
}
|
|
}
|
|
|
|
void FOpenGLGPUProfiler::EndFrame()
|
|
{
|
|
if (--NestedFrameCount != 0)
|
|
{
|
|
// ignore endframes calls from nested beginframe calls.
|
|
return;
|
|
}
|
|
|
|
if (GetEmitDrawEvents())
|
|
{
|
|
PopEvent();
|
|
}
|
|
|
|
// Skip timing events when using SLI, they will not be accurate anyway
|
|
if (GNumAlternateFrameRenderingGroups == 1)
|
|
{
|
|
if (FrameTiming.IsSupported())
|
|
{
|
|
FrameTiming.EndTiming();
|
|
}
|
|
if (FOpenGLDisjointTimeStampQuery::IsSupported())
|
|
{
|
|
DisjointGPUFrameTimeQuery[CurrentGPUFrameQueryIndex].EndTracking();
|
|
}
|
|
}
|
|
|
|
// Skip timing events when using SLI, as they will block the GPU and we want maximum throughput
|
|
// Stat unit GPU time is not accurate anyway with SLI
|
|
if (FrameTiming.IsSupported() && GNumAlternateFrameRenderingGroups == 1)
|
|
{
|
|
uint64 GPUTiming = FrameTiming.GetTiming();
|
|
uint64 GPUFreq = FrameTiming.GetTimingFrequency();
|
|
GGPUFrameTime = FMath::TruncToInt( double(GPUTiming) / double(GPUFreq) / FPlatformTime::GetSecondsPerCycle() );
|
|
}
|
|
else if (FOpenGLDisjointTimeStampQuery::IsSupported() && GNumAlternateFrameRenderingGroups == 1)
|
|
{
|
|
static uint32 GLastGPUFrameTime = 0;
|
|
uint64 GPUTiming = 0;
|
|
uint64 GPUFreq = FOpenGLDisjointTimeStampQuery::GetTimingFrequency();
|
|
int OldestQueryIndex = (CurrentGPUFrameQueryIndex + 1) % MAX_GPUFRAMEQUERIES;
|
|
if ( DisjointGPUFrameTimeQuery[OldestQueryIndex].IsResultValid() && DisjointGPUFrameTimeQuery[OldestQueryIndex].GetResult(&GPUTiming) )
|
|
{
|
|
GGPUFrameTime = FMath::TruncToInt( double(GPUTiming) / double(GPUFreq) / FPlatformTime::GetSecondsPerCycle() );
|
|
GLastGPUFrameTime = GGPUFrameTime;
|
|
}
|
|
else
|
|
{
|
|
// Keep the timing of the last frame if the query was disjoint (e.g. GPU frequency changed and the result is undefined)
|
|
GGPUFrameTime = GLastGPUFrameTime;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GGPUFrameTime = ExternalGPUTime;
|
|
}
|
|
|
|
// if we have a frame open, close it now.
|
|
if (CurrentEventNodeFrame)
|
|
{
|
|
CurrentEventNodeFrame->EndFrame();
|
|
}
|
|
|
|
check(!bTrackingEvents || bLatchedGProfilingGPU || bLatchedGProfilingGPUHitches);
|
|
check(!bTrackingEvents || CurrentEventNodeFrame);
|
|
if (bLatchedGProfilingGPU)
|
|
{
|
|
if (bTrackingEvents)
|
|
{
|
|
SetEmitDrawEvents(bOriginalGEmitDrawEvents);
|
|
UE_LOG(LogRHI, Warning, TEXT(""));
|
|
UE_LOG(LogRHI, Warning, TEXT(""));
|
|
CurrentEventNodeFrame->DumpEventTree();
|
|
|
|
// OPENGL_PERFORMANCE_DATA_INVALID is a compile time constant
|
|
bool DebugEnabled = false;
|
|
#ifdef GL_ARB_debug_output
|
|
DebugEnabled = GL_TRUE == glIsEnabled( GL_DEBUG_OUTPUT );
|
|
#endif
|
|
#if !OPENGL_PERFORMANCE_DATA_INVALID
|
|
if( DebugEnabled )
|
|
#endif
|
|
{
|
|
UE_LOG(LogRHI, Warning, TEXT(""));
|
|
UE_LOG(LogRHI, Warning, TEXT(""));
|
|
UE_LOG(LogRHI, Warning, TEXT("*********************************************************************************************"));
|
|
UE_LOG(LogRHI, Warning, TEXT("OpenGL perfomance data is potentially invalid because of the following build/runtime options:"));
|
|
|
|
#define LOG_GL_DEBUG_FLAG(a) UE_LOG(LogRHI, Warning, TEXT(" built with %s = %d"), TEXT(#a), a);
|
|
LOG_GL_DEBUG_FLAG(ENABLE_VERIFY_GL);
|
|
LOG_GL_DEBUG_FLAG(ENABLE_UNIFORM_BUFFER_LAYOUT_VERIFICATION);
|
|
LOG_GL_DEBUG_FLAG(ENABLE_UNIFORM_BUFFER_LAYOUT_DUMP);
|
|
LOG_GL_DEBUG_FLAG(DEBUG_GL_SHADERS);
|
|
LOG_GL_DEBUG_FLAG(ENABLE_OPENGL_DEBUG_GROUPS);
|
|
LOG_GL_DEBUG_FLAG(OPENGL_PERFORMANCE_DATA_INVALID);
|
|
#undef LOG_GL_DEBUG_FLAG
|
|
|
|
UE_LOG(LogRHI, Warning, TEXT("*********************************************************************************************"));
|
|
UE_LOG(LogRHI, Warning, TEXT(""));
|
|
UE_LOG(LogRHI, Warning, TEXT(""));
|
|
}
|
|
|
|
|
|
GTriggerGPUProfile = false;
|
|
bLatchedGProfilingGPU = false;
|
|
|
|
if (RHIConfig::ShouldSaveScreenshotAfterProfilingGPU()
|
|
&& GEngine->GameViewport)
|
|
{
|
|
GEngine->GameViewport->Exec( NULL, TEXT("SCREENSHOT"), *GLog);
|
|
}
|
|
}
|
|
}
|
|
else if (bLatchedGProfilingGPUHitches)
|
|
{
|
|
//@todo this really detects any hitch, even one on the game thread.
|
|
// it would be nice to restrict the test to stalls on D3D, but for now...
|
|
// this needs to be out here because bTrackingEvents is false during the hitch debounce
|
|
static double LastTime = -1.0;
|
|
double Now = FPlatformTime::Seconds();
|
|
if (bTrackingEvents)
|
|
{
|
|
/** How long, in seconds a frame much be to be considered a hitch **/
|
|
static const float HitchThreshold = .1f; //100ms
|
|
float ThisTime = Now - LastTime;
|
|
bool bHitched = (ThisTime > HitchThreshold) && LastTime > 0.0 && CurrentEventNodeFrame;
|
|
if (bHitched)
|
|
{
|
|
UE_LOG(LogRHI, Warning, TEXT("*******************************************************************************"));
|
|
UE_LOG(LogRHI, Warning, TEXT("********** Hitch detected on CPU, frametime = %6.1fms"),ThisTime * 1000.0f);
|
|
UE_LOG(LogRHI, Warning, TEXT("*******************************************************************************"));
|
|
|
|
for (int32 Frame = 0; Frame < GPUHitchEventNodeFrames.Num(); Frame++)
|
|
{
|
|
UE_LOG(LogRHI, Warning, TEXT(""));
|
|
UE_LOG(LogRHI, Warning, TEXT(""));
|
|
UE_LOG(LogRHI, Warning, TEXT("********** GPU Frame: Current - %d"),GPUHitchEventNodeFrames.Num() - Frame);
|
|
GPUHitchEventNodeFrames[Frame].DumpEventTree();
|
|
}
|
|
UE_LOG(LogRHI, Warning, TEXT(""));
|
|
UE_LOG(LogRHI, Warning, TEXT(""));
|
|
UE_LOG(LogRHI, Warning, TEXT("********** GPU Frame: Current"));
|
|
CurrentEventNodeFrame->DumpEventTree();
|
|
|
|
UE_LOG(LogRHI, Warning, TEXT("*******************************************************************************"));
|
|
UE_LOG(LogRHI, Warning, TEXT("********** End Hitch GPU Profile"));
|
|
UE_LOG(LogRHI, Warning, TEXT("*******************************************************************************"));
|
|
if (GEngine->GameViewport)
|
|
{
|
|
GEngine->GameViewport->Exec( NULL, TEXT("SCREENSHOT"), *GLog);
|
|
}
|
|
|
|
GPUHitchDebounce = 5; // don't trigger this again for a while
|
|
GPUHitchEventNodeFrames.Empty(); // clear history
|
|
}
|
|
else if (CurrentEventNodeFrame) // this will be null for discarded frames while recovering from a recent hitch
|
|
{
|
|
/** How many old frames to buffer for hitch reports **/
|
|
static const int32 HitchHistorySize = 4;
|
|
|
|
if (GPUHitchEventNodeFrames.Num() >= HitchHistorySize)
|
|
{
|
|
GPUHitchEventNodeFrames.RemoveAt(0);
|
|
}
|
|
GPUHitchEventNodeFrames.Add((FOpenGLEventNodeFrame*)CurrentEventNodeFrame);
|
|
CurrentEventNodeFrame = NULL; // prevent deletion of this below; ke kept it in the history
|
|
}
|
|
}
|
|
LastTime = Now;
|
|
}
|
|
bTrackingEvents = false;
|
|
delete CurrentEventNodeFrame;
|
|
CurrentEventNodeFrame = NULL;
|
|
}
|
|
|
|
void FOpenGLGPUProfiler::Cleanup()
|
|
{
|
|
if (bIntialized)
|
|
{
|
|
for (int32 Index = 0; Index < MAX_GPUFRAMEQUERIES; ++Index)
|
|
{
|
|
DisjointGPUFrameTimeQuery[Index].ReleaseResources();
|
|
}
|
|
|
|
FrameTiming.ReleaseResources();
|
|
NestedFrameCount = 0;
|
|
bIntialized = false;
|
|
}
|
|
}
|
|
|
|
/** Start this frame of per tracking */
|
|
void FOpenGLEventNodeFrame::StartFrame()
|
|
{
|
|
EventTree.Reset();
|
|
DisjointQuery.StartTracking();
|
|
RootEventTiming.StartTiming();
|
|
}
|
|
|
|
/** End this frame of per tracking, but do not block yet */
|
|
void FOpenGLEventNodeFrame::EndFrame()
|
|
{
|
|
RootEventTiming.EndTiming();
|
|
DisjointQuery.EndTracking();
|
|
}
|
|
|
|
|
|
float FOpenGLEventNodeFrame::GetRootTimingResults()
|
|
{
|
|
double RootResult = 0.0f;
|
|
if (RootEventTiming.IsSupported())
|
|
{
|
|
const uint64 GPUTiming = RootEventTiming.GetTiming(true);
|
|
const uint64 GPUFreq = RootEventTiming.GetTimingFrequency();
|
|
|
|
RootResult = double(GPUTiming) / double(GPUFreq);
|
|
}
|
|
|
|
return (float)RootResult;
|
|
}
|
|
|
|
void FOpenGLEventNodeFrame::LogDisjointQuery()
|
|
{
|
|
if (DisjointQuery.IsSupported())
|
|
{
|
|
UE_LOG(LogRHI, Warning, TEXT("%s"),
|
|
DisjointQuery.IsResultValid() ?
|
|
TEXT("Profiled range was continuous.") :
|
|
TEXT("Profiled range was disjoint! GPU switched to doing something else while profiling.")
|
|
);
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogRHI, Warning, TEXT("Profiled range \"disjoinness\" could not be determined due to lack of disjoint timer query functionality on this platform."));
|
|
}
|
|
}
|
|
|
|
float FOpenGLEventNode::GetTiming()
|
|
{
|
|
float Result = 0;
|
|
|
|
if (Timing.IsSupported())
|
|
{
|
|
// Get the timing result and block the CPU until it is ready
|
|
const uint64 GPUTiming = Timing.GetTiming(true);
|
|
const uint64 GPUFreq = Timing.GetTimingFrequency();
|
|
|
|
Result = double(GPUTiming) / double(GPUFreq);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
void FOpenGLDynamicRHI::InitializeStateResources()
|
|
{
|
|
VERIFY_GL_SCOPE();
|
|
SharedContextState.InitializeResources(FOpenGL::GetMaxCombinedTextureImageUnits(), FOpenGL::GetMaxCombinedUAVUnits());
|
|
RenderingContextState.InitializeResources(FOpenGL::GetMaxCombinedTextureImageUnits(), FOpenGL::GetMaxCombinedUAVUnits());
|
|
PendingState.InitializeResources(FOpenGL::GetMaxCombinedTextureImageUnits(), FOpenGL::GetMaxCombinedUAVUnits());
|
|
}
|
|
|
|
GLint FOpenGLBase::MaxTextureImageUnits = -1;
|
|
GLint FOpenGLBase::MaxCombinedTextureImageUnits = -1;
|
|
GLint FOpenGLBase::MaxVertexTextureImageUnits = -1;
|
|
GLint FOpenGLBase::MaxGeometryTextureImageUnits = -1;
|
|
GLint FOpenGLBase::MaxVaryingVectors = -1;
|
|
GLint FOpenGLBase::TextureBufferAlignment = -1;
|
|
GLint FOpenGLBase::MaxVertexUniformComponents = -1;
|
|
GLint FOpenGLBase::MaxPixelUniformComponents = -1;
|
|
GLint FOpenGLBase::MaxGeometryUniformComponents = -1;
|
|
bool FOpenGLBase::bSupportsClipControl = false;
|
|
bool FOpenGLBase::bSupportsASTC = false;
|
|
bool FOpenGLBase::bSupportsASTCHDR = false;
|
|
bool FOpenGLBase::bSupportsCopyImage = false;
|
|
bool FOpenGLBase::bSupportsSeamlessCubemap = false;
|
|
bool FOpenGLBase::bSupportsVolumeTextureRendering = false;
|
|
bool FOpenGLBase::bSupportsTextureFilterAnisotropic = false;
|
|
bool FOpenGLBase::bSupportsDrawBuffersBlend = false;
|
|
bool FOpenGLBase::bAmdWorkaround = false;
|
|
|
|
void FOpenGLBase::ProcessQueryGLInt()
|
|
{
|
|
GET_GL_INT(GL_MAX_TEXTURE_IMAGE_UNITS, 0, MaxTextureImageUnits);
|
|
GET_GL_INT(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, 0, MaxVertexTextureImageUnits);
|
|
GET_GL_INT(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, 0, MaxCombinedTextureImageUnits);
|
|
}
|
|
|
|
void FOpenGLBase::ProcessExtensions( const FString& ExtensionsString )
|
|
{
|
|
ProcessQueryGLInt();
|
|
|
|
// For now, just allocate additional units if available and advertise no tessellation units for HW that can't handle more
|
|
if ( MaxCombinedTextureImageUnits < 48 )
|
|
{
|
|
// To work around AMD driver limitation of 32 GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
|
|
// Going to hard code this for now (16 units in PS, 8 units in VS, 8 units in GS).
|
|
// This is going to be a problem for tessellation.
|
|
MaxTextureImageUnits = MaxTextureImageUnits > 16 ? 16 : MaxTextureImageUnits;
|
|
MaxVertexTextureImageUnits = MaxVertexTextureImageUnits > 8 ? 8 : MaxVertexTextureImageUnits;
|
|
MaxGeometryTextureImageUnits = MaxGeometryTextureImageUnits > 8 ? 8 : MaxGeometryTextureImageUnits;
|
|
MaxCombinedTextureImageUnits = MaxCombinedTextureImageUnits > 32 ? 32 : MaxCombinedTextureImageUnits;
|
|
}
|
|
else
|
|
{
|
|
// clamp things to the levels that the other path is going, but allow additional units for tessellation
|
|
if (IsMobilePlatform(GMaxRHIShaderPlatform))
|
|
{
|
|
MaxTextureImageUnits = MaxTextureImageUnits > 16 ? 16 : MaxTextureImageUnits;
|
|
MaxVertexTextureImageUnits = MaxVertexTextureImageUnits > 8 ? 8 : MaxVertexTextureImageUnits;
|
|
MaxGeometryTextureImageUnits = MaxGeometryTextureImageUnits > 8 ? 8 : MaxGeometryTextureImageUnits;
|
|
MaxCombinedTextureImageUnits = MaxCombinedTextureImageUnits > 48 ? 48 : MaxCombinedTextureImageUnits;
|
|
}
|
|
}
|
|
|
|
// Check for support for advanced texture compression (desktop and mobile)
|
|
bSupportsASTC = ExtensionsString.Contains(TEXT("GL_KHR_texture_compression_astc_ldr"));
|
|
|
|
bSupportsASTCHDR = bSupportsASTC && ExtensionsString.Contains(TEXT("GL_KHR_texture_compression_astc_hdr"));
|
|
|
|
// check for copy image support
|
|
bSupportsCopyImage = ExtensionsString.Contains(TEXT("GL_ARB_copy_image"));
|
|
|
|
bSupportsSeamlessCubemap = ExtensionsString.Contains(TEXT("GL_ARB_seamless_cube_map"));
|
|
|
|
bSupportsTextureFilterAnisotropic = ExtensionsString.Contains(TEXT("GL_EXT_texture_filter_anisotropic"));
|
|
|
|
bSupportsDrawBuffersBlend = ExtensionsString.Contains(TEXT("GL_ARB_draw_buffers_blend"));
|
|
|
|
#if PLATFORM_IOS
|
|
GRHIVendorId = 0x1010;
|
|
#else
|
|
FString VendorName( ANSI_TO_TCHAR((const ANSICHAR*)glGetString(GL_VENDOR) ) );
|
|
if (VendorName.Contains(TEXT("ATI ")))
|
|
{
|
|
GRHIVendorId = 0x1002;
|
|
#if PLATFORM_WINDOWS || PLATFORM_LINUX
|
|
bAmdWorkaround = true;
|
|
#endif
|
|
}
|
|
#if PLATFORM_LINUX
|
|
else if (VendorName.Contains(TEXT("X.Org")))
|
|
{
|
|
GRHIVendorId = 0x1002;
|
|
bAmdWorkaround = true;
|
|
}
|
|
#endif
|
|
else if (VendorName.Contains(TEXT("Intel ")) || VendorName == TEXT("Intel"))
|
|
{
|
|
GRHIVendorId = 0x8086;
|
|
#if PLATFORM_WINDOWS || PLATFORM_LINUX
|
|
bAmdWorkaround = true;
|
|
#endif
|
|
}
|
|
else if (VendorName.Contains(TEXT("NVIDIA ")))
|
|
{
|
|
GRHIVendorId = 0x10DE;
|
|
}
|
|
else if (VendorName.Contains(TEXT("ImgTec")))
|
|
{
|
|
GRHIVendorId = 0x1010;
|
|
}
|
|
else if (VendorName.Contains(TEXT("ARM")))
|
|
{
|
|
GRHIVendorId = 0x13B5;
|
|
}
|
|
else if (VendorName.Contains(TEXT("Qualcomm")))
|
|
{
|
|
GRHIVendorId = 0x5143;
|
|
}
|
|
|
|
#if PLATFORM_LINUX
|
|
if (GRHIVendorId == 0x0)
|
|
{
|
|
// Try harder for Mesa
|
|
const ANSICHAR* AnsiVersion = (const ANSICHAR*)glGetString(GL_VERSION);
|
|
const ANSICHAR* AnsiRenderer = (const ANSICHAR*)glGetString(GL_RENDERER);
|
|
if (AnsiVersion && AnsiRenderer)
|
|
{
|
|
if (FCStringAnsi::Strstr(AnsiVersion, "Mesa"))
|
|
{
|
|
if (FCStringAnsi::Strstr(AnsiRenderer, "AMD") || FCStringAnsi::Strstr(AnsiRenderer, "ATI"))
|
|
{
|
|
// Radeon
|
|
GRHIVendorId = 0x1002;
|
|
bAmdWorkaround = true;
|
|
}
|
|
else if (FCStringAnsi::Strstr(AnsiRenderer, "Intel"))
|
|
{
|
|
GRHIVendorId = 0x8086;
|
|
bAmdWorkaround = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If still not detected, show a message box to the user (editor build only) and
|
|
// set GRHIVendorId to something to avoid crashing in check()s later
|
|
if (GRHIVendorId == 0x0)
|
|
{
|
|
if (WITH_EDITOR != 0 && !IsRunningCommandlet() && !FApp::IsUnattended())
|
|
{
|
|
FString GlRenderer(ANSI_TO_TCHAR(AnsiRenderer));
|
|
FText ErrorMessage = FText::Format(LOCTEXT("CannotDetermineGraphicsDriversVendor", "Unknown graphics drivers '{0}' by '{1}' are installed on this system. You may experience visual artifacts and other problems."),
|
|
FText::FromString(GlRenderer), FText::FromString(VendorName));
|
|
FPlatformMisc::MessageBoxExt(EAppMsgType::Ok, *ErrorMessage.ToString(),
|
|
*LOCTEXT("CannotDetermineGraphicsDriversVendorTitle", "Cannot determine driver vendor.").ToString());
|
|
}
|
|
|
|
GRHIVendorId = 0xFFFF;
|
|
bAmdWorkaround = true; // be conservative here as well.
|
|
}
|
|
}
|
|
#endif // PLATFORM_LINUX
|
|
|
|
#if PLATFORM_WINDOWS
|
|
auto* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("OpenGL.UseStagingBuffer"));
|
|
if (CVar)
|
|
{
|
|
CVar->Set(false);
|
|
}
|
|
#endif
|
|
#endif // !PLATFORM_IOS
|
|
|
|
// Setup CVars that require the RHI initialized
|
|
}
|
|
|
|
void FOpenGLBase::PE_GetCurrentOpenGLShaderDeviceCapabilities(FOpenGLShaderDeviceCapabilities& Capabilities)
|
|
{
|
|
Capabilities.TargetPlatform = EOpenGLShaderTargetPlatform::OGLSTP_Unknown;
|
|
}
|
|
|
|
void GetExtensionsString( FString& ExtensionsString)
|
|
{
|
|
GLint ExtensionCount = 0;
|
|
ExtensionsString = TEXT("");
|
|
if ( FOpenGL::SupportsIndexedExtensions() )
|
|
{
|
|
glGetIntegerv(GL_NUM_EXTENSIONS, &ExtensionCount);
|
|
for (int32 ExtensionIndex = 0; ExtensionIndex < ExtensionCount; ++ExtensionIndex)
|
|
{
|
|
const ANSICHAR* ExtensionString = FOpenGL::GetStringIndexed(GL_EXTENSIONS, ExtensionIndex);
|
|
|
|
ExtensionsString += TEXT(" ");
|
|
ExtensionsString += ANSI_TO_TCHAR(ExtensionString);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const ANSICHAR* GlGetStringOutput = (const ANSICHAR*) glGetString( GL_EXTENSIONS );
|
|
if (GlGetStringOutput)
|
|
{
|
|
ExtensionsString += GlGetStringOutput;
|
|
ExtensionsString += TEXT(" ");
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace OpenGLConsoleVariables
|
|
{
|
|
#if PLATFORM_WINDOWS || PLATFORM_LINUX
|
|
int32 bUseGlClipControlIfAvailable = 1;
|
|
#else
|
|
int32 bUseGlClipControlIfAvailable = 0;
|
|
#endif
|
|
static FAutoConsoleVariableRef CVarUseGlClipControlIfAvailable(
|
|
TEXT("OpenGL.UseGlClipControlIfAvailable"),
|
|
bUseGlClipControlIfAvailable,
|
|
TEXT("If true, the engine trys to use glClipControl if the driver supports it."),
|
|
ECVF_RenderThreadSafe | ECVF_ReadOnly
|
|
);
|
|
}
|
|
|
|
void InitDefaultGLContextState(void)
|
|
{
|
|
// NOTE: This function can be called before capabilities setup, so extensions need to be checked directly
|
|
FString ExtensionsString;
|
|
GetExtensionsString(ExtensionsString);
|
|
|
|
// Intel HD4000 under <= 10.8.4 requires GL_DITHER disabled or dithering will occur on any channel < 8bits.
|
|
// No other driver does this but we don't need GL_DITHER on anyway.
|
|
glDisable(GL_DITHER);
|
|
|
|
if (FOpenGL::SupportsFramebufferSRGBEnable())
|
|
{
|
|
// Render targets with TexCreate_SRGB should do sRGB conversion like in D3D11
|
|
glEnable(GL_FRAMEBUFFER_SRGB);
|
|
}
|
|
|
|
// Engine always expects seamless cubemap, so enable it if available
|
|
if (ExtensionsString.Contains(TEXT("GL_ARB_seamless_cube_map")))
|
|
{
|
|
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
|
|
}
|
|
|
|
#if PLATFORM_WINDOWS || PLATFORM_LINUX
|
|
if (OpenGLConsoleVariables::bUseGlClipControlIfAvailable && ExtensionsString.Contains(TEXT("GL_ARB_clip_control")) && !FOpenGL::IsAndroidGLESCompatibilityModeEnabled())
|
|
{
|
|
FOpenGL::EnableSupportsClipControl();
|
|
glClipControl(GL_UPPER_LEFT, GL_ZERO_TO_ONE);
|
|
}
|
|
#endif
|
|
|
|
// optional per platform setup
|
|
FOpenGL::SetupDefaultGLContextState(ExtensionsString);
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|