Files
UnrealEngine/Engine/Source/Runtime/OpenGLDrv/Private/OpenGLDevice.cpp
2025-05-18 13:04:45 +08:00

1666 lines
60 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
/*=============================================================================
OpenGLDevice.cpp: OpenGL device RHI implementation.
=============================================================================*/
#include "CoreMinimal.h"
#include "Misc/CoreDelegates.h"
#include "Misc/CommandLine.h"
#include "Misc/ScopeLock.h"
#include "Stats/Stats.h"
#include "Serialization/MemoryWriter.h"
#include "Misc/ConfigCacheIni.h"
#include "HAL/IConsoleManager.h"
#include "RHI.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "Containers/List.h"
#include "RenderResource.h"
#include "ShaderCore.h"
#include "Shader.h"
#include "RenderUtils.h"
#include "OpenGLDrv.h"
#include "OpenGLDrvPrivate.h"
#include "SceneUtils.h"
#include "GlobalShader.h"
#include "HardwareInfo.h"
#include "OpenGLProgramBinaryFileCache.h"
#if PLATFORM_ANDROID
#include <jni.h>
extern bool AndroidThunkCpp_IsOculusMobileApplication();
#endif
#ifndef GL_STEREO
#define GL_STEREO 0x0C33
#endif
extern GLint GMaxOpenGLColorSamples;
extern GLint GMaxOpenGLDepthSamples;
extern GLint GMaxOpenGLIntegerSamples;
extern GLint GMaxOpenGLTextureFilterAnisotropic;
extern GLint GMaxOpenGLDrawBuffers;
/** OpenGL texture format table. */
FOpenGLTextureFormat GOpenGLTextureFormats[PF_MAX];
/** Device is necessary for vertex buffers, so they can reach the global device on destruction, and tell it to reset vertex array caches */
static FOpenGLDynamicRHI* PrivateOpenGLDevicePtr = NULL;
/** true if we're not using UBOs. */
bool GUseEmulatedUniformBuffers;
static TAutoConsoleVariable<int32> CVarAllowRGLHIThread(
TEXT("r.OpenGL.AllowRHIThread"),
1,
TEXT("Toggle OpenGL RHI thread support.\n")
TEXT("0: GL scene rendering operations are performed on the render thread.\n")
TEXT("1: GL scene rendering operations are queued onto the RHI thread gaining some parallelism with the render thread. (default, mobile feature levels only)"),
ECVF_RenderThreadSafe | ECVF_ReadOnly);
static TAutoConsoleVariable<int32> CVarGLExtraDeletionLatency(
TEXT("r.OpenGL.ExtraDeletionLatency"),
1,
TEXT("Toggle the engine's deferred deletion queue for RHI resources. (default:1)"),
ECVF_RenderThreadSafe | ECVF_ReadOnly);
static TAutoConsoleVariable<int32> CVarGLDepth24Bit(
TEXT("r.OpenGL.Depth24Bit"),
1,
TEXT("0: Use 32-bit float depth buffer \n1: Use 24-bit fixed point depth buffer (default)\n"),
ECVF_RenderThreadSafe | ECVF_ReadOnly);
// If precaching is active we should not need the file cache.
// however, precaching and filecache are compatible with each other, there maybe some scenarios in which both could be used.
static TAutoConsoleVariable<bool> CVarEnablePSOFileCacheWhenPrecachingActive(
TEXT("r.OpenGL.EnablePSOFileCacheWhenPrecachingActive"),
false,
TEXT("false: If precaching is active (r.PSOPrecaching=1) then disable the PSO filecache. (default)\n")
TEXT("true: GL RHI Allows both PSO file cache and precaching."),
ECVF_RenderThreadSafe | ECVF_ReadOnly);
static TAutoConsoleVariable<bool> CVarAllowPSOPrecaching(
TEXT("r.OpenGL.AllowPSOPrecaching"),
true,
TEXT("true: if r.PSOPrecaching=1 GL RHI will use precaching. (default)\n")
TEXT("false: GL RHI will disable precaching (even if r.PSOPrecaching=1). "),
ECVF_RenderThreadSafe | ECVF_ReadOnly);
extern void BeginFrame_UniformBufferPoolCleanup();
extern void BeginFrame_VertexBufferCleanup();
void FOpenGLDynamicRHI::RHIEndFrame(const FRHIEndFrameArgs& Args)
{
#if RHI_NEW_GPU_PROFILER
Profiler.EndFrame(Args);
#else
GPUProfilingData->EndFrame();
#endif
KickHint.Reset();
#if PLATFORM_ANDROID //adding #if since not sure if this is required for any other platform.
PendingState.DepthStencil = 0;
#endif
BeginFrame_UniformBufferPoolCleanup();
BeginFrame_VertexBufferCleanup();
#if (RHI_NEW_GPU_PROFILER == 0)
GPUProfilingData->BeginFrame();
#endif
}
#if PLATFORM_ANDROID
JNI_METHOD void Java_com_epicgames_unreal_BitmapRendererLegacy_nativeClearCachedAttributeState(JNIEnv* jenv, jobject thiz, jint PositionAttrib, jint TexCoordsAttrib)
{
FOpenGLDynamicRHI::Get().ClearCachedAttributeState(PositionAttrib, TexCoordsAttrib);
}
#endif
void FOpenGLDynamicRHI::ClearCachedAttributeState(int32 PositionAttrib, int32 TexCoordsAttrib)
{
// update vertex attributes state
ContextState.SetVertexAttrEnabled(PositionAttrib, false);
ContextState.VertexAttrs[PositionAttrib].Size = -1; // will force attribute update
ContextState.SetVertexAttrEnabled(TexCoordsAttrib, false);
ContextState.VertexAttrs[TexCoordsAttrib].Size = -1; // will force attribute update
// make sure the texture is set again
ContextState.ActiveTexture = 0;
ContextState.Textures[0].Texture = nullptr;
ContextState.Textures[0].Target = 0;
// restore previous program
FOpenGL::BindProgramPipeline(ContextState.Program);
}
bool GDisableOpenGLDebugOutput = false;
#if defined(GL_ARB_debug_output) || defined(GL_KHR_debug)
/**
* Map GL_DEBUG_SOURCE_*_ARB to a human-readable string.
*/
static const TCHAR* GetOpenGLDebugSourceStringARB(GLenum Source)
{
static const TCHAR* SourceStrings[] =
{
TEXT("API"),
TEXT("System"),
TEXT("ShaderCompiler"),
TEXT("ThirdParty"),
TEXT("Application"),
TEXT("Other")
};
if (Source >= GL_DEBUG_SOURCE_API_ARB && Source <= GL_DEBUG_SOURCE_OTHER_ARB)
{
return SourceStrings[Source - GL_DEBUG_SOURCE_API_ARB];
}
return TEXT("Unknown");
}
/**
* Map GL_DEBUG_TYPE_*_ARB to a human-readable string.
*/
static const TCHAR* GetOpenGLDebugTypeStringARB(GLenum Type)
{
static const TCHAR* TypeStrings[] =
{
TEXT("Error"),
TEXT("Deprecated"),
TEXT("UndefinedBehavior"),
TEXT("Portability"),
TEXT("Performance"),
TEXT("Other")
};
if (Type >= GL_DEBUG_TYPE_ERROR_ARB && Type <= GL_DEBUG_TYPE_OTHER_ARB)
{
return TypeStrings[Type - GL_DEBUG_TYPE_ERROR_ARB];
}
#ifdef GL_KHR_debug
{
static const TCHAR* DebugTypeStrings[] =
{
TEXT("Marker"),
TEXT("PushGroup"),
TEXT("PopGroup"),
};
if (Type >= GL_DEBUG_TYPE_MARKER && Type <= GL_DEBUG_TYPE_POP_GROUP)
{
return DebugTypeStrings[Type - GL_DEBUG_TYPE_MARKER];
}
}
#endif
return TEXT("Unknown");
}
/**
* Map GL_DEBUG_SEVERITY_*_ARB to a human-readable string.
*/
static const TCHAR* GetOpenGLDebugSeverityStringARB(GLenum Severity)
{
static const TCHAR* SeverityStrings[] =
{
TEXT("High"),
TEXT("Medium"),
TEXT("Low")
};
if (Severity >= GL_DEBUG_SEVERITY_HIGH_ARB && Severity <= GL_DEBUG_SEVERITY_LOW_ARB)
{
return SeverityStrings[Severity - GL_DEBUG_SEVERITY_HIGH_ARB];
}
#ifdef GL_KHR_debug
if(Severity == GL_DEBUG_SEVERITY_NOTIFICATION)
return TEXT("Notification");
#endif
return TEXT("Unknown");
}
/**
* OpenGL debug message callback. Conforms to GLDEBUGPROCARB.
*/
#if PLATFORM_ANDROID
#ifndef GL_APIENTRY
#define GL_APIENTRY APIENTRY
#endif
#ifndef GL_DEBUG_SEVERITY_MEDIUM_ARB
#define GL_DEBUG_SEVERITY_MEDIUM_ARB GL_DEBUG_SEVERITY_LOW_ARB
#endif
static void GL_APIENTRY OpenGLDebugMessageCallbackARB(
#else
static void APIENTRY OpenGLDebugMessageCallbackARB(
#endif
GLenum Source,
GLenum Type,
GLuint Id,
GLenum Severity,
GLsizei Length,
const GLchar* Message,
GLvoid* UserParam)
{
if (GDisableOpenGLDebugOutput)
return;
#if !NO_LOGGING
const TCHAR* SourceStr = GetOpenGLDebugSourceStringARB(Source);
const TCHAR* TypeStr = GetOpenGLDebugTypeStringARB(Type);
const TCHAR* SeverityStr = GetOpenGLDebugSeverityStringARB(Severity);
ELogVerbosity::Type Verbosity = ELogVerbosity::Warning;
if (Type == GL_DEBUG_TYPE_ERROR_ARB && Severity == GL_DEBUG_SEVERITY_HIGH_ARB)
{
Verbosity = ELogVerbosity::Error;
}
if ((Verbosity & ELogVerbosity::VerbosityMask) <= FLogCategoryLogRHI::CompileTimeVerbosity)
{
if (!LogRHI.IsSuppressed(Verbosity))
{
FMsg::Logf(__FILE__, __LINE__, LogRHI.GetCategoryName(), Verbosity,
TEXT("GL_DBG: [%s][%s][%s][%u] %s"),
SourceStr,
TypeStr,
SeverityStr,
Id,
ANSI_TO_TCHAR(Message)
);
}
// this is a debugging code to catch VIDEO->HOST copying
if (Id == 131186)
{
int A = 5;
}
}
#endif
}
#endif // GL_ARB_debug_output
#ifdef GL_AMD_debug_output
/**
* Map GL_DEBUG_CATEGORY_*_AMD to a human-readable string.
*/
static const TCHAR* GetOpenGLDebugCategoryStringAMD(GLenum Category)
{
static const TCHAR* CategoryStrings[] =
{
TEXT("API"),
TEXT("System"),
TEXT("Deprecation"),
TEXT("UndefinedBehavior"),
TEXT("Performance"),
TEXT("ShaderCompiler"),
TEXT("Application"),
TEXT("Other")
};
if (Category >= GL_DEBUG_CATEGORY_API_ERROR_AMD && Category <= GL_DEBUG_CATEGORY_OTHER_AMD)
{
return CategoryStrings[Category - GL_DEBUG_CATEGORY_API_ERROR_AMD];
}
return TEXT("Unknown");
}
/**
* Map GL_DEBUG_SEVERITY_*_AMD to a human-readable string.
*/
static const TCHAR* GetOpenGLDebugSeverityStringAMD(GLenum Severity)
{
static const TCHAR* SeverityStrings[] =
{
TEXT("High"),
TEXT("Medium"),
TEXT("Low")
};
if (Severity >= GL_DEBUG_SEVERITY_HIGH_AMD && Severity <= GL_DEBUG_SEVERITY_LOW_AMD)
{
return SeverityStrings[Severity - GL_DEBUG_SEVERITY_HIGH_AMD];
}
return TEXT("Unknown");
}
/**
* OpenGL debug message callback. Conforms to GLDEBUGPROCAMD.
*/
static void APIENTRY OpenGLDebugMessageCallbackAMD(
GLuint Id,
GLenum Category,
GLenum Severity,
GLsizei Length,
const GLchar* Message,
GLvoid* UserParam)
{
#if !NO_LOGGING
const TCHAR* CategoryStr = GetOpenGLDebugCategoryStringAMD(Category);
const TCHAR* SeverityStr = GetOpenGLDebugSeverityStringAMD(Severity);
ELogVerbosity::Type Verbosity = ELogVerbosity::Warning;
if (Severity == GL_DEBUG_SEVERITY_HIGH_AMD)
{
Verbosity = ELogVerbosity::Fatal;
}
if ((Verbosity & ELogVerbosity::VerbosityMask) <= FLogCategoryLogRHI::CompileTimeVerbosity)
{
if (!LogRHI.IsSuppressed(Verbosity))
{
FMsg::Logf(__FILE__, __LINE__, LogRHI.GetCategoryName(), Verbosity,
TEXT("[%s][%s][%u] %s"),
CategoryStr,
SeverityStr,
Id,
ANSI_TO_TCHAR(Message)
);
}
}
#endif
}
#endif // GL_AMD_debug_output
#if PLATFORM_WINDOWS
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT_ProcAddress = NULL;
#endif
// Information from OpenGLES specification 3.2 Table 8.10
// Qualcomm Adreno OpenGLES Developer Guide 4.1.4 section and Table 9-1
enum class EOpenGLFormatCapabilities : uint32
{
None = 0,
Texture = 1ull << 0,
Render = 1ull << 1,
Filterable = 1ull << 2,
Image = 1ull << 3,
DepthStencil = 1ull << 4,
};
ENUM_CLASS_FLAGS(EOpenGLFormatCapabilities);
static EOpenGLFormatCapabilities GetOpenGLFormatCapabilities(const FOpenGLTextureFormat& GLFormat)
{
EOpenGLFormatCapabilities Capabilities = EOpenGLFormatCapabilities::None;
switch (GLFormat.InternalFormat[0])
{
default: checkNoEntry();
case GL_NONE:
break;
case GL_R8:
case GL_RG8:
case GL_RGB8:
case GL_RGBA4:
case GL_RGB10_A2:
case GL_SRGB8_ALPHA8:
case GL_R16F:
case GL_RG16F:
case GL_RGBA16:
case GL_RGB565:
case GL_RGB5_A1:
case GL_R11F_G11F_B10F:
Capabilities |= EOpenGLFormatCapabilities::Texture | EOpenGLFormatCapabilities::Render | EOpenGLFormatCapabilities::Filterable;
break;
case GL_RGBA8:
case GL_RGBA16F:
Capabilities |= EOpenGLFormatCapabilities::Texture | EOpenGLFormatCapabilities::Render | EOpenGLFormatCapabilities::Filterable | EOpenGLFormatCapabilities::Image;
break;
case GL_R16:
case GL_RGB10_A2UI:
case GL_RG32F:
case GL_R8I:
case GL_R8UI:
case GL_R16I:
case GL_R16UI:
case GL_RG8I:
case GL_RG8UI:
case GL_RG16I:
case GL_RG16UI:
case GL_RG32I:
case GL_RG32UI:
Capabilities |= EOpenGLFormatCapabilities::Texture | EOpenGLFormatCapabilities::Render;
break;
case GL_R32I:
case GL_R32F:
case GL_R32UI:
case GL_RGBA8I:
case GL_RGBA8UI:
case GL_RGBA16I:
case GL_RGBA16UI:
case GL_RGBA32I:
case GL_RGBA32F:
case GL_RGBA32UI:
Capabilities |= EOpenGLFormatCapabilities::Texture | EOpenGLFormatCapabilities::Render | EOpenGLFormatCapabilities::Image;
break;
case GL_RGB32F:
case GL_RGB8I:
case GL_RGB8UI:
case GL_RGB16I:
case GL_RGB16UI:
case GL_RGB32I:
case GL_RGB32UI:
Capabilities |= EOpenGLFormatCapabilities::Texture;
break;
case GL_DEPTH24_STENCIL8:
case GL_DEPTH32F_STENCIL8:
case GL_DEPTH_COMPONENT16:
case GL_DEPTH_COMPONENT24:
Capabilities |= EOpenGLFormatCapabilities::Texture | EOpenGLFormatCapabilities::Render | EOpenGLFormatCapabilities::Filterable | EOpenGLFormatCapabilities::DepthStencil;
break;
#if PLATFORM_ANDROID
case GL_COMPRESSED_RGB8_ETC2:
case GL_COMPRESSED_RGBA8_ETC2_EAC:
case GL_COMPRESSED_R11_EAC:
case GL_COMPRESSED_RG11_EAC:
#endif
#if PLATFORM_DESKTOP
case GL_COMPRESSED_RG_RGTC2:
case GL_COMPRESSED_RED_RGTC1:
case GL_RG16:
case GL_RG16_SNORM:
case GL_COMPRESSED_RGBA_BPTC_UNORM:
#endif
case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:
case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:
case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:
case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:
case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:
case GL_R8_SNORM:
case GL_RG8_SNORM:
case GL_RGB8_SNORM:
case GL_RGB9_E5:
case GL_RGB16F:
Capabilities |= EOpenGLFormatCapabilities::Texture | EOpenGLFormatCapabilities::Filterable;
break;
case GL_RGBA8_SNORM:
Capabilities |= EOpenGLFormatCapabilities::Texture | EOpenGLFormatCapabilities::Filterable | EOpenGLFormatCapabilities::Image;
break;
}
return Capabilities;
}
static inline void SetupTextureFormat( EPixelFormat Format, const FOpenGLTextureFormat& GLFormat)
{
GOpenGLTextureFormats[Format] = GLFormat;
GPixelFormats[Format].Supported = (GLFormat.Format != GL_NONE && (GLFormat.InternalFormat[0] != GL_NONE || GLFormat.InternalFormat[1] != GL_NONE));
GPixelFormats[Format].PlatformFormat = GLFormat.InternalFormat[0];
if (GPixelFormats[Format].Supported)
{
EPixelFormatCapabilities& Capabilities = GPixelFormats[Format].Capabilities;
EOpenGLFormatCapabilities OpenGLCapabilities = GetOpenGLFormatCapabilities(GLFormat);
if (EnumHasAllFlags(OpenGLCapabilities, EOpenGLFormatCapabilities::Texture))
{
EnumAddFlags(Capabilities, EPixelFormatCapabilities::Texture1D);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::Texture2D);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::Texture3D);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::TextureCube);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::TextureMipmaps);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::TextureLoad);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::TextureSample);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::TextureGather);
}
if (EnumHasAllFlags(OpenGLCapabilities, EOpenGLFormatCapabilities::Filterable))
{
EnumAddFlags(Capabilities, EPixelFormatCapabilities::TextureFilterable);
}
if (EnumHasAllFlags(OpenGLCapabilities, EOpenGLFormatCapabilities::Render))
{
EnumAddFlags(Capabilities, EPixelFormatCapabilities::RenderTarget);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::TextureBlendable);
}
if (EnumHasAllFlags(OpenGLCapabilities, EOpenGLFormatCapabilities::DepthStencil))
{
EnumAddFlags(Capabilities, EPixelFormatCapabilities::DepthStencil);
}
if (EnumHasAllFlags(OpenGLCapabilities, EOpenGLFormatCapabilities::Image))
{
EnumAddFlags(Capabilities, EPixelFormatCapabilities::TextureAtomics);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::TextureStore);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::Buffer);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::VertexBuffer);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::IndexBuffer);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::BufferLoad);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::BufferStore);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::BufferAtomics);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::UAV);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::TypedUAVLoad);
EnumAddFlags(Capabilities, EPixelFormatCapabilities::TypedUAVStore);
}
}
}
void InitDebugContext()
{
// Set the debug output callback if the driver supports it.
VERIFY_GL(__FUNCTION__);
bool bDebugOutputInitialized = false;
if (!IsOGLDebugOutputEnabled())
{
return;
}
#if ENABLE_DEBUG_OUTPUT
#if defined(GL_ARB_debug_output)
if (glDebugMessageCallbackARB)
{
// Synchronous output can slow things down, but we'll get better callstack if breaking in or crashing in the callback. This is debug only after all.
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glDebugMessageCallbackARB(GLDEBUGPROCARB(OpenGLDebugMessageCallbackARB), /*UserParam=*/ NULL);
bDebugOutputInitialized = (glGetError() == GL_NO_ERROR);
}
#elif defined(GL_KHR_debug)
// OpenGLES names the debug functions differently, but they behave the same
if (glDebugMessageCallbackKHR)
{
glDebugMessageCallbackKHR(GLDEBUGPROCKHR(OpenGLDebugMessageCallbackARB), /*UserParam=*/ NULL);
bDebugOutputInitialized = (glGetError() == GL_NO_ERROR);
}
#endif // GL_ARB_debug_output / GL_KHR_debug
#if defined(GL_AMD_debug_output)
if (glDebugMessageCallbackAMD && !bDebugOutputInitialized)
{
glDebugMessageCallbackAMD(OpenGLDebugMessageCallbackAMD, /*UserParam=*/ NULL);
bDebugOutputInitialized = (glGetError() == GL_NO_ERROR);
}
#endif // GL_AMD_debug_output
#endif // ENABLE_DEBUG_OUTPUT
if (!bDebugOutputInitialized)
{
UE_LOG(LogRHI,Warning,TEXT("OpenGL debug output extension not supported!"));
}
else
{
UE_LOG(LogRHI, Log, TEXT("OpenGL debug output extension enabled!"));
}
// this is to suppress feeding back of the debug markers and groups to the log, since those originate in the app anyways...
#if ENABLE_OPENGL_DEBUG_GROUPS && defined(GL_ARB_debug_output) && GL_ARB_debug_output && GL_KHR_debug
if(glDebugMessageControlARB && bDebugOutputInitialized)
{
glDebugMessageControlARB(GL_DEBUG_SOURCE_APPLICATION_ARB, GL_DEBUG_TYPE_MARKER, GL_DONT_CARE, 0, NULL, GL_FALSE);
glDebugMessageControlARB(GL_DEBUG_SOURCE_APPLICATION_ARB, GL_DEBUG_TYPE_PUSH_GROUP, GL_DONT_CARE, 0, NULL, GL_FALSE);
glDebugMessageControlARB(GL_DEBUG_SOURCE_APPLICATION_ARB, GL_DEBUG_TYPE_POP_GROUP, GL_DONT_CARE, 0, NULL, GL_FALSE);
#ifdef GL_KHR_debug
glDebugMessageControlARB(GL_DEBUG_SOURCE_API_ARB, GL_DEBUG_TYPE_OTHER_ARB, GL_DEBUG_SEVERITY_NOTIFICATION, 0, NULL, GL_FALSE);
#endif
UE_LOG(LogRHI,Verbose,TEXT("disabling reporting back of debug groups and markers to the OpenGL debug output callback"));
}
#elif ENABLE_OPENGL_DEBUG_GROUPS && !defined(GL_ARB_debug_output) && defined(GL_KHR_debug) && GL_KHR_debug
if(glDebugMessageControlKHR)
{
glDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, GL_DEBUG_TYPE_MARKER_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE);
glDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, GL_DEBUG_TYPE_PUSH_GROUP_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE);
glDebugMessageControlKHR(GL_DEBUG_SOURCE_APPLICATION_KHR, GL_DEBUG_TYPE_POP_GROUP_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE);
glDebugMessageControlKHR(GL_DEBUG_SOURCE_API_KHR, GL_DEBUG_TYPE_OTHER_KHR, GL_DEBUG_SEVERITY_NOTIFICATION, 0, NULL, GL_FALSE);
GLenum Severity;
const bool bAllowPerformanceMessages = GetOGLDebugOutputLevel() >= 5;
switch (GetOGLDebugOutputLevel())
{
case 5: Severity = GL_DONT_CARE; break;
case 4: Severity = GL_DEBUG_SEVERITY_NOTIFICATION; break;
case 3: Severity = GL_DEBUG_SEVERITY_LOW_ARB; break;
case 2: Severity = GL_DEBUG_SEVERITY_MEDIUM_ARB; break;
case 1:
[[fallthrough]];
default: Severity = GL_DEBUG_SEVERITY_HIGH_ARB; break;
}
glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, Severity, 0, NULL, GL_TRUE);
if( !bAllowPerformanceMessages)
{
glDebugMessageControlKHR(GL_DONT_CARE, GL_DEBUG_TYPE_PERFORMANCE_KHR, GL_DONT_CARE, 0, NULL, GL_FALSE);
}
UE_LOG(LogRHI,Verbose,TEXT("disabling reporting back of debug groups and markers to the OpenGL debug output callback"));
}
#endif
}
TAutoConsoleVariable<FString> CVarOpenGLStripExtensions(
TEXT("r.OpenGL.StripExtensions"),
TEXT(""),
TEXT("List of comma separated OpenGL extensions to strip from a driver reported extensions string"),
ECVF_ReadOnly);
TAutoConsoleVariable<FString> CVarOpenGLAddExtensions(
TEXT("r.OpenGL.AddExtensions"),
TEXT(""),
TEXT("List of comma separated OpenGL extensions to add to a driver reported extensions string"),
ECVF_ReadOnly);
void ApplyExtensionsOverrides(FString& ExtensionsString)
{
// Strip extensions
{
TArray<FString> ExtList;
FString ExtString = CVarOpenGLStripExtensions.GetValueOnAnyThread();
ExtString.ParseIntoArray(ExtList, TEXT(","), /*InCullEmpty=*/true);
for (FString& ExtName : ExtList)
{
ExtName.TrimStartAndEndInline();
if (ExtensionsString.ReplaceInline(*ExtName, TEXT("")) > 0)
{
UE_LOG(LogRHI, Log, TEXT("Stripped extension: %s"), *ExtName);
}
}
}
// Add extensions
{
TArray<FString> ExtList;
FString ExtString = CVarOpenGLAddExtensions.GetValueOnAnyThread();
ExtString.ParseIntoArray(ExtList, TEXT(","), /*InCullEmpty=*/true);
for (FString& ExtName : ExtList)
{
ExtName.TrimStartAndEndInline();
if (!ExtensionsString.Contains(ExtName))
{
ExtensionsString.Append(TEXT(" ")); // extensions delimiter
ExtensionsString.Append(ExtName);
UE_LOG(LogRHI, Log, TEXT("Added extension: %s"), *ExtName);
}
}
}
}
/**
* Initialize RHI capabilities for the current OpenGL context.
*/
static void InitRHICapabilitiesForGL()
{
VERIFY_GL_SCOPE();
GTexturePoolSize = 0;
GPoolSizeVRAMPercentage = 0;
#if PLATFORM_WINDOWS || PLATFORM_LINUX
GConfig->GetInt( TEXT( "TextureStreaming" ), TEXT( "PoolSizeVRAMPercentage" ), GPoolSizeVRAMPercentage, GEngineIni );
#endif
// GL vendor and version information.
#if !defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !(defined(_MSC_VER) && _MSC_VER >= 1900)
#define LOG_GL_STRING(StringEnum) UE_LOG(LogRHI, Log, TEXT(" ") ## TEXT(#StringEnum) ## TEXT(": %s"), ANSI_TO_TCHAR((const ANSICHAR*)glGetString(StringEnum)))
#else
#define LOG_GL_STRING(StringEnum) UE_LOG(LogRHI, Log, TEXT(" " #StringEnum ": %s"), ANSI_TO_TCHAR((const ANSICHAR*)glGetString(StringEnum)))
#endif
UE_LOG(LogRHI, Log, TEXT("Initializing OpenGL RHI"));
LOG_GL_STRING(GL_VENDOR);
LOG_GL_STRING(GL_RENDERER);
LOG_GL_STRING(GL_VERSION);
LOG_GL_STRING(GL_SHADING_LANGUAGE_VERSION);
#undef LOG_GL_STRING
GRHIAdapterName = FOpenGL::GetAdapterName();
GRHIAdapterInternalDriverVersion = ANSI_TO_TCHAR((const ANSICHAR*)glGetString(GL_VERSION));
// Shader platform & RHI feature level
GMaxRHIFeatureLevel = ERHIFeatureLevel::ES3_1;
GMaxRHIShaderPlatform = FOpenGL::GetShaderPlatform();
GRHIMaximumInFlightQueries = 4000;
// Log all supported extensions.
#if PLATFORM_WINDOWS
bool bWindowsSwapControlExtensionPresent = false;
#endif
{
extern void GetExtensionsString( FString& ExtensionsString);
FString ExtensionsString;
GetExtensionsString(ExtensionsString);
#if PLATFORM_WINDOWS
if (ExtensionsString.Contains(TEXT("WGL_EXT_swap_control")))
{
bWindowsSwapControlExtensionPresent = true;
}
#endif
// Log supported GL extensions
UE_LOG(LogRHI, Log, TEXT("OpenGL Extensions:"));
TArray<FString> GLExtensionArray;
ExtensionsString.ParseIntoArray(GLExtensionArray, TEXT(" "), true);
for (int ExtIndex = 0; ExtIndex < GLExtensionArray.Num(); ExtIndex++)
{
UE_LOG(LogRHI, Log, TEXT(" %s"), *GLExtensionArray[ExtIndex]);
}
ApplyExtensionsOverrides(ExtensionsString);
FOpenGL::ProcessExtensions(ExtensionsString);
}
#if PLATFORM_WINDOWS
#pragma warning(push)
#pragma warning(disable:4191)
if (!bWindowsSwapControlExtensionPresent)
{
// Disable warning C4191: 'type cast' : unsafe conversion from 'PROC' to 'XXX' while getting GL entry points.
PFNWGLGETEXTENSIONSSTRINGEXTPROC wglGetExtensionsStringEXT_ProcAddress =
(PFNWGLGETEXTENSIONSSTRINGEXTPROC) wglGetProcAddress("wglGetExtensionsStringEXT");
if (strstr(wglGetExtensionsStringEXT_ProcAddress(), "WGL_EXT_swap_control") != NULL)
{
bWindowsSwapControlExtensionPresent = true;
}
}
if (bWindowsSwapControlExtensionPresent)
{
wglSwapIntervalEXT_ProcAddress = (PFNWGLSWAPINTERVALEXTPROC) wglGetProcAddress("wglSwapIntervalEXT");
}
#pragma warning(pop)
#endif
// Set debug flag if context was setup with debugging
FOpenGL::InitDebugContext();
// Log and get various limits.
#if !defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER) && !(defined(_MSC_VER) && _MSC_VER >= 1900)
#define LOG_AND_GET_GL_INT_TEMP(IntEnum,Default) GLint Value_##IntEnum = Default; if (IntEnum) {glGetIntegerv(IntEnum, &Value_##IntEnum); glGetError();} else {Value_##IntEnum = Default;} UE_LOG(LogRHI, Log, TEXT(" ") ## TEXT(#IntEnum) ## TEXT(": %d"), Value_##IntEnum)
#else
#define LOG_AND_GET_GL_INT_TEMP(IntEnum,Default) GLint Value_##IntEnum = Default; if (IntEnum) {glGetIntegerv(IntEnum, &Value_##IntEnum); glGetError();} else {Value_##IntEnum = Default;} UE_LOG(LogRHI, Log, TEXT(" " #IntEnum ": %d"), Value_##IntEnum)
#endif
LOG_AND_GET_GL_INT_TEMP(GL_MAX_TEXTURE_SIZE, 0);
#if defined(GL_MAX_TEXTURE_BUFFER_SIZE)
LOG_AND_GET_GL_INT_TEMP(GL_MAX_TEXTURE_BUFFER_SIZE, 0);
#endif
LOG_AND_GET_GL_INT_TEMP(GL_MAX_CUBE_MAP_TEXTURE_SIZE, 0);
#if defined(GL_MAX_ARRAY_TEXTURE_LAYERS) && GL_MAX_ARRAY_TEXTURE_LAYERS
LOG_AND_GET_GL_INT_TEMP(GL_MAX_ARRAY_TEXTURE_LAYERS, 0);
#endif
#if GL_MAX_3D_TEXTURE_SIZE
LOG_AND_GET_GL_INT_TEMP(GL_MAX_3D_TEXTURE_SIZE, 0);
#endif
LOG_AND_GET_GL_INT_TEMP(GL_MAX_RENDERBUFFER_SIZE, 0);
LOG_AND_GET_GL_INT_TEMP(GL_MAX_TEXTURE_IMAGE_UNITS, 0);
LOG_AND_GET_GL_INT_TEMP(GL_MAX_DRAW_BUFFERS, 1);
GMaxOpenGLDrawBuffers = FMath::Min(Value_GL_MAX_DRAW_BUFFERS, (GLint)MaxSimultaneousRenderTargets);
LOG_AND_GET_GL_INT_TEMP(GL_MAX_COLOR_ATTACHMENTS, 1);
LOG_AND_GET_GL_INT_TEMP(GL_MAX_SAMPLES, 1);
LOG_AND_GET_GL_INT_TEMP(GL_MAX_COLOR_TEXTURE_SAMPLES, 1);
LOG_AND_GET_GL_INT_TEMP(GL_MAX_DEPTH_TEXTURE_SAMPLES, 1);
LOG_AND_GET_GL_INT_TEMP(GL_MAX_INTEGER_SAMPLES, 1);
LOG_AND_GET_GL_INT_TEMP(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, 0);
LOG_AND_GET_GL_INT_TEMP(GL_MAX_VERTEX_ATTRIBS, 0);
LOG_AND_GET_GL_INT_TEMP(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, 0);
LOG_AND_GET_GL_INT_TEMP(GL_MAX_COMPUTE_SHARED_MEMORY_SIZE, 0);
if (FParse::Param(FCommandLine::Get(), TEXT("quad_buffer_stereo")))
{
GLboolean Result = GL_FALSE;
glGetBooleanv(GL_STEREO, &Result);
// Skip any errors if any were generated
glGetError();
GSupportsQuadBufferStereo = (Result == GL_TRUE);
}
if (FOpenGL::SupportsTextureFilterAnisotropic())
{
LOG_AND_GET_GL_INT_TEMP(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, 0);
GMaxOpenGLTextureFilterAnisotropic = Value_GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT;
}
if (FOpenGL::SupportsPixelLocalStorage())
{
LOG_AND_GET_GL_INT_TEMP(GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_SIZE_EXT, 0);
}
#undef LOG_AND_GET_GL_INT_TEMP
GMaxOpenGLColorSamples = Value_GL_MAX_COLOR_TEXTURE_SAMPLES;
GMaxOpenGLDepthSamples = Value_GL_MAX_DEPTH_TEXTURE_SAMPLES;
GMaxOpenGLIntegerSamples = Value_GL_MAX_INTEGER_SAMPLES;
// Verify some assumptions.
// Android seems like reports one color attachment even when it supports MRT
#if !PLATFORM_ANDROID
check(Value_GL_MAX_COLOR_ATTACHMENTS >= MaxSimultaneousRenderTargets);
#endif
// We don't check for compressed formats right now because vendors have not
// done a great job reporting what is actually supported:
// OSX/NVIDIA doesn't claim to support SRGB DXT formats even though they work correctly.
// Windows/AMD sometimes claim to support no compressed formats even though they all work correctly.
#if 0
// Check compressed texture formats.
bool bSupportsCompressedTextures = true;
{
FString CompressedFormatsString = TEXT(" GL_COMPRESSED_TEXTURE_FORMATS:");
TArray<GLint> CompressedFormats;
GLint NumCompressedFormats = 0;
glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &NumCompressedFormats);
CompressedFormats.Empty(NumCompressedFormats);
CompressedFormats.AddZeroed(NumCompressedFormats);
glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, CompressedFormats.GetTypedData());
#define CHECK_COMPRESSED_FORMAT(GLName) if (CompressedFormats.Contains(GLName)) { CompressedFormatsString += TEXT(" ") TEXT(#GLName); } else { bSupportsCompressedTextures = false; }
CHECK_COMPRESSED_FORMAT(GL_COMPRESSED_RGB_S3TC_DXT1_EXT);
CHECK_COMPRESSED_FORMAT(GL_COMPRESSED_SRGB_S3TC_DXT1_EXT);
CHECK_COMPRESSED_FORMAT(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT);
CHECK_COMPRESSED_FORMAT(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT);
CHECK_COMPRESSED_FORMAT(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT);
CHECK_COMPRESSED_FORMAT(GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT);
//CHECK_COMPRESSED_FORMAT(GL_COMPRESSED_RG_RGTC2);
#undef CHECK_COMPRESSED_FORMAT
// ATI does not report that it supports RGTC2, but the 3.2 core profile mandates it.
// For now assume it is supported and bring it up with ATI if it really isn't(?!)
CompressedFormatsString += TEXT(" GL_COMPRESSED_RG_RGTC2");
UE_LOG(LogRHI, Log, *CompressedFormatsString);
}
check(bSupportsCompressedTextures);
#endif
// Set capabilities.
const GLint MajorVersion = FOpenGL::GetMajorVersion();
const GLint MinorVersion = FOpenGL::GetMinorVersion();
// Enable the OGL rhi thread if explicitly requested.
GRHISupportsRHIThread = (GMaxRHIFeatureLevel <= ERHIFeatureLevel::ES3_1 && CVarAllowRGLHIThread.GetValueOnAnyThread());
GRHISupportsMultithreadedResources = GRHISupportsRHIThread;
// OpenGL ES does not support glTextureView
GRHISupportsTextureViews = false;
// By default use emulated UBs on mobile
GUseEmulatedUniformBuffers = IsUsingEmulatedUniformBuffers(GMaxRHIShaderPlatform);
FString FeatureLevelName;
GetFeatureLevelName(GMaxRHIFeatureLevel, FeatureLevelName);
FString ShaderPlatformName = LegacyShaderPlatformToShaderFormat(GMaxRHIShaderPlatform).ToString();
UE_LOG(LogRHI, Log, TEXT("OpenGL MajorVersion = %d, MinorVersion = %d, ShaderPlatform = %s, FeatureLevel = %s"), MajorVersion, MinorVersion, *ShaderPlatformName, *FeatureLevelName);
#if PLATFORM_ANDROID
UE_LOG(LogRHI, Log, TEXT("PLATFORM_ANDROID"));
#endif
GMaxTextureSamplers = Value_GL_MAX_TEXTURE_IMAGE_UNITS;
GMaxTextureMipCount = FMath::CeilLogTwo(Value_GL_MAX_TEXTURE_SIZE) + 1;
GMaxTextureMipCount = FMath::Min<int32>(MAX_TEXTURE_MIP_COUNT, GMaxTextureMipCount);
GMaxTextureDimensions = Value_GL_MAX_TEXTURE_SIZE;
GRHIGlobals.MaxViewSizeBytesForNonTypedBuffer = 1 << 27;
#if defined(GL_MAX_TEXTURE_BUFFER_SIZE)
GRHIGlobals.MaxViewDimensionForTypedBuffer = Value_GL_MAX_TEXTURE_BUFFER_SIZE;
#else
GRHIGlobals.MaxViewDimensionForTypedBuffer = 1 << 27;
#endif
GMaxCubeTextureDimensions = Value_GL_MAX_CUBE_MAP_TEXTURE_SIZE;
#if defined(GL_MAX_ARRAY_TEXTURE_LAYERS) && GL_MAX_ARRAY_TEXTURE_LAYERS
GMaxTextureArrayLayers = Value_GL_MAX_ARRAY_TEXTURE_LAYERS;
#endif
#if defined(GL_MAX_COMPUTE_SHARED_MEMORY_SIZE)
GMaxComputeSharedMemory = Value_GL_MAX_COMPUTE_SHARED_MEMORY_SIZE;
#endif
GMaxWorkGroupInvocations = Value_GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS;
GSupportsParallelRenderingTasksWithSeparateRHIThread = false; // hopefully this is just temporary
GRHIThreadNeedsKicking = true; // GL is SLOW
GRHISupportsExactOcclusionQueries = FOpenGL::SupportsExactOcclusionQueries();
GSupportsVolumeTextureRendering = FOpenGL::SupportsVolumeTextureRendering();
GSupportsRenderDepthTargetableShaderResources = true;
GSupportsRenderTargetFormat_PF_G8 = true;
GSupportsSeparateRenderTargetBlendState = FOpenGL::SupportsSeparateAlphaBlend();
GSupportsDepthBoundsTest = FOpenGL::SupportsDepthBoundsTest();
GSupportsRenderTargetFormat_PF_FloatRGBA = FOpenGL::SupportsColorBufferHalfFloat();
GSupportsWideMRT = FOpenGL::SupportsWideMRT();
GSupportsTexture3D = FOpenGL::SupportsTexture3D();
GSupportsMobileMultiView = FOpenGL::SupportsMobileMultiView();
GSupportsImageExternal = FOpenGL::SupportsImageExternal();
GSupportsShaderFramebufferFetch = FOpenGL::SupportsShaderFramebufferFetch();
GSupportsShaderMRTFramebufferFetch = FOpenGL::SupportsShaderMRTFramebufferFetch();
GSupportsShaderDepthStencilFetch = FOpenGL::SupportsShaderDepthStencilFetch();
GSupportsPixelLocalStorage = FOpenGL::SupportsPixelLocalStorage();
GSupportsShaderFramebufferFetchProgrammableBlending = FOpenGL::SupportsShaderFramebufferFetchProgrammableBlending();
GMaxShadowDepthBufferSizeX = FMath::Min<int32>(Value_GL_MAX_RENDERBUFFER_SIZE, 4096); // Limit to the D3D11 max.
GMaxShadowDepthBufferSizeY = FMath::Min<int32>(Value_GL_MAX_RENDERBUFFER_SIZE, 4096);
GHardwareHiddenSurfaceRemoval = FOpenGL::HasHardwareHiddenSurfaceRemoval();
GSupportsTimestampRenderQueries = FOpenGL::SupportsTimestampQueries();
GRHIMaxDispatchThreadGroupsPerDimension.X = MAX_uint16;
GRHIMaxDispatchThreadGroupsPerDimension.Y = MAX_uint16;
GRHIMaxDispatchThreadGroupsPerDimension.Z = MAX_uint16;
// It's not possible to create a framebuffer with the backbuffer as the color attachment and a custom renderbuffer as the depth/stencil surface.
GRHISupportsBackBufferWithCustomDepthStencil = false;
GRHISupportsLazyShaderCodeLoading = true;
GShaderPlatformForFeatureLevel[ERHIFeatureLevel::ES2_REMOVED] = SP_NumPlatforms;
GShaderPlatformForFeatureLevel[ERHIFeatureLevel::ES3_1] = (GMaxRHIFeatureLevel == ERHIFeatureLevel::ES3_1) ? GMaxRHIShaderPlatform : SP_OPENGL_PCES3_1;
GShaderPlatformForFeatureLevel[ERHIFeatureLevel::SM4_REMOVED] = SP_NumPlatforms;
GShaderPlatformForFeatureLevel[ERHIFeatureLevel::SM5] = SP_NumPlatforms;
// Not implemented in OpenGL RHI. Supported in GLcore 3.3, not in OpenGL ES, extension GL_EXT_blend_func_extended not widely supported.
GSupportsDualSrcBlending = false;
GRHISupportsTextureStreaming = true;
// Disable texture streaming if forced off by r.OpenGL.DisableTextureStreamingSupport
static const auto CVarDisableOpenGLTextureStreamingSupport = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.OpenGL.DisableTextureStreamingSupport"));
if (CVarDisableOpenGLTextureStreamingSupport->GetValueOnAnyThread())
{
GRHISupportsTextureStreaming = false;
}
for (int32 PF = 0; PF < PF_MAX; ++PF)
{
SetupTextureFormat(EPixelFormat(PF), FOpenGLTextureFormat());
}
GLenum DepthFormat = FOpenGL::GetDepthFormat();
GLenum ShadowDepthFormat = FOpenGL::GetShadowDepthFormat();
// Initialize the platform pixel format map. InternalFormat InternalFormatSRGB Format Type bCompressed bBGRA
SetupTextureFormat( PF_Unknown, FOpenGLTextureFormat( ));
SetupTextureFormat( PF_A32B32G32R32F, FOpenGLTextureFormat( GL_RGBA32F, GL_RGBA32F, GL_RGBA, GL_FLOAT, false, false));
SetupTextureFormat( PF_UYVY, FOpenGLTextureFormat( ));
if (CVarGLDepth24Bit.GetValueOnAnyThread() != 0)
{
SetupTextureFormat(PF_DepthStencil, FOpenGLTextureFormat(GL_DEPTH24_STENCIL8, GL_NONE, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, false, false));
GPixelFormats[PF_DepthStencil].BlockBytes = 4;
GPixelFormats[PF_DepthStencil].bIs24BitUnormDepthStencil = true;
}
else
{
SetupTextureFormat(PF_DepthStencil, FOpenGLTextureFormat(GL_DEPTH32F_STENCIL8, GL_NONE, GL_DEPTH_STENCIL, GL_FLOAT_32_UNSIGNED_INT_24_8_REV, false, false));
GPixelFormats[PF_DepthStencil].BlockBytes = 8;
GPixelFormats[PF_DepthStencil].bIs24BitUnormDepthStencil = false;
}
GPixelFormats[PF_X24_G8].Supported = true;
SetupTextureFormat( PF_ShadowDepth, FOpenGLTextureFormat( ShadowDepthFormat, ShadowDepthFormat, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, false, false));
SetupTextureFormat( PF_D24, FOpenGLTextureFormat( DepthFormat, DepthFormat, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, false, false));
SetupTextureFormat( PF_A16B16G16R16, FOpenGLTextureFormat( GL_RGBA16F, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false, false));
SetupTextureFormat( PF_A1, FOpenGLTextureFormat( ));
SetupTextureFormat( PF_R16G16B16A16_UINT, FOpenGLTextureFormat( GL_RGBA16UI, GL_RGBA16UI, GL_RGBA_INTEGER, GL_UNSIGNED_SHORT, false, false));
SetupTextureFormat( PF_R16G16B16A16_SINT, FOpenGLTextureFormat( GL_RGBA16I, GL_RGBA16I, GL_RGBA_INTEGER, GL_SHORT, false, false));
SetupTextureFormat( PF_R32G32B32A32_UINT, FOpenGLTextureFormat( GL_RGBA32UI, GL_RGBA32UI, GL_RGBA_INTEGER, GL_UNSIGNED_INT, false, false));
SetupTextureFormat( PF_R16G16B16A16_UNORM, FOpenGLTextureFormat( GL_RGBA16, GL_RGBA16, GL_RGBA, GL_UNSIGNED_SHORT, false, false));
SetupTextureFormat( PF_R16G16B16A16_SNORM, FOpenGLTextureFormat( GL_RGBA16, GL_RGBA16, GL_RGBA, GL_SHORT, false, false));
SetupTextureFormat( PF_R16G16_UINT, FOpenGLTextureFormat( GL_RG16UI, GL_RG16UI, GL_RG_INTEGER, GL_UNSIGNED_SHORT, false, false));
SetupTextureFormat( PF_R16G16_SINT, FOpenGLTextureFormat( GL_RG16I, GL_RG16I, GL_RG_INTEGER, GL_SHORT, false, false));
SetupTextureFormat( PF_R8, FOpenGLTextureFormat( GL_R8, GL_R8, GL_RED, GL_UNSIGNED_BYTE, false, false));
SetupTextureFormat( PF_R5G6B5_UNORM, FOpenGLTextureFormat( GL_RGB565, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, false, false));
//GL_UNSIGNED_SHORT_1_5_5_5_REV is not defined in gles
GLenum GL5551Format = FOpenGL::GetPlatfrom5551Format();
//the last 5 bits of GL_UNSIGNED_SHORT_1_5_5_5_REV is Red, while the last 5 bits of DXGI_FORMAT_B5G5R5A1_UNORM is Blue
bool bNeedsToSwizzleRedBlue = GL5551Format != GL_UNSIGNED_SHORT_5_5_5_1;
SetupTextureFormat( PF_B5G5R5A1_UNORM, FOpenGLTextureFormat( GL_RGB5_A1, GL_RGB5_A1, GL_RGBA, GL5551Format, false, bNeedsToSwizzleRedBlue));
#if PLATFORM_ANDROID
// Unfortunatelly most OpenGLES devices do not support R16Unorm pixel format, fallback to a less precise R16F
SetupTextureFormat( PF_G16, FOpenGLTextureFormat( GL_R16F, GL_R16F, GL_RED, GL_HALF_FLOAT, false, false));
#else
SetupTextureFormat( PF_G16, FOpenGLTextureFormat( GL_R16, GL_R16, GL_RED, GL_UNSIGNED_SHORT, false, false));
#endif
SetupTextureFormat( PF_R32_FLOAT, FOpenGLTextureFormat( GL_R32F, GL_R32F, GL_RED, GL_FLOAT, false, false));
SetupTextureFormat( PF_G16R16F, FOpenGLTextureFormat( GL_RG16F, GL_RG16F, GL_RG, GL_HALF_FLOAT, false, false));
SetupTextureFormat( PF_G16R16F_FILTER, FOpenGLTextureFormat( GL_RG16F, GL_RG16F, GL_RG, GL_HALF_FLOAT, false, false));
SetupTextureFormat( PF_G32R32F, FOpenGLTextureFormat( GL_RG32F, GL_RG32F, GL_RG, GL_FLOAT, false, false));
SetupTextureFormat( PF_A2B10G10R10, FOpenGLTextureFormat( GL_RGB10_A2, GL_RGB10_A2, GL_RGBA, GL_UNSIGNED_INT_2_10_10_10_REV, false, false));
SetupTextureFormat( PF_R16F, FOpenGLTextureFormat( GL_R16F, GL_R16F, GL_RED, GL_HALF_FLOAT, false, false));
SetupTextureFormat( PF_R16F_FILTER, FOpenGLTextureFormat( GL_R16F, GL_R16F, GL_RED, GL_HALF_FLOAT, false, false));
SetupTextureFormat( PF_A8, FOpenGLTextureFormat( GL_R8, GL_R8, GL_RED, GL_UNSIGNED_BYTE, false, false));
SetupTextureFormat( PF_R32_UINT, FOpenGLTextureFormat( GL_R32UI, GL_R32UI, GL_RED_INTEGER, GL_UNSIGNED_INT, false, false));
SetupTextureFormat( PF_R32_SINT, FOpenGLTextureFormat( GL_R32I, GL_R32I, GL_RED_INTEGER, GL_INT, false, false));
SetupTextureFormat( PF_R16_UINT, FOpenGLTextureFormat( GL_R16UI, GL_R16UI, GL_RED_INTEGER, GL_UNSIGNED_SHORT, false, false));
SetupTextureFormat( PF_R16_SINT, FOpenGLTextureFormat( GL_R16I, GL_R16I, GL_RED_INTEGER, GL_SHORT, false, false));
SetupTextureFormat( PF_R8_UINT, FOpenGLTextureFormat( GL_R8UI, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, false, false));
SetupTextureFormat( PF_R8G8, FOpenGLTextureFormat( GL_RG8, GL_RG8, GL_RG, GL_UNSIGNED_BYTE, false, false));
SetupTextureFormat( PF_R32G32_UINT, FOpenGLTextureFormat( GL_RG32UI, GL_RG32UI, GL_RG_INTEGER, GL_UNSIGNED_INT, false, false));
// Should be GL_RGBA8_SNORM but it doesn't work with glTexBuffer -> mapping to Unorm and unpacking in the shader
SetupTextureFormat( PF_R8G8B8A8_SNORM, FOpenGLTextureFormat( GL_RGBA8, GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false, false));
SetupTextureFormat( PF_FloatRGB, FOpenGLTextureFormat( GL_R11F_G11F_B10F, GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, false, false));
SetupTextureFormat( PF_FloatR11G11B10, FOpenGLTextureFormat( GL_R11F_G11F_B10F, GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV, false, false));
SetupTextureFormat( PF_FloatRGBA, FOpenGLTextureFormat( GL_RGBA16F, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT, false, false));
SetupTextureFormat( PF_R8G8_UINT, FOpenGLTextureFormat( GL_RG8UI, GL_RG8UI, GL_RG_INTEGER, GL_UNSIGNED_BYTE, false, false));
SetupTextureFormat( PF_R32G32B32_UINT, FOpenGLTextureFormat( GL_RGB32UI, GL_RGB32UI, GL_RGB_INTEGER, GL_UNSIGNED_INT, false, false));
SetupTextureFormat( PF_R32G32B32_SINT, FOpenGLTextureFormat( GL_RGB32I, GL_RGB32I, GL_RGB_INTEGER, GL_INT, false, false));
SetupTextureFormat( PF_R32G32B32F, FOpenGLTextureFormat( GL_RGB32F, GL_RGB32F, GL_RGB, GL_FLOAT, false, false));
SetupTextureFormat( PF_R8_SINT, FOpenGLTextureFormat( GL_R8I, GL_R8I, GL_RED_INTEGER, GL_BYTE, false, false));
SetupTextureFormat( PF_B8G8R8A8, FOpenGLTextureFormat( GL_RGBA8, GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false, true));
SetupTextureFormat( PF_R8G8B8A8, FOpenGLTextureFormat( GL_RGBA8, GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE, false, false));
SetupTextureFormat( PF_R8G8B8A8_UINT, FOpenGLTextureFormat( GL_RGBA8UI, GL_RGBA8UI, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, false, false));
SetupTextureFormat( PF_G8, FOpenGLTextureFormat( GL_R8, GL_R8, GL_RED, GL_UNSIGNED_BYTE, false, false));
SetupTextureFormat( PF_R8G8B8, FOpenGLTextureFormat( GL_RGB8, GL_SRGB8, GL_RGB, GL_UNSIGNED_BYTE, false, false));
#if PLATFORM_DESKTOP
CA_SUPPRESS(6286);
if (PLATFORM_DESKTOP)
{
SetupTextureFormat( PF_V8U8, FOpenGLTextureFormat( GL_RG8_SNORM, GL_NONE, GL_RG, GL_BYTE, false, false));
SetupTextureFormat( PF_BC5, FOpenGLTextureFormat( GL_COMPRESSED_RG_RGTC2, GL_COMPRESSED_RG_RGTC2, GL_RG, GL_UNSIGNED_BYTE, true, false));
SetupTextureFormat( PF_BC4, FOpenGLTextureFormat( GL_COMPRESSED_RED_RGTC1, GL_COMPRESSED_RED_RGTC1,GL_RED, GL_UNSIGNED_BYTE, true, false));
SetupTextureFormat( PF_BC7, FOpenGLTextureFormat( GL_COMPRESSED_RGBA_BPTC_UNORM, GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM, GL_RGBA, GL_UNSIGNED_BYTE, true, false));
SetupTextureFormat( PF_G16R16, FOpenGLTextureFormat(GL_RG16, GL_RG16, GL_RG, GL_UNSIGNED_SHORT, false, false));
SetupTextureFormat( PF_G16R16_SNORM, FOpenGLTextureFormat(GL_RG16_SNORM, GL_RG16_SNORM, GL_RG, GL_SHORT, false, false));
}
else
#endif // PLATFORM_DESKTOP
{
#if !PLATFORM_DESKTOP
FOpenGL::PE_SetupTextureFormat(&SetupTextureFormat); // platform extension
#endif // !PLATFORM_DESKTOP
}
if (FOpenGL::SupportsDXT())
{
SetupTextureFormat( PF_DXT1, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT, GL_RGBA, GL_UNSIGNED_BYTE, true, false));
SetupTextureFormat( PF_DXT3, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_BYTE, true, false));
SetupTextureFormat( PF_DXT5, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_BYTE, true, false));
}
#if PLATFORM_ANDROID
if ( FOpenGL::SupportsETC2() )
{
SetupTextureFormat( PF_ETC2_RGB, FOpenGLTextureFormat(GL_COMPRESSED_RGB8_ETC2, GL_COMPRESSED_SRGB8_ETC2, GL_RGBA, GL_UNSIGNED_BYTE, true, false));
SetupTextureFormat( PF_ETC2_RGBA, FOpenGLTextureFormat(GL_COMPRESSED_RGBA8_ETC2_EAC, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, GL_RGBA, GL_UNSIGNED_BYTE, true, false));
SetupTextureFormat( PF_ETC2_R11_EAC, FOpenGLTextureFormat(GL_COMPRESSED_R11_EAC, GL_COMPRESSED_R11_EAC, GL_RED, GL_UNSIGNED_BYTE, true, false));
SetupTextureFormat( PF_ETC2_RG11_EAC, FOpenGLTextureFormat(GL_COMPRESSED_RG11_EAC, GL_COMPRESSED_RG11_EAC, GL_RG, GL_UNSIGNED_BYTE, true, false));
}
#endif
if (FOpenGL::SupportsASTC())
{
SetupTextureFormat( PF_ASTC_4x4, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, GL_RGBA, GL_UNSIGNED_BYTE, true, false) );
SetupTextureFormat( PF_ASTC_6x6, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_ASTC_6x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, GL_RGBA, GL_UNSIGNED_BYTE, true, false) );
SetupTextureFormat( PF_ASTC_8x8, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_ASTC_8x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, GL_RGBA, GL_UNSIGNED_BYTE, true, false) );
SetupTextureFormat( PF_ASTC_10x10, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_ASTC_10x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, GL_RGBA, GL_UNSIGNED_BYTE, true, false) );
SetupTextureFormat( PF_ASTC_12x12, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_ASTC_12x12_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, GL_RGBA, GL_UNSIGNED_BYTE, true, false) );
}
if (FOpenGL::SupportsASTCHDR())
{
SetupTextureFormat(PF_ASTC_4x4_HDR, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, GL_RGBA, GL_HALF_FLOAT, true, false));
SetupTextureFormat(PF_ASTC_6x6_HDR, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_ASTC_6x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, GL_RGBA, GL_HALF_FLOAT, true, false));
SetupTextureFormat(PF_ASTC_8x8_HDR, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_ASTC_8x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, GL_RGBA, GL_HALF_FLOAT, true, false));
SetupTextureFormat(PF_ASTC_10x10_HDR, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_ASTC_10x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, GL_RGBA, GL_HALF_FLOAT, true, false));
SetupTextureFormat(PF_ASTC_12x12_HDR, FOpenGLTextureFormat(GL_COMPRESSED_RGBA_ASTC_12x12_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, GL_RGBA, GL_HALF_FLOAT, true, false));
}
// Some formats need to know how large a block is.
GPixelFormats[ PF_FloatRGB ].BlockBytes = 4;
GPixelFormats[ PF_FloatRGBA ].BlockBytes = 8;
// Temporary fix for nvidia driver issue with non-power-of-two shadowmaps (9/8/2016) UE-35312
// @TODO revisit this with newer drivers
GRHINeedsUnatlasedCSMDepthsWorkaround = true;
GRHISupportsPSOPrecaching = CVarAllowPSOPrecaching.GetValueOnAnyThread();
GRHISupportsPipelineFileCache = !GRHISupportsPSOPrecaching || CVarEnablePSOFileCacheWhenPrecachingActive.GetValueOnAnyThread();
GRHIGlobals.NeedsShaderUnbinds = true;
}
FDynamicRHI* FOpenGLDynamicRHIModule::CreateRHI(ERHIFeatureLevel::Type InRequestedFeatureLevel)
{
GRequestedFeatureLevel = InRequestedFeatureLevel;
return new FOpenGLDynamicRHI();
}
FOpenGLDynamicRHI::FOpenGLDynamicRHI()
{
check(Singleton == nullptr);
Singleton = this;
// This should be called once at the start
check( IsInGameThread() );
check( !GIsThreadedRendering );
#if PLATFORM_ANDROID
GRHINeedsExtraDeletionLatency = CVarGLExtraDeletionLatency.GetValueOnAnyThread() == 1;
#endif
// Ogl must have all programs created on the context owning thread.
// when GRHISupportsAsyncPipelinePrecompile is true pipelinefilecache will call RHICreateGraphicsPipelineState from any thread and
// RHISetGraphicsPipelineState will not be called for precompiled programs.
GRHISupportsAsyncPipelinePrecompile = false;
PlatformInitOpenGL();
PlatformDevice = PlatformCreateOpenGLDevice();
VERIFY_GL_SCOPE();
InitRHICapabilitiesForGL();
if (PlatformCanEnableGPUCapture())
{
EnableIdealGPUCaptureOptions(true);
// Disable persistent mapping
{
auto* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("OpenGL.UBODirectWrite"));
if (CVar)
{
CVar->Set(false);
}
}
{
auto* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("OpenGL.UseStagingBuffer"));
if (CVar)
{
CVar->Set(false);
}
}
}
{
// Temp disable gpusorting for Opengl because of issues on Adreno and Mali devices
auto* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("FX.AllowGPUSorting"));
if (CVar)
{
#if PLATFORM_ANDROID
if(!AndroidThunkCpp_IsOculusMobileApplication())
#endif
{
CVar->Set(false);
}
}
}
#if PLATFORM_ANDROID
// Temp disable gpu particles for OpenGL because of issues on Adreno devices with old driver version
if (GRHIVendorId == 0x5143)
{
auto* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("fx.NiagaraAllowGPUParticles"));
if (CVar)
{
int32 DriverVersion = INT32_MAX;
FString SubVersionString, DriverVersionString;
FString VersionString = FString(ANSI_TO_TCHAR((const ANSICHAR*)glGetString(GL_VERSION)));
if (VersionString.Split(TEXT("V@"), nullptr, &SubVersionString) && SubVersionString.Split(TEXT(" "), &DriverVersionString, nullptr))
{
DriverVersion = FCString::Atoi(*DriverVersionString);
}
if (DriverVersion < 415)
{
CVar->Set(false);
UE_LOG(LogRHI, Log, TEXT("GPU particles are disabled on this device because the driver version %d is less than 415"), DriverVersion);
}
}
}
#endif
// Disable SingleRHIThreadStall for GL occlusion queiresn, which should be set for D3D11 only. Enabling it causes RT->RHIT deadlock
{
auto* CVar = IConsoleManager::Get().FindConsoleVariable(TEXT("r.Occlusion.SingleRHIThreadStall"));
if (CVar)
{
CVar->Set(0);
}
}
PrivateOpenGLDevicePtr = this;
GlobalUniformBuffers.AddZeroed(FUniformBufferStaticSlotRegistry::Get().GetSlotCount());
#if RHI_NEW_GPU_PROFILER == 0
GPUProfilingData.Emplace();
#endif
}
extern void DestroyShadersAndPrograms();
#if PLATFORM_ANDROID
extern bool GOpenGLShaderHackLastCompileSuccess;
// only used to test for shader compatibility issues
static bool VerifyCompiledShader(GLuint Shader, const ANSICHAR* GlslCode, bool IsFatal)
{
SCOPE_CYCLE_COUNTER(STAT_OpenGLShaderCompileVerifyTime);
if (!GOpenGLShaderHackLastCompileSuccess)
{
#if DEBUG_GL_SHADERS
if (GlslCode)
{
UE_LOG(LogRHI, Warning, TEXT("Shader:\n%s"), ANSI_TO_TCHAR(GlslCode));
const ANSICHAR *Temp = GlslCode;
for (int i = 0; i < 30 && (*Temp != '\0'); ++i)
{
FString Converted = ANSI_TO_TCHAR(Temp);
Converted = Converted.LeftChop(256);
UE_LOG(LogRHI, Display, TEXT("%s"), *Converted);
Temp += Converted.Len();
}
}
#endif
UE_LOG(LogRHI, Warning, TEXT("Failed to compile shader."));
return false;
}
return true;
}
#endif
void FOpenGLDynamicRHI::Init()
{
check(!GIsRHIInitialized);
VERIFY_GL_SCOPE();
FOpenGLProgramBinaryCache::Initialize();
InitializeStateResources();
// Create a default point sampler state for internal use.
FSamplerStateInitializerRHI PointSamplerStateParams(SF_Point,AM_Clamp,AM_Clamp,AM_Clamp);
PointSamplerState = this->RHICreateSamplerState(PointSamplerStateParams);
#if PLATFORM_WINDOWS || PLATFORM_LINUX
extern int64 GOpenGLDedicatedVideoMemory;
extern int64 GOpenGLTotalGraphicsMemory;
GOpenGLDedicatedVideoMemory = FOpenGL::GetVideoMemorySize();
if ( GOpenGLDedicatedVideoMemory != 0)
{
GOpenGLTotalGraphicsMemory = GOpenGLDedicatedVideoMemory;
if ( GPoolSizeVRAMPercentage > 0 )
{
float PoolSize = float(GPoolSizeVRAMPercentage) * 0.01f * float(GOpenGLTotalGraphicsMemory);
// Truncate GTexturePoolSize to MB (but still counted in bytes)
GTexturePoolSize = int64(FGenericPlatformMath::TruncToInt(PoolSize / 1024.0f / 1024.0f)) * 1024 * 1024;
UE_LOG(LogRHI, Log, TEXT("Texture pool is %llu MB (%d%% of %llu MB)"),
GTexturePoolSize / 1024 / 1024,
GPoolSizeVRAMPercentage,
GOpenGLTotalGraphicsMemory / 1024 / 1024);
}
}
#else
static const auto CVarStreamingTexturePoolSize = IConsoleManager::Get().FindTConsoleVariableDataInt(TEXT("r.Streaming.PoolSize"));
GTexturePoolSize = (int64)CVarStreamingTexturePoolSize->GetValueOnAnyThread() * 1024 * 1024;
UE_LOG(LogRHI,Log,TEXT("Texture pool is %llu MB (of %llu MB total graphics mem)"),
GTexturePoolSize / 1024 / 1024,
FOpenGL::GetVideoMemorySize());
#endif
// Flush here since we might be switching to a different context/thread for rendering
FOpenGL::Flush();
#if RHI_NEW_GPU_PROFILER
Profiler.Setup();
#endif
FHardwareInfo::RegisterHardwareInfo( NAME_RHI, TEXT( "OpenGL" ) );
FRenderResource::InitPreRHIResources();
GIsRHIInitialized = true;
}
void FOpenGLDynamicRHI::Shutdown()
{
check(IsInGameThread() && IsInRenderingThread()); // require that the render thread has been shut down
#if RHI_NEW_GPU_PROFILER
Profiler.Shutdown();
#endif
Cleanup();
FOpenGLRenderQuery::Cleanup();
DestroyShadersAndPrograms();
PlatformDestroyOpenGLDevice(PlatformDevice);
PrivateOpenGLDevicePtr = NULL;
}
void FOpenGLDynamicRHI::Cleanup()
{
if(GIsRHIInitialized)
{
FOpenGLProgramBinaryCache::Shutdown();
// Reset the RHI initialized flag.
GIsRHIInitialized = false;
#if (RHI_NEW_GPU_PROFILER == 0)
GPUProfilingData->Cleanup();
#endif
// Ask all initialized FRenderResources to release their RHI resources.
FRenderResource::ReleaseRHIForAllResources();
}
// Release the point sampler state.
PointSamplerState.SafeRelease();
extern void EmptyGLSamplerStateCache();
EmptyGLSamplerStateCache();
// Release zero-filled dummy uniform buffer, if it exists.
if (PendingState.ZeroFilledDummyUniformBuffer)
{
FOpenGL::DeleteBuffers(1, &PendingState.ZeroFilledDummyUniformBuffer);
PendingState.ZeroFilledDummyUniformBuffer = 0;
OpenGLBufferStats::UpdateUniformBufferStats(ZERO_FILLED_DUMMY_UNIFORM_BUFFER_SIZE, false);
}
// Release pending shader
PendingState.BoundShaderState.SafeRelease();
check(!IsValidRef(PendingState.BoundShaderState));
VERIFY_GL_SCOPE();
PendingState.CleanupResources();
ContextState.CleanupResources();
}
void FOpenGLDynamicRHI::RHIFlushResources()
{
PlatformFlushIfNeeded();
}
void FOpenGLDynamicRHI::RHIAcquireThreadOwnership()
{
PlatformRenderingContextSetup(PlatformDevice);
VERIFY_GL(RHIAcquireThreadOwnership);
}
void FOpenGLDynamicRHI::RHIReleaseThreadOwnership()
{
VERIFY_GL(RHIReleaseThreadOwnership);
PlatformNULLContextSetup();
}
void* FOpenGLDynamicRHI::RHIGetNativeDevice()
{
return PlatformDevice;
}
void* FOpenGLDynamicRHI::RHIGetNativeInstance()
{
return nullptr;
}
void FOpenGLDynamicRHI::SetCustomPresent(FRHICustomPresent* InCustomPresent)
{
FScopeLock lock(&CustomPresentSection);
CustomPresent = InCustomPresent;
}
uint64 FOpenGLDynamicRHI::RHIGetMinimumAlignmentForBufferBackedSRV(EPixelFormat Format)
{
check(FOpenGL::GetTextureBufferAlignment()>=0);
return FOpenGL::GetTextureBufferAlignment();
}
bool FOpenGLDynamicRHIModule::IsSupported()
{
return true;
}
#if RHI_NEW_GPU_PROFILER
FOpenGLRenderQuery* FOpenGLProfiler::GetQuery()
{
if (QueryPool.IsEmpty())
{
return new FOpenGLRenderQuery(FOpenGLRenderQuery::EType::Profiler);
}
else
{
return QueryPool.Pop();
}
}
FOpenGLRenderQuery* FOpenGLProfiler::InsertQuery(uint64* Target)
{
check(bEnabled);
// Temporarily store the current frame pointer in the Target.
// We retrieve this later when resolving the query.
*Target = reinterpret_cast<uint64>(Current.Get());
FOpenGLRenderQuery* Query = GetQuery();
Query->End(Target);
return Query;
}
FOpenGLProfiler::FFrame::FFrame(FOpenGLProfiler& Profiler)
: EventStream(UE::RHI::GPUProfiler::FQueue(UE::RHI::GPUProfiler::FQueue::EType::Graphics, 0, 0))
{
if (Profiler.bEnabled)
{
glGetInteger64v(GL_TIMESTAMP_EXT, (GLint64*)&Calibration.GLTimestamp);
Calibration.CPUTimestamp = FPlatformTime::Cycles64();
Calibration.CPUFrequency = uint64(1.0 / FPlatformTime::GetSecondsPerCycle64());
}
}
void FOpenGLProfiler::Setup()
{
bEnabled = false;
if (!FOpenGL::SupportsDisjointTimeQueries())
{
UE_LOG(LogOpenGL, Warning, TEXT("OpenGL RHI profiler not supported."));
}
else
{
// Test for the correctness of the GL timestamp functions.
// On some devices, the values reported by the GPU queries do not tick at the same rate as those reported by the CPU function.
GLuint Query;
FOpenGL::GenQueries(1, &Query);
GLuint64 GPU = 0;
GLint64 CPU = 0;
for (int32 Attempts = 0; Attempts < 10; ++Attempts)
{
GPU = 0;
CPU = 0;
// Sample both the GPU and CPU timestamps, as close to simultenously as possible
FOpenGL::QueryTimestampCounter(Query);
FOpenGL::GetQueryObject(Query, FOpenGL::QM_Result, &GPU);
glGetInteger64v(GL_TIMESTAMP_EXT, &CPU);
GLint64 Delta = FMath::Abs(GLint64(GPU) - CPU);
// We consider the timestamps reliable if the GPU and CPU clocks are within 100ms of each other.
// On devices that don't implement this properly, the difference between the clocks can be minutes / hours.
const GLint64 Threshold = 100 * 1000 * 1000; // 100ms in nanoseconds
if (!FOpenGL::TimerQueryDisjoint() && GPU != 0 && CPU != 0 && Delta < Threshold)
{
bEnabled = true;
break;
}
}
FOpenGL::DeleteQueries(1, &Query);
if (bEnabled)
{
UE_LOG(LogOpenGL, Display, TEXT("OpenGL RHI profiler detected reliable timestamps. GPU profiler is enabled. GPU clock: 0x%llx, CPU Clock: 0x%llx"), GPU, CPU);
}
else
{
UE_LOG(LogOpenGL, Warning, TEXT("OpenGL RHI profiler detected unreliable timestamps. GPU profiler is disabled. GPU clock: 0x%llx, CPU Clock: 0x%llx"), GPU, CPU);
}
}
// Register the single graphics GPU queue we have access to in OpenGL.
UE::RHI::GPUProfiler::FQueue Queue(UE::RHI::GPUProfiler::FQueue::EType::Graphics, 0, 0);
UE::RHI::GPUProfiler::InitializeQueues(MakeConstArrayView(&Queue, 1));
Current = MakeUnique<FFrame>(*this);
if (bEnabled)
{
//
// Since we can't tell when the GPU is actually executing engine work in OpenGL,
// just mark the GPU as always busy in the frame. We also push begin/end work
// markers in RHIEndFrame either side of the frame boundary.
//
auto& Event = EmplaceEvent<UE::RHI::GPUProfiler::FEvent::FBeginWork>(Current->Calibration.CPUTimestamp);
InsertQuery(&Event.GPUTimestampTOP);
}
}
void FOpenGLProfiler::Shutdown()
{
// Drain the query readback queue
FOpenGLRenderQuery::PollQueryResults();
}
void FOpenGLProfiler::EndFrame(FDynamicRHI::FRHIEndFrameArgs const& Args)
{
if (bEnabled)
{
auto& EventWork = EmplaceEvent<UE::RHI::GPUProfiler::FEvent::FEndWork>();
Current->EndWorkQuery = InsertQuery(&EventWork.GPUTimestampBOP);
}
if (ExternalGPUTime)
{
EmplaceEvent<UE::RHI::GPUProfiler::FEvent::FFrameTime>(*ExternalGPUTime);
}
// Flushing after the EndWork event helps improve the accuracy of the timer
FOpenGL::Flush();
// We must always emit the frame boundary event, even if the profiler is disabled in the RHI.
TUniquePtr<FFrame> NewFrame = MakeUnique<FFrame>(*this);
// Insert frame boundary
Current->FrameBoundaryEvent = &EmplaceEvent<UE::RHI::GPUProfiler::FEvent::FFrameBoundary>(NewFrame->Calibration.CPUTimestamp, Args.FrameNumber
#if WITH_RHI_BREADCRUMBS
, Args.GPUBreadcrumbs[ERHIPipeline::Graphics]
#endif
#if STATS
, Args.StatsFrame
#endif
);
Pending.Enqueue(MoveTemp(Current));
Current = MoveTemp(NewFrame);
// Attempt to process historic results
while (TUniquePtr<FFrame>* PreviousFramePtr = Pending.Peek())
{
TUniquePtr<FFrame>& PreviousFrame = *PreviousFramePtr;
// When the profiler is disabled, EndWorkQuery will be nullptr
if (PreviousFrame->EndWorkQuery && !FOpenGLRenderQuery::PollQueryResults(PreviousFrame->EndWorkQuery))
{
// Frame not yet finished on the GPU
break;
}
// Previous frame has completed and the data is available.
if (PreviousFrame->bDisjoint)
{
// Discard all the events from this frame, except the frame boundary
UE::RHI::GPUProfiler::FEventStream Stream(PreviousFrame->EventStream.Queue);
Stream.Emplace<UE::RHI::GPUProfiler::FEvent::FFrameBoundary>(*PreviousFrame->FrameBoundaryEvent);
UE::RHI::GPUProfiler::ProcessEvents(MakeArrayView(&Stream, 1));
}
else
{
// This is a valid frame. Publish the profiler events.
UE::RHI::GPUProfiler::ProcessEvents(MakeArrayView(&PreviousFrame->EventStream, 1));
}
Pending.Pop();
}
if (bEnabled)
{
// Start the next frame's GPU work
auto& BeginWork = EmplaceEvent<UE::RHI::GPUProfiler::FEvent::FBeginWork>(Current->Calibration.CPUTimestamp);
InsertQuery(&BeginWork.GPUTimestampTOP);
}
}
uint64 FOpenGLProfiler::ResolveQuery(uint64 Value, uint64* Target, bool bDisjoint)
{
check(bEnabled);
// Read the current frame pointer back out of the target, which was stored here during InsertQuery().
FFrame* Frame = reinterpret_cast<FFrame*>(*Target);
Frame->bDisjoint |= bDisjoint;
if (Frame->bDisjoint)
{
return 0;
}
else
{
// Convert from GPU timestamp to CPU timestamp (relative to FPlatformTime::Cycles64())
int64 GLDelta = Value - Frame->Calibration.GLTimestamp;
int64 CPUDelta = (GLDelta * Frame->Calibration.CPUFrequency) / Frame->Calibration.GLFrequency;
return CPUDelta + Frame->Calibration.CPUTimestamp;
}
}
#endif // RHI_NEW_GPU_PROFILER