mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
1051 lines
52 KiB
C++
1051 lines
52 KiB
C++
// MobileGL - MobileGL/MG_Util/BackendLoaders/OpenGL/Loader.cpp
|
|
// Copyright (c) 2025-2026 MobileGL-Dev
|
|
// Licensed under the GNU Lesser General Public License v3.0:
|
|
// https://www.gnu.org/licenses/gpl-3.0.txt
|
|
// https://www.gnu.org/licenses/lgpl-3.0.txt
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
|
// End of Source File Header
|
|
|
|
#include "Loader.h"
|
|
#include "MG_Util/Types.h"
|
|
#include <Config.h>
|
|
#if !defined(__WIN32) && !defined(_WIN32)
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
namespace MobileGL::MG_Util::BackendLoader {
|
|
static Bool UseAngle() {
|
|
return MG_Config::Features.UseAngle;
|
|
}
|
|
|
|
#if defined(MOBILEGL_TRACE_ANGLE_VARIANTS) && defined(__ANDROID__)
|
|
static Bool IsTraceAngleLibrary(const String& name) {
|
|
return name == "libGLESv2_angle.so" || name == "libEGL_angle.so";
|
|
}
|
|
|
|
static Bool IsAllowedTraceAngleVariant(const String& variant) {
|
|
return variant == "ec889e6ea831" || variant == "90a62123d794";
|
|
}
|
|
|
|
static Bool ResolveTraceAngleLibraryPath(const String& name, String& path) {
|
|
const String& variant = MG_Config::Features.TraceAngleVariant;
|
|
if (!IsAllowedTraceAngleVariant(variant)) {
|
|
MGLOG_F("Rejected trace ANGLE variant '%s'", variant.c_str());
|
|
return false;
|
|
}
|
|
|
|
Dl_info mobileGlInfo{};
|
|
if (dladdr(reinterpret_cast<const void*>(&ResolveTraceAngleLibraryPath), &mobileGlInfo) == 0 ||
|
|
mobileGlInfo.dli_fname == nullptr) {
|
|
MGLOG_F("Failed to resolve signed trace native library directory");
|
|
return false;
|
|
}
|
|
|
|
String nativeLibraryPath = mobileGlInfo.dli_fname;
|
|
const SizeT separator = nativeLibraryPath.find_last_of('/');
|
|
if (separator == String::npos) {
|
|
MGLOG_F("Invalid MobileGL library path: %s", nativeLibraryPath.c_str());
|
|
return false;
|
|
}
|
|
|
|
const SizeT extension = name.rfind(".so");
|
|
if (extension == String::npos) {
|
|
MGLOG_F("Invalid trace ANGLE library name: %s", name.c_str());
|
|
return false;
|
|
}
|
|
path = nativeLibraryPath.substr(0, separator + 1) + name.substr(0, extension) + "_" + variant + ".so";
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
static void* OpenLib(const Vector<String>& names) {
|
|
#if !defined(__WIN32) && !defined(_WIN32) && (!defined(__APPLE__) || defined(MOBILEGL_IOS))
|
|
static const String LibPathPrefixes[] = {
|
|
#if defined(MOBILEGL_IOS)
|
|
"@rpath/", "@executable_path/Frameworks/", "@loader_path/Frameworks/",
|
|
#else
|
|
"/opt/vc/lib/", "/usr/local/lib/", "/usr/lib/", "/usr/lib/x86_64-linux-gnu/",
|
|
"/usr/lib64/", "/lib64/",
|
|
#endif
|
|
"" // Keep this last so the dynamic loader can use LD_LIBRARY_PATH.
|
|
};
|
|
|
|
void* lib = nullptr;
|
|
|
|
Int flags = RTLD_LOCAL | RTLD_NOW;
|
|
for (const auto& prefix : LibPathPrefixes) {
|
|
for (const auto& name : names) {
|
|
#if defined(MOBILEGL_TRACE_ANGLE_VARIANTS) && defined(__ANDROID__)
|
|
if (UseAngle() && IsTraceAngleLibrary(name)) {
|
|
String signedPath;
|
|
if (!ResolveTraceAngleLibraryPath(name, signedPath)) {
|
|
return nullptr;
|
|
}
|
|
lib = dlopen(signedPath.c_str(), flags);
|
|
if (lib == nullptr) {
|
|
MGLOG_F("Failed to open signed trace ANGLE library %s: %s",
|
|
signedPath.c_str(), dlerror());
|
|
return nullptr;
|
|
}
|
|
MGLOG_I("Loaded signed trace ANGLE library: %s", signedPath.c_str());
|
|
return lib;
|
|
}
|
|
#endif
|
|
String path_name = prefix + name;
|
|
if ((lib = dlopen(path_name.c_str(), flags))) {
|
|
MGLOG_I("Loaded GL backend library: %s", path_name.c_str());
|
|
return lib;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
return nullptr;
|
|
}
|
|
|
|
inline void* ProcAddress(void* lib, const char* name) {
|
|
#if !defined(__WIN32) && !defined(_WIN32) && (!defined(__APPLE__) || defined(MOBILEGL_IOS))
|
|
return dlsym(lib, name);
|
|
#else
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
void AcquireGLESFunctions(MG_External::GLESFunctionsTable& funcs,
|
|
MG_External::EGL::eglGetProcAddress_PTR procAddress) {
|
|
if (!procAddress) {
|
|
MGLOG_E("eglGetProcAddress is nullptr, cannot load GLES functions");
|
|
return;
|
|
}
|
|
|
|
#define INIT_GLES_FUNC(name) \
|
|
do { \
|
|
funcs.name = (MG_External::GLES::name##_PTR)procAddress(#name); \
|
|
if (!funcs.name) { \
|
|
MGLOG_E("Failed to load GLES function: %s", #name); \
|
|
} \
|
|
} while (0);
|
|
|
|
// Optional (extension-provided) entry points: a null pointer is expected on drivers that lack the
|
|
// extension, so absence is not an error. The call site null-checks before use.
|
|
#define INIT_GLES_FUNC_OPTIONAL(name) \
|
|
do { \
|
|
funcs.name = (MG_External::GLES::name##_PTR)procAddress(#name); \
|
|
} while (0);
|
|
|
|
{
|
|
INIT_GLES_FUNC(glActiveTexture)
|
|
INIT_GLES_FUNC(glAttachShader)
|
|
INIT_GLES_FUNC(glBindAttribLocation)
|
|
INIT_GLES_FUNC(glBindBuffer)
|
|
INIT_GLES_FUNC(glBindFramebuffer)
|
|
INIT_GLES_FUNC(glBindRenderbuffer)
|
|
INIT_GLES_FUNC(glBindTexture)
|
|
INIT_GLES_FUNC(glBlendColor)
|
|
INIT_GLES_FUNC(glBlendEquation)
|
|
INIT_GLES_FUNC(glBlendEquationSeparate)
|
|
INIT_GLES_FUNC(glBlendFunc)
|
|
INIT_GLES_FUNC(glBlendFuncSeparate)
|
|
INIT_GLES_FUNC(glBufferData)
|
|
INIT_GLES_FUNC(glBufferSubData)
|
|
INIT_GLES_FUNC(glCheckFramebufferStatus)
|
|
INIT_GLES_FUNC(glClear)
|
|
INIT_GLES_FUNC(glClearColor)
|
|
INIT_GLES_FUNC(glClearDepthf)
|
|
INIT_GLES_FUNC(glClearStencil)
|
|
INIT_GLES_FUNC(glColorMask)
|
|
INIT_GLES_FUNC(glCompileShader)
|
|
INIT_GLES_FUNC(glCompressedTexImage2D)
|
|
INIT_GLES_FUNC(glCompressedTexSubImage2D)
|
|
// INIT_GLES_FUNC(glCopyTexImage1D)
|
|
INIT_GLES_FUNC(glCopyTexImage2D)
|
|
INIT_GLES_FUNC(glCopyTexSubImage2D)
|
|
INIT_GLES_FUNC(glCreateProgram)
|
|
INIT_GLES_FUNC(glCreateShader)
|
|
INIT_GLES_FUNC(glCullFace)
|
|
INIT_GLES_FUNC(glDeleteBuffers)
|
|
INIT_GLES_FUNC(glDeleteFramebuffers)
|
|
INIT_GLES_FUNC(glDeleteProgram)
|
|
INIT_GLES_FUNC(glDeleteRenderbuffers)
|
|
INIT_GLES_FUNC(glDeleteShader)
|
|
INIT_GLES_FUNC(glDeleteTextures)
|
|
INIT_GLES_FUNC(glDepthFunc)
|
|
INIT_GLES_FUNC(glDepthMask)
|
|
INIT_GLES_FUNC(glDepthRangef)
|
|
INIT_GLES_FUNC(glDetachShader)
|
|
INIT_GLES_FUNC(glDisable)
|
|
INIT_GLES_FUNC(glDisableVertexAttribArray)
|
|
INIT_GLES_FUNC(glDrawArrays)
|
|
INIT_GLES_FUNC(glDrawElements)
|
|
INIT_GLES_FUNC(glEnable)
|
|
INIT_GLES_FUNC(glEnableVertexAttribArray)
|
|
INIT_GLES_FUNC(glFinish)
|
|
INIT_GLES_FUNC(glFlush)
|
|
INIT_GLES_FUNC(glFramebufferRenderbuffer)
|
|
INIT_GLES_FUNC(glFramebufferTexture2D)
|
|
INIT_GLES_FUNC(glFrontFace)
|
|
INIT_GLES_FUNC(glGenBuffers)
|
|
INIT_GLES_FUNC(glGenerateMipmap)
|
|
INIT_GLES_FUNC(glGenFramebuffers)
|
|
INIT_GLES_FUNC(glGenRenderbuffers)
|
|
INIT_GLES_FUNC(glGenTextures)
|
|
INIT_GLES_FUNC(glGetActiveAttrib)
|
|
INIT_GLES_FUNC(glGetActiveUniform)
|
|
INIT_GLES_FUNC(glGetAttachedShaders)
|
|
INIT_GLES_FUNC(glGetAttribLocation)
|
|
INIT_GLES_FUNC(glGetBooleanv)
|
|
INIT_GLES_FUNC(glGetBufferParameteriv)
|
|
INIT_GLES_FUNC(glGetError)
|
|
INIT_GLES_FUNC(glGetString)
|
|
INIT_GLES_FUNC(glGetStringi)
|
|
INIT_GLES_FUNC(glGetFloatv)
|
|
INIT_GLES_FUNC(glGetFramebufferAttachmentParameteriv)
|
|
INIT_GLES_FUNC(glGetIntegerv)
|
|
INIT_GLES_FUNC(glGetProgramiv)
|
|
INIT_GLES_FUNC(glGetProgramInfoLog)
|
|
INIT_GLES_FUNC(glGetRenderbufferParameteriv)
|
|
INIT_GLES_FUNC(glGetShaderiv)
|
|
INIT_GLES_FUNC(glGetShaderInfoLog)
|
|
INIT_GLES_FUNC(glGetShaderPrecisionFormat)
|
|
INIT_GLES_FUNC(glGetShaderSource)
|
|
INIT_GLES_FUNC(glGetTexParameterfv)
|
|
INIT_GLES_FUNC(glGetTexParameteriv)
|
|
INIT_GLES_FUNC(glGetUniformfv)
|
|
INIT_GLES_FUNC(glGetUniformiv)
|
|
INIT_GLES_FUNC(glGetUniformLocation)
|
|
INIT_GLES_FUNC(glGetVertexAttribfv)
|
|
INIT_GLES_FUNC(glGetVertexAttribiv)
|
|
INIT_GLES_FUNC(glGetVertexAttribPointerv)
|
|
INIT_GLES_FUNC(glHint)
|
|
INIT_GLES_FUNC(glIsBuffer)
|
|
INIT_GLES_FUNC(glIsEnabled)
|
|
INIT_GLES_FUNC(glIsFramebuffer)
|
|
INIT_GLES_FUNC(glIsProgram)
|
|
INIT_GLES_FUNC(glIsRenderbuffer)
|
|
INIT_GLES_FUNC(glIsShader)
|
|
INIT_GLES_FUNC(glIsTexture)
|
|
INIT_GLES_FUNC(glLineWidth)
|
|
INIT_GLES_FUNC(glLinkProgram)
|
|
INIT_GLES_FUNC(glLogicOp)
|
|
INIT_GLES_FUNC(glPointSize)
|
|
INIT_GLES_FUNC(glPixelStorei)
|
|
INIT_GLES_FUNC(glPolygonOffset)
|
|
INIT_GLES_FUNC(glReadPixels)
|
|
INIT_GLES_FUNC(glReleaseShaderCompiler)
|
|
INIT_GLES_FUNC(glRenderbufferStorage)
|
|
INIT_GLES_FUNC(glSampleCoverage)
|
|
INIT_GLES_FUNC(glScissor)
|
|
INIT_GLES_FUNC(glShaderBinary)
|
|
INIT_GLES_FUNC(glShaderSource)
|
|
INIT_GLES_FUNC(glStencilFunc)
|
|
INIT_GLES_FUNC(glStencilFuncSeparate)
|
|
INIT_GLES_FUNC(glStencilMask)
|
|
INIT_GLES_FUNC(glStencilMaskSeparate)
|
|
INIT_GLES_FUNC(glStencilOp)
|
|
INIT_GLES_FUNC(glStencilOpSeparate)
|
|
// INIT_GLES_FUNC(glTexImage1D)
|
|
INIT_GLES_FUNC(glTexImage2D)
|
|
// INIT_GLES_FUNC(glTexStorage1D)
|
|
INIT_GLES_FUNC(glTexParameterf)
|
|
INIT_GLES_FUNC(glTexParameterfv)
|
|
INIT_GLES_FUNC(glTexParameteri)
|
|
INIT_GLES_FUNC(glTexParameteriv)
|
|
INIT_GLES_FUNC(glTexSubImage2D)
|
|
INIT_GLES_FUNC(glUniform1f)
|
|
INIT_GLES_FUNC(glUniform1fv)
|
|
INIT_GLES_FUNC(glUniform1i)
|
|
INIT_GLES_FUNC(glUniform1iv)
|
|
INIT_GLES_FUNC(glUniform2f)
|
|
INIT_GLES_FUNC(glUniform2fv)
|
|
INIT_GLES_FUNC(glUniform2i)
|
|
INIT_GLES_FUNC(glUniform2iv)
|
|
INIT_GLES_FUNC(glUniform3f)
|
|
INIT_GLES_FUNC(glUniform3fv)
|
|
INIT_GLES_FUNC(glUniform3i)
|
|
INIT_GLES_FUNC(glUniform3iv)
|
|
INIT_GLES_FUNC(glUniform4f)
|
|
INIT_GLES_FUNC(glUniform4fv)
|
|
INIT_GLES_FUNC(glUniform4i)
|
|
INIT_GLES_FUNC(glUniform4iv)
|
|
INIT_GLES_FUNC(glUniformMatrix2fv)
|
|
INIT_GLES_FUNC(glUniformMatrix3fv)
|
|
INIT_GLES_FUNC(glUniformMatrix4fv)
|
|
INIT_GLES_FUNC(glUseProgram)
|
|
INIT_GLES_FUNC(glValidateProgram)
|
|
INIT_GLES_FUNC(glVertexAttrib1f)
|
|
INIT_GLES_FUNC(glVertexAttrib1fv)
|
|
INIT_GLES_FUNC(glVertexAttrib2f)
|
|
INIT_GLES_FUNC(glVertexAttrib2fv)
|
|
INIT_GLES_FUNC(glVertexAttrib3f)
|
|
INIT_GLES_FUNC(glVertexAttrib3fv)
|
|
INIT_GLES_FUNC(glVertexAttrib4f)
|
|
INIT_GLES_FUNC(glVertexAttrib4fv)
|
|
INIT_GLES_FUNC(glVertexAttribPointer)
|
|
INIT_GLES_FUNC(glViewport)
|
|
INIT_GLES_FUNC(glReadBuffer)
|
|
INIT_GLES_FUNC(glDrawRangeElements)
|
|
INIT_GLES_FUNC(glTexImage3D)
|
|
INIT_GLES_FUNC(glTexSubImage3D)
|
|
INIT_GLES_FUNC(glCopyTexSubImage3D)
|
|
INIT_GLES_FUNC(glCompressedTexImage3D)
|
|
INIT_GLES_FUNC(glCompressedTexSubImage3D)
|
|
INIT_GLES_FUNC(glGenQueries)
|
|
INIT_GLES_FUNC(glDeleteQueries)
|
|
INIT_GLES_FUNC(glIsQuery)
|
|
INIT_GLES_FUNC(glBeginQuery)
|
|
INIT_GLES_FUNC(glEndQuery)
|
|
INIT_GLES_FUNC(glGetQueryiv)
|
|
INIT_GLES_FUNC(glGetQueryObjectuiv)
|
|
INIT_GLES_FUNC(glUnmapBuffer)
|
|
INIT_GLES_FUNC(glGetBufferPointerv)
|
|
INIT_GLES_FUNC(glDrawBuffers)
|
|
INIT_GLES_FUNC(glUniformMatrix2x3fv)
|
|
INIT_GLES_FUNC(glUniformMatrix3x2fv)
|
|
INIT_GLES_FUNC(glUniformMatrix2x4fv)
|
|
INIT_GLES_FUNC(glUniformMatrix4x2fv)
|
|
INIT_GLES_FUNC(glUniformMatrix3x4fv)
|
|
INIT_GLES_FUNC(glUniformMatrix4x3fv)
|
|
INIT_GLES_FUNC(glBlitFramebuffer)
|
|
INIT_GLES_FUNC(glRenderbufferStorageMultisample)
|
|
INIT_GLES_FUNC(glFramebufferTextureLayer)
|
|
INIT_GLES_FUNC(glFlushMappedBufferRange)
|
|
INIT_GLES_FUNC(glBindVertexArray)
|
|
INIT_GLES_FUNC(glDeleteVertexArrays)
|
|
INIT_GLES_FUNC(glGenVertexArrays)
|
|
INIT_GLES_FUNC(glIsVertexArray)
|
|
INIT_GLES_FUNC(glGetIntegeri_v)
|
|
INIT_GLES_FUNC(glBeginTransformFeedback)
|
|
INIT_GLES_FUNC(glEndTransformFeedback)
|
|
INIT_GLES_FUNC(glBindBufferRange)
|
|
INIT_GLES_FUNC(glBindBufferBase)
|
|
INIT_GLES_FUNC(glTransformFeedbackVaryings)
|
|
INIT_GLES_FUNC(glGetTransformFeedbackVarying)
|
|
INIT_GLES_FUNC(glVertexAttribIPointer)
|
|
INIT_GLES_FUNC(glGetVertexAttribIiv)
|
|
INIT_GLES_FUNC(glGetVertexAttribIuiv)
|
|
INIT_GLES_FUNC(glVertexAttribI4i)
|
|
INIT_GLES_FUNC(glVertexAttribI4ui)
|
|
INIT_GLES_FUNC(glVertexAttribI4iv)
|
|
INIT_GLES_FUNC(glVertexAttribI4uiv)
|
|
INIT_GLES_FUNC(glGetUniformuiv)
|
|
INIT_GLES_FUNC(glGetFragDataLocation)
|
|
INIT_GLES_FUNC(glUniform1ui)
|
|
INIT_GLES_FUNC(glUniform2ui)
|
|
INIT_GLES_FUNC(glUniform3ui)
|
|
INIT_GLES_FUNC(glUniform4ui)
|
|
INIT_GLES_FUNC(glUniform1uiv)
|
|
INIT_GLES_FUNC(glUniform2uiv)
|
|
INIT_GLES_FUNC(glUniform3uiv)
|
|
INIT_GLES_FUNC(glUniform4uiv)
|
|
INIT_GLES_FUNC(glClearBufferiv)
|
|
INIT_GLES_FUNC(glClearBufferuiv)
|
|
INIT_GLES_FUNC(glClearBufferfv)
|
|
INIT_GLES_FUNC(glClearBufferfi)
|
|
INIT_GLES_FUNC(glCopyBufferSubData)
|
|
INIT_GLES_FUNC(glGetUniformIndices)
|
|
INIT_GLES_FUNC(glGetActiveUniformsiv)
|
|
INIT_GLES_FUNC(glGetUniformBlockIndex)
|
|
INIT_GLES_FUNC(glGetActiveUniformBlockiv)
|
|
INIT_GLES_FUNC(glGetActiveUniformBlockName)
|
|
INIT_GLES_FUNC(glUniformBlockBinding)
|
|
INIT_GLES_FUNC(glDrawArraysInstanced)
|
|
INIT_GLES_FUNC(glDrawElementsInstanced)
|
|
INIT_GLES_FUNC(glFenceSync)
|
|
INIT_GLES_FUNC(glIsSync)
|
|
INIT_GLES_FUNC(glDeleteSync)
|
|
INIT_GLES_FUNC(glClientWaitSync)
|
|
INIT_GLES_FUNC(glWaitSync)
|
|
INIT_GLES_FUNC(glGetInteger64v)
|
|
INIT_GLES_FUNC(glGetSynciv)
|
|
INIT_GLES_FUNC(glGetInteger64i_v)
|
|
INIT_GLES_FUNC(glGetBufferParameteri64v)
|
|
INIT_GLES_FUNC(glGenSamplers)
|
|
INIT_GLES_FUNC(glDeleteSamplers)
|
|
INIT_GLES_FUNC(glIsSampler)
|
|
INIT_GLES_FUNC(glBindSampler)
|
|
INIT_GLES_FUNC(glSamplerParameteri)
|
|
INIT_GLES_FUNC(glSamplerParameteriv)
|
|
INIT_GLES_FUNC(glSamplerParameterf)
|
|
INIT_GLES_FUNC(glSamplerParameterfv)
|
|
INIT_GLES_FUNC(glGetSamplerParameteriv)
|
|
INIT_GLES_FUNC(glGetSamplerParameterfv)
|
|
INIT_GLES_FUNC(glVertexAttribDivisor)
|
|
INIT_GLES_FUNC(glBindTransformFeedback)
|
|
INIT_GLES_FUNC(glDeleteTransformFeedbacks)
|
|
INIT_GLES_FUNC(glGenTransformFeedbacks)
|
|
INIT_GLES_FUNC(glIsTransformFeedback)
|
|
INIT_GLES_FUNC(glPauseTransformFeedback)
|
|
INIT_GLES_FUNC(glResumeTransformFeedback)
|
|
INIT_GLES_FUNC(glGetProgramBinary)
|
|
INIT_GLES_FUNC(glProgramBinary)
|
|
INIT_GLES_FUNC(glProgramParameteri)
|
|
INIT_GLES_FUNC(glInvalidateFramebuffer)
|
|
INIT_GLES_FUNC(glInvalidateSubFramebuffer)
|
|
INIT_GLES_FUNC(glTexStorage2D)
|
|
INIT_GLES_FUNC(glTexStorage3D)
|
|
INIT_GLES_FUNC(glGetInternalformativ)
|
|
INIT_GLES_FUNC(glDispatchCompute)
|
|
INIT_GLES_FUNC(glDispatchComputeIndirect)
|
|
INIT_GLES_FUNC(glDrawArraysIndirect)
|
|
INIT_GLES_FUNC(glDrawElementsIndirect)
|
|
INIT_GLES_FUNC(glFramebufferParameteri)
|
|
INIT_GLES_FUNC(glGetFramebufferParameteriv)
|
|
INIT_GLES_FUNC(glGetProgramInterfaceiv)
|
|
INIT_GLES_FUNC(glShaderStorageBlockBinding)
|
|
INIT_GLES_FUNC(glGetProgramResourceIndex)
|
|
INIT_GLES_FUNC(glGetProgramResourceName)
|
|
INIT_GLES_FUNC(glGetProgramResourceiv)
|
|
INIT_GLES_FUNC(glGetProgramResourceLocation)
|
|
INIT_GLES_FUNC(glUseProgramStages)
|
|
INIT_GLES_FUNC(glActiveShaderProgram)
|
|
INIT_GLES_FUNC(glCreateShaderProgramv)
|
|
INIT_GLES_FUNC(glBindProgramPipeline)
|
|
INIT_GLES_FUNC(glDeleteProgramPipelines)
|
|
INIT_GLES_FUNC(glGenProgramPipelines)
|
|
INIT_GLES_FUNC(glIsProgramPipeline)
|
|
INIT_GLES_FUNC(glGetProgramPipelineiv)
|
|
INIT_GLES_FUNC(glProgramUniform1i)
|
|
INIT_GLES_FUNC(glProgramUniform2i)
|
|
INIT_GLES_FUNC(glProgramUniform3i)
|
|
INIT_GLES_FUNC(glProgramUniform4i)
|
|
INIT_GLES_FUNC(glProgramUniform1ui)
|
|
INIT_GLES_FUNC(glProgramUniform2ui)
|
|
INIT_GLES_FUNC(glProgramUniform3ui)
|
|
INIT_GLES_FUNC(glProgramUniform4ui)
|
|
INIT_GLES_FUNC(glProgramUniform1f)
|
|
INIT_GLES_FUNC(glProgramUniform2f)
|
|
INIT_GLES_FUNC(glProgramUniform3f)
|
|
INIT_GLES_FUNC(glProgramUniform4f)
|
|
INIT_GLES_FUNC(glProgramUniform1iv)
|
|
INIT_GLES_FUNC(glProgramUniform2iv)
|
|
INIT_GLES_FUNC(glProgramUniform3iv)
|
|
INIT_GLES_FUNC(glProgramUniform4iv)
|
|
INIT_GLES_FUNC(glProgramUniform1uiv)
|
|
INIT_GLES_FUNC(glProgramUniform2uiv)
|
|
INIT_GLES_FUNC(glProgramUniform3uiv)
|
|
INIT_GLES_FUNC(glProgramUniform4uiv)
|
|
INIT_GLES_FUNC(glProgramUniform1fv)
|
|
INIT_GLES_FUNC(glProgramUniform2fv)
|
|
INIT_GLES_FUNC(glProgramUniform3fv)
|
|
INIT_GLES_FUNC(glProgramUniform4fv)
|
|
INIT_GLES_FUNC(glProgramUniformMatrix2fv)
|
|
INIT_GLES_FUNC(glProgramUniformMatrix3fv)
|
|
INIT_GLES_FUNC(glProgramUniformMatrix4fv)
|
|
INIT_GLES_FUNC(glProgramUniformMatrix2x3fv)
|
|
INIT_GLES_FUNC(glProgramUniformMatrix3x2fv)
|
|
INIT_GLES_FUNC(glProgramUniformMatrix2x4fv)
|
|
INIT_GLES_FUNC(glProgramUniformMatrix4x2fv)
|
|
INIT_GLES_FUNC(glProgramUniformMatrix3x4fv)
|
|
INIT_GLES_FUNC(glProgramUniformMatrix4x3fv)
|
|
INIT_GLES_FUNC(glValidateProgramPipeline)
|
|
INIT_GLES_FUNC(glGetProgramPipelineInfoLog)
|
|
INIT_GLES_FUNC(glBindImageTexture)
|
|
INIT_GLES_FUNC(glGetBooleani_v)
|
|
INIT_GLES_FUNC(glMemoryBarrier)
|
|
INIT_GLES_FUNC(glMemoryBarrierByRegion)
|
|
INIT_GLES_FUNC(glTexStorage2DMultisample)
|
|
INIT_GLES_FUNC(glGetMultisamplefv)
|
|
INIT_GLES_FUNC(glSampleMaski)
|
|
INIT_GLES_FUNC(glGetTexLevelParameteriv)
|
|
INIT_GLES_FUNC(glGetTexLevelParameterfv)
|
|
INIT_GLES_FUNC(glBindVertexBuffer)
|
|
INIT_GLES_FUNC(glVertexAttribFormat)
|
|
INIT_GLES_FUNC(glVertexAttribIFormat)
|
|
INIT_GLES_FUNC(glVertexAttribBinding)
|
|
INIT_GLES_FUNC(glVertexBindingDivisor)
|
|
INIT_GLES_FUNC(glBlendBarrier)
|
|
INIT_GLES_FUNC(glCopyImageSubData)
|
|
INIT_GLES_FUNC(glDebugMessageControl)
|
|
INIT_GLES_FUNC(glDebugMessageInsert)
|
|
INIT_GLES_FUNC(glDebugMessageCallback)
|
|
INIT_GLES_FUNC(glGetDebugMessageLog)
|
|
INIT_GLES_FUNC(glPushDebugGroup)
|
|
INIT_GLES_FUNC(glPopDebugGroup)
|
|
INIT_GLES_FUNC(glObjectLabel)
|
|
INIT_GLES_FUNC(glGetObjectLabel)
|
|
INIT_GLES_FUNC(glObjectPtrLabel)
|
|
INIT_GLES_FUNC(glGetObjectPtrLabel)
|
|
INIT_GLES_FUNC(glGetPointerv)
|
|
INIT_GLES_FUNC(glEnablei)
|
|
INIT_GLES_FUNC(glDisablei)
|
|
INIT_GLES_FUNC(glBlendEquationi)
|
|
INIT_GLES_FUNC(glBlendEquationSeparatei)
|
|
INIT_GLES_FUNC(glBlendFunci)
|
|
INIT_GLES_FUNC(glBlendFuncSeparatei)
|
|
INIT_GLES_FUNC(glColorMaski)
|
|
INIT_GLES_FUNC(glIsEnabledi)
|
|
INIT_GLES_FUNC(glDrawElementsBaseVertex)
|
|
INIT_GLES_FUNC(glDrawRangeElementsBaseVertex)
|
|
INIT_GLES_FUNC(glDrawElementsInstancedBaseVertex)
|
|
INIT_GLES_FUNC(glFramebufferTexture)
|
|
INIT_GLES_FUNC(glPrimitiveBoundingBox)
|
|
INIT_GLES_FUNC(glGetGraphicsResetStatus)
|
|
INIT_GLES_FUNC(glReadnPixels)
|
|
INIT_GLES_FUNC(glGetnUniformfv)
|
|
INIT_GLES_FUNC(glGetnUniformiv)
|
|
INIT_GLES_FUNC(glGetnUniformuiv)
|
|
INIT_GLES_FUNC(glMinSampleShading)
|
|
INIT_GLES_FUNC(glPatchParameteri)
|
|
INIT_GLES_FUNC(glTexParameterIiv)
|
|
INIT_GLES_FUNC(glTexParameterIuiv)
|
|
INIT_GLES_FUNC(glGetTexParameterIiv)
|
|
INIT_GLES_FUNC(glGetTexParameterIuiv)
|
|
INIT_GLES_FUNC(glSamplerParameterIiv)
|
|
INIT_GLES_FUNC(glSamplerParameterIuiv)
|
|
INIT_GLES_FUNC(glGetSamplerParameterIiv)
|
|
INIT_GLES_FUNC(glGetSamplerParameterIuiv)
|
|
INIT_GLES_FUNC(glTexBuffer)
|
|
INIT_GLES_FUNC(glTexBufferRange)
|
|
INIT_GLES_FUNC(glTexStorage3DMultisample)
|
|
INIT_GLES_FUNC(glMapBufferRange)
|
|
INIT_GLES_FUNC(glBufferStorageEXT)
|
|
INIT_GLES_FUNC(glGetQueryObjectivEXT)
|
|
INIT_GLES_FUNC(glGetQueryObjecti64vEXT)
|
|
INIT_GLES_FUNC(glQueryCounterEXT)
|
|
INIT_GLES_FUNC(glGetQueryObjectui64vEXT)
|
|
INIT_GLES_FUNC(glBindFragDataLocationEXT)
|
|
INIT_GLES_FUNC(glMapBufferOES)
|
|
INIT_GLES_FUNC_OPTIONAL(glPolygonModeNV)
|
|
INIT_GLES_FUNC_OPTIONAL(glPolygonModeANGLE)
|
|
INIT_GLES_FUNC_OPTIONAL(glColorMaskiEXT)
|
|
INIT_GLES_FUNC_OPTIONAL(glColorMaskiOES)
|
|
INIT_GLES_FUNC(glMultiDrawArraysIndirectEXT)
|
|
INIT_GLES_FUNC(glMultiDrawElementsIndirectEXT)
|
|
INIT_GLES_FUNC(glMultiDrawElementsBaseVertexEXT)
|
|
}
|
|
}
|
|
|
|
void AcquireEGLFunctions(MG_External::EGLFunctionsTable& funcs) {
|
|
void* eglLib = nullptr;
|
|
#if defined(MOBILEGL_TRACE_ANGLE_VARIANTS) && defined(__ANDROID__)
|
|
void* angleGlesLib = nullptr;
|
|
#endif
|
|
if (UseAngle()) {
|
|
void* glesLib = OpenLib({"libGLESv2_angle.so"});
|
|
if (!glesLib) {
|
|
MGLOG_E("Failed to open ANGLE libGLESv2_angle.so");
|
|
return;
|
|
}
|
|
#if defined(MOBILEGL_TRACE_ANGLE_VARIANTS) && defined(__ANDROID__)
|
|
angleGlesLib = glesLib;
|
|
#endif
|
|
eglLib = OpenLib({"libEGL_angle.so"});
|
|
if (!eglLib) {
|
|
MGLOG_E("Failed to open ANGLE libEGL_angle.so");
|
|
return;
|
|
}
|
|
} else {
|
|
#if defined(MOBILEGL_IOS)
|
|
eglLib = OpenLib({"libtinygl4angle.dylib"});
|
|
#else
|
|
eglLib = OpenLib({"libEGL.so"});
|
|
#endif
|
|
}
|
|
|
|
if (!eglLib) {
|
|
MGLOG_E("Failed to open EGL library");
|
|
return;
|
|
}
|
|
|
|
auto resolveEGLProc = [&](const char* name) -> void* {
|
|
#if defined(MOBILEGL_TRACE_ANGLE_VARIANTS) && defined(__ANDROID__)
|
|
if (UseAngle()) {
|
|
// Avoid the wrapper's canonical libGLESv2_angle.so lookup:
|
|
// resolve its forwarding target from the verified variant.
|
|
String target = "EGL_";
|
|
target += name + 3;
|
|
return ProcAddress(angleGlesLib, target.c_str());
|
|
}
|
|
#endif
|
|
return ProcAddress(eglLib, name);
|
|
};
|
|
|
|
#define INIT_EGL_FUNC(name) \
|
|
do { \
|
|
funcs.name = (MG_External::EGL::name##_PTR)resolveEGLProc(#name); \
|
|
if (!funcs.name) { \
|
|
MGLOG_E("Failed to load EGL function: %s", #name); \
|
|
} \
|
|
} while (0);
|
|
|
|
{
|
|
INIT_EGL_FUNC(eglBindAPI)
|
|
INIT_EGL_FUNC(eglBindTexImage)
|
|
INIT_EGL_FUNC(eglChooseConfig)
|
|
INIT_EGL_FUNC(eglCopyBuffers)
|
|
INIT_EGL_FUNC(eglCreateContext)
|
|
INIT_EGL_FUNC(eglCreatePbufferFromClientBuffer)
|
|
INIT_EGL_FUNC(eglCreatePbufferSurface)
|
|
INIT_EGL_FUNC(eglCreatePixmapSurface)
|
|
INIT_EGL_FUNC(eglCreatePlatformPixmapSurface)
|
|
INIT_EGL_FUNC(eglCreatePlatformWindowSurface)
|
|
INIT_EGL_FUNC(eglCreateWindowSurface)
|
|
INIT_EGL_FUNC(eglDestroyContext)
|
|
INIT_EGL_FUNC(eglDestroySurface)
|
|
INIT_EGL_FUNC(eglGetConfigAttrib)
|
|
INIT_EGL_FUNC(eglGetConfigs)
|
|
INIT_EGL_FUNC(eglGetCurrentContext)
|
|
INIT_EGL_FUNC(eglGetCurrentDisplay)
|
|
INIT_EGL_FUNC(eglGetCurrentSurface)
|
|
INIT_EGL_FUNC(eglGetDisplay)
|
|
INIT_EGL_FUNC(eglGetPlatformDisplay)
|
|
INIT_EGL_FUNC(eglGetError)
|
|
INIT_EGL_FUNC(eglGetProcAddress)
|
|
INIT_EGL_FUNC(eglInitialize)
|
|
INIT_EGL_FUNC(eglMakeCurrent)
|
|
INIT_EGL_FUNC(eglQueryAPI)
|
|
INIT_EGL_FUNC(eglQueryContext)
|
|
INIT_EGL_FUNC(eglQueryString)
|
|
INIT_EGL_FUNC(eglQuerySurface)
|
|
INIT_EGL_FUNC(eglReleaseTexImage)
|
|
INIT_EGL_FUNC(eglReleaseThread)
|
|
INIT_EGL_FUNC(eglSurfaceAttrib)
|
|
INIT_EGL_FUNC(eglSwapBuffers)
|
|
INIT_EGL_FUNC(eglSwapBuffersWithDamageEXT)
|
|
INIT_EGL_FUNC(eglSwapInterval)
|
|
INIT_EGL_FUNC(eglTerminate)
|
|
INIT_EGL_FUNC(eglUnlockSurfaceKHR)
|
|
INIT_EGL_FUNC(eglWaitClient)
|
|
INIT_EGL_FUNC(eglWaitGL)
|
|
INIT_EGL_FUNC(eglWaitNative)
|
|
INIT_EGL_FUNC(eglCreateSync)
|
|
INIT_EGL_FUNC(eglDestroySync)
|
|
INIT_EGL_FUNC(eglClientWaitSync)
|
|
INIT_EGL_FUNC(eglGetSyncAttrib)
|
|
INIT_EGL_FUNC(eglCreateImage)
|
|
INIT_EGL_FUNC(eglDestroyImage)
|
|
INIT_EGL_FUNC(eglGetPlatformDisplay)
|
|
INIT_EGL_FUNC(eglWaitSync)
|
|
}
|
|
}
|
|
|
|
// Detects whether indirect draws leak the command's baseInstance word ("reserved, must
|
|
// be zero" in unextended ES) into gl_InstanceID. Conforming ES drivers keep
|
|
// gl_InstanceID zero-based, but ANGLE's Vulkan backend forwards the command verbatim to
|
|
// vkCmdDraw*Indirect and compiles gl_InstanceID to SPIR-V InstanceIndex, which includes
|
|
// firstInstance. The DirectGLES native indirect-draw path uses this answer to keep
|
|
// gl_InstanceID zero-based in rewritten shaders (PromoteDrawParameterGlobalsToUniforms).
|
|
Bool ProbeIndirectInstanceIdIncludesBaseInstance(const MG_External::GLESCapabilities& caps,
|
|
const MG_External::GLESFunctionsTable& f) {
|
|
const Bool esVersionOk =
|
|
caps.GLESVersion.Major > 3 || (caps.GLESVersion.Major == 3 && caps.GLESVersion.Minor >= 1);
|
|
if (!esVersionOk || !f.glDrawArraysIndirect || !f.glBindBufferBase || !f.glMapBufferRange ||
|
|
!f.glUnmapBuffer || !f.glMemoryBarrier || !f.glCreateShader || !f.glCreateProgram) {
|
|
return false;
|
|
}
|
|
GLint maxVertexSsboBlocks = 0;
|
|
f.glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &maxVertexSsboBlocks);
|
|
if (maxVertexSsboBlocks < 1) {
|
|
// The native indirect machinery cannot read the command buffer from the vertex
|
|
// stage on this driver anyway; assume conforming zero-based gl_InstanceID.
|
|
MGLOG_I("baseInstance probe skipped: GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS = %d", maxVertexSsboBlocks);
|
|
return false;
|
|
}
|
|
while (f.glGetError() != GL_NO_ERROR) {
|
|
}
|
|
|
|
const char* vsSource = "#version 310 es\n"
|
|
"layout(std430, binding = 0) buffer MgProbeResult { highp int mg_probeValue; };\n"
|
|
"void main() {\n"
|
|
" mg_probeValue = gl_InstanceID;\n"
|
|
" gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n"
|
|
" gl_PointSize = 1.0;\n"
|
|
"}\n";
|
|
const char* fsSource = "#version 310 es\n"
|
|
"void main() {}\n";
|
|
const auto compileShader = [&f](GLenum type, const char* src) -> GLuint {
|
|
const GLuint shader = f.glCreateShader(type);
|
|
if (shader == 0) {
|
|
return 0;
|
|
}
|
|
f.glShaderSource(shader, 1, &src, nullptr);
|
|
f.glCompileShader(shader);
|
|
GLint status = GL_FALSE;
|
|
f.glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
|
|
if (status != GL_TRUE) {
|
|
f.glDeleteShader(shader);
|
|
return 0;
|
|
}
|
|
return shader;
|
|
};
|
|
const GLuint vs = compileShader(GL_VERTEX_SHADER, vsSource);
|
|
const GLuint fs = compileShader(GL_FRAGMENT_SHADER, fsSource);
|
|
GLuint program = 0;
|
|
if (vs != 0 && fs != 0) {
|
|
program = f.glCreateProgram();
|
|
if (program != 0) {
|
|
f.glAttachShader(program, vs);
|
|
f.glAttachShader(program, fs);
|
|
f.glLinkProgram(program);
|
|
GLint status = GL_FALSE;
|
|
f.glGetProgramiv(program, GL_LINK_STATUS, &status);
|
|
if (status != GL_TRUE) {
|
|
f.glDeleteProgram(program);
|
|
program = 0;
|
|
}
|
|
}
|
|
}
|
|
if (vs != 0) f.glDeleteShader(vs);
|
|
if (fs != 0) f.glDeleteShader(fs);
|
|
if (program == 0) {
|
|
MGLOG_I("baseInstance probe skipped: probe program failed to build (vs=%u fs=%u)", vs, fs);
|
|
while (f.glGetError() != GL_NO_ERROR) {
|
|
}
|
|
return false;
|
|
}
|
|
|
|
constexpr GLuint kProbeBaseInstance = 7;
|
|
struct {
|
|
GLuint count;
|
|
GLuint instanceCount;
|
|
GLuint first;
|
|
GLuint baseInstance;
|
|
} command = {1, 1, 0, kProbeBaseInstance};
|
|
const GLint sentinel = -1;
|
|
// ES makes indirect draws INVALID_OPERATION on the default vertex array object.
|
|
GLuint vao = 0;
|
|
f.glGenVertexArrays(1, &vao);
|
|
f.glBindVertexArray(vao);
|
|
GLuint buffers[2] = {0, 0}; // [0] = result SSBO, [1] = indirect command buffer
|
|
f.glGenBuffers(2, buffers);
|
|
f.glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffers[0]);
|
|
f.glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(sentinel), &sentinel, GL_STATIC_DRAW);
|
|
f.glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffers[0]);
|
|
f.glBindBuffer(GL_DRAW_INDIRECT_BUFFER, buffers[1]);
|
|
f.glBufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(command), &command, GL_STATIC_DRAW);
|
|
|
|
// The default framebuffer may be incomplete (e.g. surfaceless contexts) and draws
|
|
// are validated against completeness even under GL_RASTERIZER_DISCARD, so give the
|
|
// probe its own 1x1 target.
|
|
GLuint framebuffer = 0;
|
|
GLuint renderbuffer = 0;
|
|
f.glGenFramebuffers(1, &framebuffer);
|
|
f.glGenRenderbuffers(1, &renderbuffer);
|
|
f.glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
|
|
f.glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 1, 1);
|
|
f.glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
|
|
f.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
|
|
|
|
f.glUseProgram(program);
|
|
f.glEnable(GL_RASTERIZER_DISCARD);
|
|
f.glDrawArraysIndirect(GL_POINTS, nullptr);
|
|
f.glDisable(GL_RASTERIZER_DISCARD);
|
|
f.glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
|
|
|
|
Bool includesBase = false;
|
|
const GLenum drawError = f.glGetError();
|
|
if (drawError == GL_NO_ERROR) {
|
|
f.glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffers[0]);
|
|
const void* mapped = f.glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(GLint), GL_MAP_READ_BIT);
|
|
if (mapped != nullptr) {
|
|
GLint written = -1;
|
|
std::memcpy(&written, mapped, sizeof(written));
|
|
f.glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
|
|
includesBase = written == static_cast<GLint>(kProbeBaseInstance);
|
|
MGLOG_I("baseInstance probe: shader observed gl_InstanceID = %d (baseInstance was %u)", written,
|
|
kProbeBaseInstance);
|
|
} else {
|
|
MGLOG_I("baseInstance probe inconclusive: result map failed");
|
|
}
|
|
} else {
|
|
MGLOG_I("baseInstance probe inconclusive: draw raised GL error 0x%x", drawError);
|
|
}
|
|
|
|
f.glUseProgram(0);
|
|
f.glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
f.glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
f.glDeleteFramebuffers(1, &framebuffer);
|
|
f.glDeleteRenderbuffers(1, &renderbuffer);
|
|
f.glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, 0);
|
|
f.glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
|
|
f.glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0);
|
|
f.glBindVertexArray(0);
|
|
f.glDeleteVertexArrays(1, &vao);
|
|
f.glDeleteBuffers(2, buffers);
|
|
f.glDeleteProgram(program);
|
|
while (f.glGetError() != GL_NO_ERROR) {
|
|
}
|
|
return includesBase;
|
|
}
|
|
|
|
Bool FillInGLESCapabilities(MG_External::GLESCapabilities& caps, const MG_External::GLESFunctionsTable& glesFuncs) {
|
|
if (!glesFuncs.glGetString || !glesFuncs.glGetIntegerv) {
|
|
MGLOG_E("Required GLES functions are not loaded, cannot query capabilities");
|
|
return false;
|
|
}
|
|
|
|
auto* vendorName = glesFuncs.glGetString(GL_VENDOR);
|
|
MGLOG_I("GL_VENDOR: %s", vendorName);
|
|
auto* gpuName = glesFuncs.glGetString(GL_RENDERER);
|
|
MGLOG_I("GL_RENDERER: %s", gpuName);
|
|
glesFuncs.glGetIntegerv(GL_MAJOR_VERSION, &caps.GLESVersion.Major);
|
|
glesFuncs.glGetIntegerv(GL_MINOR_VERSION, &caps.GLESVersion.Minor);
|
|
|
|
caps.GLESVersionString = String((char*)glesFuncs.glGetString(GL_VERSION));
|
|
caps.GLESRendererString = String((char*)gpuName);
|
|
caps.GLESVendorString = String((char*)vendorName);
|
|
caps.GLESShadingLanguageVersionString = String((char*)glesFuncs.glGetString(GL_SHADING_LANGUAGE_VERSION));
|
|
|
|
GLint extCount = 0;
|
|
glesFuncs.glGetIntegerv(GL_NUM_EXTENSIONS, &extCount);
|
|
MGLOG_I("Detected %d OpenGL ES extensions:", extCount);
|
|
for (GLint i = 0; i < extCount; ++i) {
|
|
const char* extension = (const char*)glesFuncs.glGetStringi(GL_EXTENSIONS, i);
|
|
if (extension) {
|
|
MGLOG_I(" %s", extension);
|
|
if (std::strcmp(extension, "GL_EXT_buffer_storage") == 0) {
|
|
caps.SupportsPersistentMapping = true;
|
|
}
|
|
if (std::strcmp(extension, "GL_EXT_texture_norm16") == 0) {
|
|
caps.SupportsNorm16Texture = true;
|
|
}
|
|
if (std::strcmp(extension, "GL_EXT_texture_filter_anisotropic") == 0) {
|
|
caps.SupportsTextureFilterAnisotropy = true;
|
|
}
|
|
if (std::strcmp(extension, "GL_EXT_base_instance") == 0) {
|
|
caps.SupportsBaseInstance = true;
|
|
}
|
|
if (std::strcmp(extension, "GL_EXT_disjoint_timer_query") == 0) {
|
|
caps.SupportsDisjointTimerQuery = true;
|
|
}
|
|
if (std::strcmp(extension, "GL_EXT_blend_func_extended") == 0) {
|
|
caps.SupportsDualSourceBlend = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Detect optional raster/color-mask entry points by whether they loaded. glColorMaski is GLES
|
|
// 3.2 core (no extension string), so pointer presence is the reliable signal for all of these.
|
|
caps.SupportsPolygonMode =
|
|
glesFuncs.glPolygonModeNV != nullptr || glesFuncs.glPolygonModeANGLE != nullptr;
|
|
caps.SupportsIndexedColorMask = glesFuncs.glColorMaski != nullptr ||
|
|
glesFuncs.glColorMaskiEXT != nullptr ||
|
|
glesFuncs.glColorMaskiOES != nullptr;
|
|
MGLOG_I(" glPolygonMode (NV/ANGLE): %s", caps.SupportsPolygonMode ? "yes" : "no");
|
|
MGLOG_I(" indexed glColorMaski: %s", caps.SupportsIndexedColorMask ? "yes" : "no");
|
|
MGLOG_I(" dual-source blend (EXT_blend_func_extended): %s",
|
|
caps.SupportsDualSourceBlend ? "yes" : "no");
|
|
|
|
MGLOG_I("OpenGL ES capabilities:");
|
|
glesFuncs.glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &caps.UniformBufferOffsetAlignment);
|
|
MGLOG_I(" GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: %d", caps.UniformBufferOffsetAlignment);
|
|
GLfloat aliasedLineWidthRange[2] = {1.0f, 1.0f};
|
|
GLfloat smoothLineWidthRange[2] = {1.0f, 1.0f};
|
|
GLfloat smoothLineWidthGranularity = 1.0f;
|
|
GLfloat aliasedPointSizeRange[2] = {1.0f, 1.0f};
|
|
GLfloat viewportBoundsRange[2] = {0.0f, 0.0f};
|
|
GLint maxViewportDims[2] = {16384, 16384};
|
|
GLint viewportSubpixelBits = 0;
|
|
GLint max3DTextureSize = 16384;
|
|
GLint maxArrayTextureLayers = 2048;
|
|
GLint maxCubeMapTextureSize = 16384;
|
|
GLint maxFramebufferWidth = 16384;
|
|
GLint maxFramebufferHeight = 16384;
|
|
GLint maxFramebufferLayers = 2048;
|
|
GLint maxRenderbufferSize = 16384;
|
|
GLint maxTextureSize = 16384;
|
|
GLint maxColorTextureSamples = 1;
|
|
GLint maxDepthTextureSamples = 1;
|
|
GLint maxFramebufferSamples = 1;
|
|
GLint maxIntegerSamples = 1;
|
|
GLint maxSamples = 1;
|
|
GLint maxSampleMaskWords = 1;
|
|
GLint maxTextureImageUnits = 32;
|
|
GLint maxVertexTextureImageUnits = 32;
|
|
GLint maxComputeTextureImageUnits = 32;
|
|
GLint maxCombinedTextureImageUnits = 192;
|
|
GLint maxVertexAttribs = 16;
|
|
GLint maxComputeShaderStorageBlocks = 8;
|
|
GLint maxCombinedShaderStorageBlocks = 32;
|
|
GLint maxComputeUniformBlocks = 12;
|
|
GLint maxComputeWorkGroupInvocations = 128;
|
|
GLint maxShaderStorageBufferBindings = 8;
|
|
GLint maxTextureBufferSize = 65536;
|
|
GLint maxUniformBufferBindings = 24;
|
|
GLint maxUniformBlockSize = 16384;
|
|
GLint maxImageUnits = 8;
|
|
GLint maxCombinedImageUniforms = 8;
|
|
GLint maxVertexImageUniforms = 0;
|
|
GLint maxGeometryImageUniforms = 0;
|
|
GLint maxFragmentImageUniforms = 8;
|
|
GLint maxComputeImageUniforms = 8;
|
|
GLint maxDrawBuffers = 8;
|
|
GLint maxColorAttachments = 8;
|
|
GLint maxClipDistances = 8;
|
|
GLint maxViewports = 16;
|
|
glesFuncs.glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, aliasedLineWidthRange);
|
|
glesFuncs.glGetFloatv(GL_SMOOTH_LINE_WIDTH_RANGE, smoothLineWidthRange);
|
|
glesFuncs.glGetFloatv(GL_SMOOTH_LINE_WIDTH_GRANULARITY, &smoothLineWidthGranularity);
|
|
glesFuncs.glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, aliasedPointSizeRange);
|
|
glesFuncs.glGetFloatv(GL_VIEWPORT_BOUNDS_RANGE, viewportBoundsRange);
|
|
glesFuncs.glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &max3DTextureSize);
|
|
glesFuncs.glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxArrayTextureLayers);
|
|
glesFuncs.glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxCubeMapTextureSize);
|
|
glesFuncs.glGetIntegerv(GL_MAX_FRAMEBUFFER_WIDTH, &maxFramebufferWidth);
|
|
glesFuncs.glGetIntegerv(GL_MAX_FRAMEBUFFER_HEIGHT, &maxFramebufferHeight);
|
|
glesFuncs.glGetIntegerv(GL_MAX_FRAMEBUFFER_LAYERS, &maxFramebufferLayers);
|
|
glesFuncs.glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxRenderbufferSize);
|
|
glesFuncs.glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
|
|
glesFuncs.glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &maxColorTextureSamples);
|
|
glesFuncs.glGetIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &maxDepthTextureSamples);
|
|
glesFuncs.glGetIntegerv(GL_MAX_FRAMEBUFFER_SAMPLES, &maxFramebufferSamples);
|
|
glesFuncs.glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &maxIntegerSamples);
|
|
glesFuncs.glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
|
|
glesFuncs.glGetIntegerv(GL_MAX_SAMPLE_MASK_WORDS, &maxSampleMaskWords);
|
|
glesFuncs.glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits);
|
|
glesFuncs.glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, &maxVertexTextureImageUnits);
|
|
glesFuncs.glGetIntegerv(GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS, &maxComputeTextureImageUnits);
|
|
glesFuncs.glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxCombinedTextureImageUnits);
|
|
glesFuncs.glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs);
|
|
glesFuncs.glGetIntegerv(GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS, &maxComputeShaderStorageBlocks);
|
|
glesFuncs.glGetIntegerv(GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, &maxCombinedShaderStorageBlocks);
|
|
glesFuncs.glGetIntegerv(GL_MAX_COMPUTE_UNIFORM_BLOCKS, &maxComputeUniformBlocks);
|
|
glesFuncs.glGetIntegerv(GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS, &maxComputeWorkGroupInvocations);
|
|
glesFuncs.glGetIntegerv(GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &maxShaderStorageBufferBindings);
|
|
glesFuncs.glGetIntegerv(GL_MAX_TEXTURE_BUFFER_SIZE, &maxTextureBufferSize);
|
|
glesFuncs.glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings);
|
|
glesFuncs.glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUniformBlockSize);
|
|
glesFuncs.glGetIntegerv(GL_MAX_IMAGE_UNITS, &maxImageUnits);
|
|
glesFuncs.glGetIntegerv(GL_MAX_COMBINED_IMAGE_UNIFORMS, &maxCombinedImageUniforms);
|
|
glesFuncs.glGetIntegerv(GL_MAX_VERTEX_IMAGE_UNIFORMS, &maxVertexImageUniforms);
|
|
glesFuncs.glGetIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &maxFragmentImageUniforms);
|
|
glesFuncs.glGetIntegerv(GL_MAX_COMPUTE_IMAGE_UNIFORMS, &maxComputeImageUniforms);
|
|
// Geometry shaders and their image-uniform query are core only in ES 3.2. DirectGLES
|
|
// emits ESSL 3.10 on an ES 3.1 context, so reporting zero there is both legal and an
|
|
// accurate description of what the backend compiler can consume.
|
|
if (caps.GLESVersion.Major > 3 ||
|
|
(caps.GLESVersion.Major == 3 && caps.GLESVersion.Minor >= 2)) {
|
|
glesFuncs.glGetIntegerv(GL_MAX_GEOMETRY_IMAGE_UNIFORMS, &maxGeometryImageUniforms);
|
|
}
|
|
glesFuncs.glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
|
|
glesFuncs.glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxColorAttachments);
|
|
glesFuncs.glGetIntegerv(GL_MAX_CLIP_DISTANCES, &maxClipDistances);
|
|
glesFuncs.glGetIntegerv(GL_MAX_VIEWPORTS, &maxViewports);
|
|
glesFuncs.glGetIntegerv(GL_MAX_VIEWPORT_DIMS, maxViewportDims);
|
|
glesFuncs.glGetIntegerv(GL_VIEWPORT_SUBPIXEL_BITS, &viewportSubpixelBits);
|
|
// Only legal to query once the extension has been seen in the loop above, hence not batched
|
|
// with the unconditional probes: on a driver without it this raises GL_INVALID_ENUM.
|
|
if (caps.SupportsTextureFilterAnisotropy) {
|
|
GLfloat maxTextureMaxAnisotropy = 1.0f;
|
|
glesFuncs.glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxTextureMaxAnisotropy);
|
|
caps.MaxTextureMaxAnisotropy = std::max(maxTextureMaxAnisotropy, 1.0f);
|
|
}
|
|
caps.AliasedLineWidthRangeMin = aliasedLineWidthRange[0];
|
|
caps.AliasedLineWidthRangeMax = aliasedLineWidthRange[1];
|
|
caps.SmoothLineWidthRangeMin = smoothLineWidthRange[0];
|
|
caps.SmoothLineWidthRangeMax = smoothLineWidthRange[1];
|
|
caps.SmoothLineWidthGranularity = smoothLineWidthGranularity;
|
|
caps.PointSizeRangeMin = aliasedPointSizeRange[0];
|
|
caps.PointSizeRangeMax = aliasedPointSizeRange[1];
|
|
caps.PointSizeGranularity = 1.0f;
|
|
caps.Max3DTextureSize = max3DTextureSize;
|
|
caps.MaxArrayTextureLayers = maxArrayTextureLayers;
|
|
caps.MaxCubeMapTextureSize = maxCubeMapTextureSize;
|
|
caps.MaxFramebufferWidth = maxFramebufferWidth;
|
|
caps.MaxFramebufferHeight = maxFramebufferHeight;
|
|
caps.MaxFramebufferLayers = maxFramebufferLayers;
|
|
caps.MaxRenderbufferSize = maxRenderbufferSize;
|
|
caps.MaxTextureSize = maxTextureSize;
|
|
caps.MaxColorTextureSamples = maxColorTextureSamples;
|
|
caps.MaxDepthTextureSamples = maxDepthTextureSamples;
|
|
caps.MaxFramebufferSamples = maxFramebufferSamples;
|
|
caps.MaxIntegerSamples = maxIntegerSamples;
|
|
caps.MaxSamples = maxSamples;
|
|
caps.MaxSampleMaskWords = maxSampleMaskWords;
|
|
caps.MaxTextureImageUnits = maxTextureImageUnits;
|
|
caps.MaxVertexTextureImageUnits = maxVertexTextureImageUnits;
|
|
caps.MaxComputeTextureImageUnits = maxComputeTextureImageUnits;
|
|
caps.MaxCombinedTextureImageUnits = maxCombinedTextureImageUnits;
|
|
caps.MaxVertexAttribs = maxVertexAttribs;
|
|
caps.MaxComputeShaderStorageBlocks = maxComputeShaderStorageBlocks;
|
|
caps.MaxCombinedShaderStorageBlocks = maxCombinedShaderStorageBlocks;
|
|
caps.MaxComputeUniformBlocks = maxComputeUniformBlocks;
|
|
caps.MaxComputeWorkGroupInvocations = maxComputeWorkGroupInvocations;
|
|
caps.MaxShaderStorageBufferBindings = maxShaderStorageBufferBindings;
|
|
caps.MaxTextureBufferSize = maxTextureBufferSize;
|
|
caps.MaxUniformBufferBindings = maxUniformBufferBindings;
|
|
caps.MaxUniformBlockSize = maxUniformBlockSize;
|
|
caps.MaxImageUnits = maxImageUnits;
|
|
caps.MaxCombinedImageUniforms = maxCombinedImageUniforms;
|
|
caps.MaxVertexImageUniforms = maxVertexImageUniforms;
|
|
caps.MaxGeometryImageUniforms = maxGeometryImageUniforms;
|
|
caps.MaxFragmentImageUniforms = maxFragmentImageUniforms;
|
|
caps.MaxComputeImageUniforms = maxComputeImageUniforms;
|
|
caps.MaxDrawBuffers = maxDrawBuffers;
|
|
caps.MaxColorAttachments = maxColorAttachments;
|
|
caps.MaxClipDistances = maxClipDistances;
|
|
caps.MaxViewports = maxViewports;
|
|
caps.MaxViewportWidth = maxViewportDims[0];
|
|
caps.MaxViewportHeight = maxViewportDims[1];
|
|
caps.ViewportBoundsRangeMin = viewportBoundsRange[0];
|
|
caps.ViewportBoundsRangeMax = viewportBoundsRange[1];
|
|
caps.ViewportSubpixelBits = viewportSubpixelBits;
|
|
MGLOG_I(" GL_ALIASED_LINE_WIDTH_RANGE: [%.3f, %.3f]", caps.AliasedLineWidthRangeMin,
|
|
caps.AliasedLineWidthRangeMax);
|
|
MGLOG_I(" GL_SMOOTH_LINE_WIDTH_RANGE: [%.3f, %.3f]", caps.SmoothLineWidthRangeMin,
|
|
caps.SmoothLineWidthRangeMax);
|
|
MGLOG_I(" GL_SMOOTH_LINE_WIDTH_GRANULARITY: %.3f", caps.SmoothLineWidthGranularity);
|
|
MGLOG_I(" GL_ALIASED_POINT_SIZE_RANGE: [%.3f, %.3f]", caps.PointSizeRangeMin, caps.PointSizeRangeMax);
|
|
MGLOG_I(" GL_MAX_3D_TEXTURE_SIZE: %d", caps.Max3DTextureSize);
|
|
MGLOG_I(" GL_MAX_ARRAY_TEXTURE_LAYERS: %d", caps.MaxArrayTextureLayers);
|
|
MGLOG_I(" GL_MAX_CUBE_MAP_TEXTURE_SIZE: %d", caps.MaxCubeMapTextureSize);
|
|
MGLOG_I(" GL_MAX_FRAMEBUFFER_WIDTH: %d", caps.MaxFramebufferWidth);
|
|
MGLOG_I(" GL_MAX_FRAMEBUFFER_HEIGHT: %d", caps.MaxFramebufferHeight);
|
|
MGLOG_I(" GL_MAX_FRAMEBUFFER_LAYERS: %d", caps.MaxFramebufferLayers);
|
|
MGLOG_I(" GL_MAX_RENDERBUFFER_SIZE: %d", caps.MaxRenderbufferSize);
|
|
MGLOG_I(" GL_MAX_TEXTURE_SIZE: %d", caps.MaxTextureSize);
|
|
MGLOG_I(" GL_MAX_COLOR_TEXTURE_SAMPLES: %d", caps.MaxColorTextureSamples);
|
|
MGLOG_I(" GL_MAX_DEPTH_TEXTURE_SAMPLES: %d", caps.MaxDepthTextureSamples);
|
|
MGLOG_I(" GL_MAX_FRAMEBUFFER_SAMPLES: %d", caps.MaxFramebufferSamples);
|
|
MGLOG_I(" GL_MAX_INTEGER_SAMPLES: %d", caps.MaxIntegerSamples);
|
|
MGLOG_I(" GL_MAX_SAMPLES: %d", caps.MaxSamples);
|
|
MGLOG_I(" GL_MAX_SAMPLE_MASK_WORDS: %d", caps.MaxSampleMaskWords);
|
|
MGLOG_I(" GL_MAX_TEXTURE_IMAGE_UNITS: %d", caps.MaxTextureImageUnits);
|
|
MGLOG_I(" GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: %d", caps.MaxVertexTextureImageUnits);
|
|
MGLOG_I(" GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS: %d", caps.MaxComputeTextureImageUnits);
|
|
MGLOG_I(" GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: %d", caps.MaxCombinedTextureImageUnits);
|
|
MGLOG_I(" GL_MAX_VERTEX_ATTRIBS: %d", caps.MaxVertexAttribs);
|
|
MGLOG_I(" GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS: %d", caps.MaxComputeShaderStorageBlocks);
|
|
MGLOG_I(" GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS: %d", caps.MaxCombinedShaderStorageBlocks);
|
|
MGLOG_I(" GL_MAX_COMPUTE_UNIFORM_BLOCKS: %d", caps.MaxComputeUniformBlocks);
|
|
MGLOG_I(" GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS: %d", caps.MaxComputeWorkGroupInvocations);
|
|
MGLOG_I(" GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS: %d", caps.MaxShaderStorageBufferBindings);
|
|
MGLOG_I(" GL_MAX_TEXTURE_BUFFER_SIZE: %d", caps.MaxTextureBufferSize);
|
|
MGLOG_I(" GL_MAX_UNIFORM_BUFFER_BINDINGS: %d", caps.MaxUniformBufferBindings);
|
|
MGLOG_I(" GL_MAX_UNIFORM_BLOCK_SIZE: %d", caps.MaxUniformBlockSize);
|
|
MGLOG_I(" GL_MAX_IMAGE_UNITS: %d", caps.MaxImageUnits);
|
|
MGLOG_I(" GL_MAX_COMBINED_IMAGE_UNIFORMS: %d", caps.MaxCombinedImageUniforms);
|
|
MGLOG_I(" GL_MAX_VERTEX_IMAGE_UNIFORMS: %d", caps.MaxVertexImageUniforms);
|
|
MGLOG_I(" GL_MAX_GEOMETRY_IMAGE_UNIFORMS: %d", caps.MaxGeometryImageUniforms);
|
|
MGLOG_I(" GL_MAX_FRAGMENT_IMAGE_UNIFORMS: %d", caps.MaxFragmentImageUniforms);
|
|
MGLOG_I(" GL_MAX_COMPUTE_IMAGE_UNIFORMS: %d", caps.MaxComputeImageUniforms);
|
|
MGLOG_I(" GL_MAX_DRAW_BUFFERS: %d", caps.MaxDrawBuffers);
|
|
MGLOG_I(" GL_MAX_COLOR_ATTACHMENTS: %d", caps.MaxColorAttachments);
|
|
MGLOG_I(" GL_MAX_CLIP_DISTANCES: %d", caps.MaxClipDistances);
|
|
MGLOG_I(" GL_MAX_VIEWPORTS: %d", caps.MaxViewports);
|
|
MGLOG_I(" GL_MAX_VIEWPORT_DIMS: [%d, %d]", caps.MaxViewportWidth, caps.MaxViewportHeight);
|
|
MGLOG_I(" GL_VIEWPORT_BOUNDS_RANGE: [%.3f, %.3f]", caps.ViewportBoundsRangeMin,
|
|
caps.ViewportBoundsRangeMax);
|
|
MGLOG_I(" GL_VIEWPORT_SUBPIXEL_BITS: %d", caps.ViewportSubpixelBits);
|
|
|
|
caps.IndirectDrawInstanceIdIncludesBaseInstance =
|
|
ProbeIndirectInstanceIdIncludesBaseInstance(caps, glesFuncs);
|
|
MGLOG_I(" Indirect draw gl_InstanceID includes baseInstance: %s",
|
|
caps.IndirectDrawInstanceIdIncludesBaseInstance ? "true" : "false");
|
|
|
|
caps.IsAngleRenderer = caps.GLESRendererString.find("ANGLE") != String::npos;
|
|
caps.IsAngleLlvmpipeRenderer =
|
|
caps.IsAngleRenderer && caps.GLESRendererString.find("llvmpipe") != String::npos;
|
|
caps.AvoidSamplerMipmapMinFilter =
|
|
caps.IsAngleLlvmpipeRenderer && MG_Config::Features.AvoidSamplerMipmapMinFilter;
|
|
MGLOG_I(" GL_EXT_disjoint_timer_query supported: %s",
|
|
caps.SupportsDisjointTimerQuery ? "true" : "false");
|
|
MGLOG_I(" ANGLE renderer: %s", caps.IsAngleRenderer ? "true" : "false");
|
|
MGLOG_I(" ANGLE llvmpipe renderer: %s", caps.IsAngleLlvmpipeRenderer ? "true" : "false");
|
|
MGLOG_I(" Avoid sampler mipmap min filter: %s",
|
|
caps.AvoidSamplerMipmapMinFilter ? "true" : "false");
|
|
|
|
return true;
|
|
}
|
|
} // namespace MobileGL::MG_Util::BackendLoader
|