Merge the 2021-09-05 SPL branch from AOSP-Partner
* security-aosp-pi-release:
Do not modify vector after getting references
Change-Id: I6be06a35a91061480826da9b4be5c34937479aee
diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h
index c5b57c7..fb682ec 100644
--- a/libs/binder/include/binder/AppOpsManager.h
+++ b/libs/binder/include/binder/AppOpsManager.h
@@ -94,7 +94,32 @@
OP_PROCESS_OUTGOING_CALLS = 54,
OP_USE_FINGERPRINT = 55,
OP_BODY_SENSORS = 56,
+ OP_READ_CELL_BROADCASTS = 57,
+ OP_MOCK_LOCATION = 58,
+ OP_READ_EXTERNAL_STORAGE = 59,
+ OP_WRITE_EXTERNAL_STORAGE = 60,
+ OP_TURN_SCREEN_ON = 61,
+ OP_GET_ACCOUNTS = 62,
+ OP_RUN_IN_BACKGROUND = 63,
OP_AUDIO_ACCESSIBILITY_VOLUME = 64,
+ OP_READ_PHONE_NUMBERS = 65,
+ OP_REQUEST_INSTALL_PACKAGES = 66,
+ OP_PICTURE_IN_PICTURE = 67,
+ OP_INSTANT_APP_START_FOREGROUND = 68,
+ OP_ANSWER_PHONE_CALLS = 69,
+ OP_RUN_ANY_IN_BACKGROUND = 70,
+ OP_CHANGE_WIFI_STATE = 71,
+ OP_REQUEST_DELETE_PACKAGES = 72,
+ OP_BIND_ACCESSIBILITY_SERVICE = 73,
+ OP_ACCEPT_HANDOVER = 74,
+ OP_MANAGE_IPSEC_TUNNELS = 75,
+ OP_START_FOREGROUND = 76,
+ OP_BLUETOOTH_SCAN = 77,
+ OP_BLUETOOTH_CHANGE = 78,
+ OP_BOOT_COMPLETED = 79,
+ OP_NFC_CHANGE = 80,
+ OP_DATA_CONNECT_CHANGE = 81,
+ OP_SU = 82
};
AppOpsManager();
diff --git a/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp
index 49ffc8f..651b7f6 100644
--- a/libs/nativewindow/AHardwareBuffer.cpp
+++ b/libs/nativewindow/AHardwareBuffer.cpp
@@ -23,6 +23,7 @@
#include <memory>
#include <cutils/native_handle.h>
+#include <cutils/properties.h>
#include <log/log.h>
#include <utils/StrongPointer.h>
#include <ui/GraphicBuffer.h>
@@ -31,6 +32,8 @@
#include <private/android/AHardwareBufferHelpers.h>
#include <android/hardware/graphics/common/1.1/types.h>
+#include "../../opengl/libs/gles_workarounds.h"
+
static constexpr int kFdBufferSize = 128 * sizeof(int); // 128 ints
@@ -67,6 +70,17 @@
return BAD_VALUE;
}
+ // Refuse creating buffers that are proven to be unreliable (by CTS), unless
+ // the user opts in for experimental features.
+ if (!FP2GLESWorkarounds::isExperimentalGLES3Enabled()) {
+ // SingleLayer_ColorTest_GpuColorOutputCpuRead_R8G8B8_UNORM
+ if (desc->layers == 1 && desc->format == AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM) {
+ ALOGW("Refusing to create buffer with unreliable configuration (R8G8B8_UNORM) while "
+ "experimental GLES3 support is disabled.");
+ return BAD_VALUE;
+ }
+ }
+
uint64_t usage = AHardwareBuffer_convertToGrallocUsageBits(desc->usage);
sp<GraphicBuffer> gbuffer(new GraphicBuffer(
desc->width, desc->height, format, desc->layers, usage,
diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp
index 5fbb3b2..2e5a876 100644
--- a/libs/nativewindow/Android.bp
+++ b/libs/nativewindow/Android.bp
@@ -68,6 +68,10 @@
"libgrallocusage",
],
+ whole_static_libs: [
+ "libgles_workarounds",
+ ],
+
header_libs: [
"libnativebase_headers",
],
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index 5200545..481bd7c 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -20,6 +20,8 @@
#include <sys/types.h>
#include <cutils/native_handle.h>
+#include <cutils/properties.h>
+#include <log/log.h>
#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>
@@ -67,6 +69,13 @@
while (n) {
n--;
reply.read(s);
+ if (s.getType() == Sensor::TYPE_GYROSCOPE &&
+ !property_get_bool("persist.fp2.use_gyroscope", false)) {
+ ALOGW("BpSensorServer::getSensorList: Skipping sensor with insufficient precision: "
+ "\"%s\", vendor \"%s\"",
+ s.getName().c_str(), s.getVendor().c_str());
+ continue;
+ }
v.add(s);
}
return v;
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index 2383516..e742bec 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -55,7 +55,10 @@
// Set fifo event count zero for older devices which do not support batching. Fused
// sensors also have their fifo counts set to zero.
- if (halVersion > SENSORS_DEVICE_API_VERSION_1_0) {
+ if (halVersion > SENSORS_DEVICE_API_VERSION_1_0
+ // HANDLE_ACCELERATION == 0. FP2 accelerometer doesn't support batching properly.
+ && mHandle != 0
+ ) {
mFifoReservedEventCount = hwSensor.fifoReservedEventCount;
mFifoMaxEventCount = hwSensor.fifoMaxEventCount;
} else {
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index ff9d19e..f763496 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -23,6 +23,7 @@
cflags: [
"-Wall",
"-Werror",
+ "-DADDNL_GRALLOC_10_USAGE_BITS=0x02000000U",
],
cppflags: [
"-Weverything",
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index b92cbf3..918251f 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -45,6 +45,12 @@
// TODO(b/72323293, b/72703005): Remove these additional bits
bits = bits | (1 << 10) | (1 << 13);
+#ifdef ADDNL_GRALLOC_10_USAGE_BITS
+ uint64_t addnl_bits = static_cast<uint64_t>(ADDNL_GRALLOC_10_USAGE_BITS);
+ ALOGI("Adding additional valid usage bits: 0x%" PRIx64, addnl_bits);
+ bits = bits | addnl_bits;
+#endif
+
return bits;
}();
return valid10UsageBits;
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index d43c164..114319a 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -83,6 +83,14 @@
include_dirs: ["bionic/libc/private"],
}
+cc_library_static {
+ name: "libgles_workarounds",
+ defaults: ["gl_libs_defaults"],
+ srcs: [
+ "gles_workarounds.cpp"
+ ]
+}
+
//##############################################################################
// Build META EGL library
//
@@ -104,6 +112,9 @@
"libnativewindow",
"libbacktrace",
],
+ static_libs: [
+ "libgles_workarounds",
+ ],
target: {
vendor: {
exclude_shared_libs: ["libgraphicsenv"],
@@ -181,6 +192,9 @@
},
},
shared_libs: ["libEGL"],
+ static_libs: [
+ "libgles_workarounds",
+ ],
}
//##############################################################################
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index 91a3455..1e8d7ba 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -1,5 +1,6 @@
/*
** Copyright 2007, The Android Open Source Project
+ ** Copyright 2018-2020, Fairphone B.V.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -25,6 +26,7 @@
#include <dlfcn.h>
#include <android/dlext.h>
+#include <cutils/compiler.h>
#include <cutils/properties.h>
#include <log/log.h>
@@ -35,6 +37,7 @@
#include "egl_trace.h"
#include "egldefs.h"
+#include "gles_workarounds.h"
extern "C" {
android_namespace_t* android_get_exported_namespace(const char*);
@@ -480,6 +483,30 @@
return nullptr;
}
+namespace
+{
+
+typedef EGLBoolean (*eglGetConfigAttrib_func_t)(
+ EGLDisplay display, EGLConfig config, EGLint attribute, EGLint * value);
+
+eglGetConfigAttrib_func_t eglGetConfigAttrib_func_ptr = nullptr;
+
+EGLBoolean eglGetConfigAttrib_wrapper(EGLDisplay display,
+ EGLConfig config,
+ EGLint attribute,
+ EGLint * value)
+{
+ if (CC_UNLIKELY(!eglGetConfigAttrib_func_ptr)) {
+ return EGL_FALSE;
+ }
+
+ const EGLBoolean result = eglGetConfigAttrib_func_ptr(display, config, attribute, value);
+
+ return FP2GLESWorkarounds::eglGetConfigAttrib(display, config, attribute, value, result);
+}
+
+}
+
void *Loader::load_driver(const char* kind,
egl_connection_t* cnx, uint32_t mask)
{
@@ -510,6 +537,8 @@
char const * const * api = egl_names;
while (*api) {
char const * name = *api;
+ // wrap eglGetConfigAttrib(), see eglGetConfigAttrib_wrapper()
+ const bool loading_eglGetConfigAttrib = (strcmp(name, "eglGetConfigAttrib") == 0);
__eglMustCastToProperFunctionPointerType f =
(__eglMustCastToProperFunctionPointerType)dlsym(dso, name);
if (f == NULL) {
@@ -519,6 +548,10 @@
f = (__eglMustCastToProperFunctionPointerType)0;
}
}
+ if (f && loading_eglGetConfigAttrib) {
+ eglGetConfigAttrib_func_ptr = (eglGetConfigAttrib_func_t)f;
+ f = (__eglMustCastToProperFunctionPointerType)&eglGetConfigAttrib_wrapper;
+ }
*curr++ = f;
api++;
}
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index f53cf3f..5eb1ee6 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -111,6 +111,32 @@
// ----------------------------------------------------------------------------
+void egl_set_framework_error_for_currrent_context(GLenum error) {
+ EGLContext context = egl_tls_t::getContext();
+ if (context == EGL_NO_CONTEXT)
+ return;
+
+ egl_context_t * const c = get_context(context);
+ if (c == NULL) // this should never happen, by construction
+ return;
+
+ c->setFrameworkGLError(error);
+}
+
+GLenum egl_get_reset_framework_error_for_current_context() {
+ EGLContext context = egl_tls_t::getContext();
+ if (context == EGL_NO_CONTEXT)
+ return 0;
+
+ egl_context_t * const c = get_context(context);
+ if (c == NULL) // this should never happen, by construction
+ return 0;
+
+ return c->getResetFrameworkGLError();
+}
+
+// ----------------------------------------------------------------------------
+
const GLubyte * egl_get_string_for_current_context(GLenum name) {
// NOTE: returning NULL here will fall-back to the default
// implementation.
diff --git a/opengl/libs/EGL/egl_object.cpp b/opengl/libs/EGL/egl_object.cpp
index f879254..306352e 100644
--- a/opengl/libs/EGL/egl_object.cpp
+++ b/opengl/libs/EGL/egl_object.cpp
@@ -18,6 +18,8 @@
#include <sstream>
+#include "../gles_workarounds.h"
+
// ----------------------------------------------------------------------------
namespace android {
@@ -281,7 +283,8 @@
egl_context_t::egl_context_t(EGLDisplay dpy, EGLContext context, EGLConfig config,
egl_connection_t const* cnx, int version) :
egl_object_t(get_display_nowake(dpy)), dpy(dpy), context(context),
- config(config), read(0), draw(0), cnx(cnx), version(version) {
+ config(config), read(0), draw(0), cnx(cnx), version(version),
+ frameworkGLError(0) {
}
void egl_context_t::onLooseCurrent() {
@@ -318,10 +321,27 @@
while (ss >> str) {
tokenized_gl_extensions.push_back(str);
}
+
+ FP2GLESWorkarounds::filterEGLContextExtensions(
+ &gl_extensions,
+ &tokenized_gl_extensions
+ );
}
}
}
+
+void egl_context_t::setFrameworkGLError(int32_t error) {
+ frameworkGLError = error;
+}
+
+int32_t egl_context_t::getResetFrameworkGLError() {
+ const int32_t currentError = frameworkGLError;
+ frameworkGLError = 0;
+ return currentError;
+}
+
+
// ----------------------------------------------------------------------------
}; // namespace android
// ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_object.h b/opengl/libs/EGL/egl_object.h
index 4e1de5c..c6f478e 100644
--- a/opengl/libs/EGL/egl_object.h
+++ b/opengl/libs/EGL/egl_object.h
@@ -198,6 +198,15 @@
void onLooseCurrent();
void onMakeCurrent(EGLSurface draw, EGLSurface read);
+ // Store GL error flag set by the framework.
+ // Used for working around driver issues.
+ // This uses int32_t instead of GLenum, so that we don't depend on
+ // GL headers here.
+ void setFrameworkGLError(int32_t error);
+ // As per specification of glGetError(), retrieving the error flag
+ // also resets it.
+ int32_t getResetFrameworkGLError();
+
EGLDisplay dpy;
EGLContext context;
EGLConfig config;
@@ -207,6 +216,9 @@
int version;
std::string gl_extensions;
std::vector<std::string> tokenized_gl_extensions;
+
+private:
+ int32_t frameworkGLError;
};
// ----------------------------------------------------------------------------
diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp
index f7fde96..303b8f0 100644
--- a/opengl/libs/GLES2/gl2.cpp
+++ b/opengl/libs/GLES2/gl2.cpp
@@ -1,5 +1,6 @@
/*
** Copyright 2007, The Android Open Source Project
+ ** Copyright 2018-2020, Fairphone B.V.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -18,12 +19,14 @@
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
+#include <vector>
#include <log/log.h>
#include <cutils/properties.h>
#include "../hooks.h"
#include "../egl_impl.h"
+#include "../gles_workarounds.h"
using namespace android;
@@ -292,12 +295,49 @@
*/
extern "C" {
+ GLenum __glGetError();
const GLubyte * __glGetString(GLenum name);
const GLubyte * __glGetStringi(GLenum name, GLuint index);
void __glGetBooleanv(GLenum pname, GLboolean * data);
void __glGetFloatv(GLenum pname, GLfloat * data);
void __glGetIntegerv(GLenum pname, GLint * data);
void __glGetInteger64v(GLenum pname, GLint64 * data);
+ void __glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
+ GLsizei height, GLenum format, GLenum type, const void *pixels);
+ void __glTexImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width,
+ GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type,
+ const void *pixels);
+ void __glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
+ GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type,
+ const void *pixels);
+ void __glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width,
+ GLsizei height);
+ void __glShaderSource(GLuint shader, GLsizei count, const GLchar *const*string,
+ const GLint *length);
+
+}
+
+namespace
+{
+
+template<typename T>
+void getAliasedPointSizeRange(T * data) {
+ data[0] = static_cast<T>(FP2GLESWorkarounds::GL_ALIASED_POINT_SIZE_MIN);
+ data[1] = static_cast<T>(FP2GLESWorkarounds::GL_ALIASED_POINT_SIZE_MAX);
+}
+
+}
+
+GLenum glGetError() {
+ // As per specification of glGetError(), it resets the error flag when being
+ // called. So make sure to always fetch and reset both the framework and
+ // driver error flag.
+ const GLenum frameworkError =
+ egl_get_reset_framework_error_for_current_context();
+ const GLenum driverError = __glGetError();
+
+ // Framework-defined errors override driver errors.
+ return frameworkError != 0 ? frameworkError : driverError;
}
const GLubyte * glGetString(GLenum name) {
@@ -306,7 +346,8 @@
gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
if(_c) ret = _c->glGetString(name);
}
- return ret;
+
+ return FP2GLESWorkarounds::glGetString(name, ret);
}
const GLubyte * glGetStringi(GLenum name, GLuint index) {
@@ -340,6 +381,11 @@
}
}
+ if (pname == GL_ALIASED_POINT_SIZE_RANGE) {
+ getAliasedPointSizeRange(data);
+ return;
+ }
+
gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
if (_c) _c->glGetFloatv(pname, data);
}
@@ -353,6 +399,11 @@
}
}
+ if (pname == GL_ALIASED_POINT_SIZE_RANGE) {
+ getAliasedPointSizeRange(data);
+ return;
+ }
+
gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
if (_c) _c->glGetIntegerv(pname, data);
}
@@ -366,6 +417,109 @@
}
}
+ if (pname == GL_ALIASED_POINT_SIZE_RANGE) {
+ getAliasedPointSizeRange(data);
+ return;
+ }
+
gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
if (_c) _c->glGetInteger64v(pname, data);
}
+
+void glTexImage2D(GLenum target, GLint level,GLint internalformat, GLsizei width, GLsizei height,
+ GLint border, GLenum format, GLenum type, const void *pixels) {
+ const bool isValidRequest = FP2GLESWorkarounds::checkValidGlTexImage2D(
+ target, level, internalformat, width, height, border, format, type, pixels);
+ if (isValidRequest) {
+ __glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels);
+ }
+}
+
+void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width,
+ GLsizei height, GLenum format, GLenum type, const void *pixels) {
+ const bool isValidRequest = FP2GLESWorkarounds::checkValidGlTexSubImage2D(
+ target, level, xoffset, yoffset, width, height, format, type, pixels);
+ if (isValidRequest) {
+ __glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels);
+ }
+}
+
+void glTexImage3D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height,
+ GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) {
+ const bool isValidRequest = FP2GLESWorkarounds::checkValidGlTexImage3D(
+ target, level, internalformat, width, height, depth, border, format, type, pixels);
+ if (isValidRequest) {
+ __glTexImage3D(target, level, internalformat, width, height, depth, border, format, type,
+ pixels);
+ }
+}
+
+void glTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset,
+ GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) {
+ const bool isValidRequest = FP2GLESWorkarounds::checkValidGlTexSubImage3D(
+ target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
+ if (isValidRequest) {
+ __glTexSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format,
+ type, pixels);
+ }
+}
+
+void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
+ const bool isValidRequest = FP2GLESWorkarounds::checkValidGlRenderbufferStorage(
+ target, internalformat, width, height);
+ if (isValidRequest) {
+ __glRenderbufferStorage(target, internalformat, width, height);
+ }
+}
+
+
+void glShaderSource(const GLuint shader, const GLsizei count, const GLchar *const*string,
+ const GLint *length) {
+
+ if (count < 0 || !string) {
+ // Let the driver handle error reporting for invalid parameters.
+ __glShaderSource(shader, count, string, length);
+ return;
+ }
+
+ bool anyModified = false;
+ std::vector<std::string> modifiedStrings(count);
+ std::vector<const GLchar*> modifiedStringPtrs(count, nullptr);
+ std::vector<GLint> modifiedLengths(count);
+ for (GLsizei i = 0; i < count; ++i) {
+ const GLsizei inputLength = length ? length[i] : -1;
+ const GLchar *const inputString = string[i];
+ // default-initialize with unmodified values
+ modifiedLengths[i] = inputLength;
+ modifiedStringPtrs[i] = inputString;
+
+ if (!inputString) {
+ // This case would be a bug. Let the driver handle it.
+ continue;
+ }
+ std::string str;
+ if (inputLength > 0) {
+ // Length is explicitly defined.
+ str = std::string(inputString, inputLength);
+ } else if (inputLength < 0) {
+ // String is null-terminated.
+ str = std::string(inputString);
+ } else {
+ // Unexpected, probably also a bug on application-side.
+ continue;
+ }
+ if (FP2GLESWorkarounds::adjustShaderString(str)) {
+ modifiedLengths[i] = str.length();
+ modifiedStrings[i] = std::move(str);
+ modifiedStringPtrs[i] = modifiedStrings[i].c_str();
+ anyModified = true;
+ }
+ }
+
+ if (!anyModified) {
+ __glShaderSource(shader, count, string, length);
+ } else {
+ __glShaderSource(shader, count,
+ modifiedStringPtrs.data(), modifiedLengths.data());
+ }
+}
diff --git a/opengl/libs/GLES2/gl2_api.in b/opengl/libs/GLES2/gl2_api.in
index a331572..dfb2ac5 100644
--- a/opengl/libs/GLES2/gl2_api.in
+++ b/opengl/libs/GLES2/gl2_api.in
@@ -178,7 +178,7 @@
void API_ENTRY(glGetBufferParameteriv)(GLenum target, GLenum pname, GLint *params) {
CALL_GL_API(glGetBufferParameteriv, target, pname, params);
}
-GLenum API_ENTRY(glGetError)(void) {
+GLenum API_ENTRY(__glGetError)(void) {
CALL_GL_API_RETURN(glGetError);
}
void API_ENTRY(__glGetFloatv)(GLenum pname, GLfloat *data) {
@@ -280,7 +280,7 @@
void API_ENTRY(glReleaseShaderCompiler)(void) {
CALL_GL_API(glReleaseShaderCompiler);
}
-void API_ENTRY(glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
+void API_ENTRY(__glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) {
CALL_GL_API(glRenderbufferStorage, target, internalformat, width, height);
}
void API_ENTRY(glSampleCoverage)(GLfloat value, GLboolean invert) {
@@ -292,7 +292,7 @@
void API_ENTRY(glShaderBinary)(GLsizei count, const GLuint *shaders, GLenum binaryformat, const void *binary, GLsizei length) {
CALL_GL_API(glShaderBinary, count, shaders, binaryformat, binary, length);
}
-void API_ENTRY(glShaderSource)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) {
+void API_ENTRY(__glShaderSource)(GLuint shader, GLsizei count, const GLchar *const*string, const GLint *length) {
CALL_GL_API(glShaderSource, shader, count, string, length);
}
void API_ENTRY(glStencilFunc)(GLenum func, GLint ref, GLuint mask) {
@@ -313,7 +313,7 @@
void API_ENTRY(glStencilOpSeparate)(GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass) {
CALL_GL_API(glStencilOpSeparate, face, sfail, dpfail, dppass);
}
-void API_ENTRY(glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) {
+void API_ENTRY(__glTexImage2D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels) {
CALL_GL_API(glTexImage2D, target, level, internalformat, width, height, border, format, type, pixels);
}
void API_ENTRY(glTexParameterf)(GLenum target, GLenum pname, GLfloat param) {
@@ -328,7 +328,7 @@
void API_ENTRY(glTexParameteriv)(GLenum target, GLenum pname, const GLint *params) {
CALL_GL_API(glTexParameteriv, target, pname, params);
}
-void API_ENTRY(glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) {
+void API_ENTRY(__glTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels) {
CALL_GL_API(glTexSubImage2D, target, level, xoffset, yoffset, width, height, format, type, pixels);
}
void API_ENTRY(glUniform1f)(GLint location, GLfloat v0) {
@@ -430,10 +430,10 @@
void API_ENTRY(glDrawRangeElements)(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices) {
CALL_GL_API(glDrawRangeElements, mode, start, end, count, type, indices);
}
-void API_ENTRY(glTexImage3D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) {
+void API_ENTRY(__glTexImage3D)(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels) {
CALL_GL_API(glTexImage3D, target, level, internalformat, width, height, depth, border, format, type, pixels);
}
-void API_ENTRY(glTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) {
+void API_ENTRY(__glTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels) {
CALL_GL_API(glTexSubImage3D, target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels);
}
void API_ENTRY(glCopyTexSubImage3D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height) {
diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp
index bacd4b4..562f923 100644
--- a/opengl/libs/GLES_CM/gl.cpp
+++ b/opengl/libs/GLES_CM/gl.cpp
@@ -1,5 +1,6 @@
/*
** Copyright 2007, The Android Open Source Project
+ ** Copyright 2018-2020, Fairphone B.V.
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
@@ -27,6 +28,7 @@
#include "../hooks.h"
#include "../egl_impl.h"
+#include "../gles_workarounds.h"
using namespace android;
@@ -352,5 +354,6 @@
gl_hooks_t::gl_t const * const _c = &getGlThreadSpecific()->gl;
ret = _c->glGetString(name);
}
- return ret;
+
+ return FP2GLESWorkarounds::glGetString(name, ret);
}
diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h
index a8855ef..981239f 100644
--- a/opengl/libs/egl_impl.h
+++ b/opengl/libs/egl_impl.h
@@ -27,6 +27,11 @@
namespace android {
// ----------------------------------------------------------------------------
+// Allow framework to set GL error state on unreliable driver implementations.
+EGLAPI void egl_set_framework_error_for_currrent_context(GLenum error);
+// Get and reset the current GL error flag defined by the framework.
+EGLAPI GLenum egl_get_reset_framework_error_for_current_context();
+
EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name);
EGLAPI const GLubyte * egl_get_string_for_current_context(GLenum name, GLuint index);
EGLAPI GLint egl_get_num_extensions_for_current_context();
diff --git a/opengl/libs/gles_workarounds.cpp b/opengl/libs/gles_workarounds.cpp
new file mode 100644
index 0000000..5283741
--- /dev/null
+++ b/opengl/libs/gles_workarounds.cpp
@@ -0,0 +1,427 @@
+#include "gles_workarounds.h"
+
+#include <cstring>
+#include <regex>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <cutils/properties.h>
+#include <log/log.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <GLES3/gl3.h>
+
+#include "egl_impl.h"
+
+namespace
+{
+
+bool fp2_experimental_gles3 = false;
+
+bool check_fp2_experimental_gles3()
+{
+ static const char s_OpenGLESv30[] = "196608";
+ char prop[PROPERTY_VALUE_MAX];
+ property_get("ro.opengles.version", prop, "");
+ return strncmp(prop, s_OpenGLESv30, PROPERTY_VALUE_MAX) == 0;
+}
+
+void early_gles_workarounds_init()
+{
+ fp2_experimental_gles3 = check_fp2_experimental_gles3();
+}
+
+pthread_once_t once_control = PTHREAD_ONCE_INIT;
+const int sEarlyInitState = pthread_once(&once_control, &early_gles_workarounds_init);
+
+
+const std::vector<std::string> & unstableGLExtension()
+{
+ static const std::vector<std::string> exts = {
+ "GL_EXT_sRGB",
+ "GL_EXT_sRGB_write_control",
+ "GL_EXT_texture_sRGB_decode",
+ };
+ return exts;
+}
+
+const std::regex & unstableGLExtRegex()
+{
+ static const std::regex r = []() {
+ // Construct regex: match all unstable extension names.
+ std::string regexStr;
+ for (size_t i = 0; i < unstableGLExtension().size(); ++i) {
+ regexStr.append(unstableGLExtension()[i]);
+ if (i != unstableGLExtension().size() - 1) {
+ regexStr.append("|");
+ }
+ }
+ // Match whole words only and remove redundant space.
+ regexStr = "\\b(" + regexStr + ")\\b[ ]?";
+ return std::regex(regexStr);
+ }();
+ return r;
+}
+
+std::string filterStableExtensions(const std::string & extensions)
+{
+ return std::regex_replace(
+ extensions,
+ unstableGLExtRegex(),
+ "");
+}
+
+// Check for incompletely supported sRGB formats for textures.
+bool isValidTextureFormat(GLenum format, GLenum internalformat) {
+ if (fp2_experimental_gles3) {
+ // Experimental mode: Expose everything the driver implements.
+ return true;
+ }
+
+ // The "GL_SRGB*_EXT" enums below are introduced by the extension
+ // GL_EXT_sRGB. They are available as identical, non-*_EXT enum in GLES3 as
+ // well. GL_SRGB8 is available through GLES3 only.
+
+ if (format == GL_SRGB_EXT || format == GL_SRGB_ALPHA_EXT) {
+ return false;
+ }
+
+ if (internalformat == GL_SRGB8 || internalformat == GL_SRGB8_ALPHA8_EXT
+ || internalformat == GL_SRGB_EXT || internalformat == GL_SRGB_ALPHA_EXT) {
+ return false;
+ }
+
+ return true;
+}
+
+// Check for incompletely supported sRGB formats for renderbuffers.
+bool isValidRenderbufferFormat(GLenum internalformat) {
+ if (fp2_experimental_gles3) {
+ return true;
+ }
+
+ // GL_EXT_sRGB adds GL_SRGB8_ALPHA8_EXT for renderbuffers. All other
+ // GL_SRGB* enums get rejected by the driver, if used on renderbuffers.
+ if (internalformat == GL_SRGB8_ALPHA8_EXT) {
+ return false;
+ }
+
+ return true;
+}
+
+
+// The following two functions are taken from system/core/base/strings.cpp for
+// convenience. See ./Android.bp for comments on not adding additional link
+// dependencies.
+
+std::vector<std::string> SplitString(const std::string& s,
+ const std::string& delimiters) {
+ if (delimiters.size() == 0u) {
+ return {};
+ }
+
+ std::vector<std::string> result;
+
+ size_t base = 0;
+ size_t found;
+ while (true) {
+ found = s.find_first_of(delimiters, base);
+ result.push_back(s.substr(base, found - base));
+ if (found == s.npos) break;
+ base = found + 1;
+ }
+
+ return result;
+}
+
+template <typename ContainerT, typename SeparatorT>
+std::string JoinStrings(const ContainerT& things, SeparatorT separator) {
+ if (things.empty()) {
+ return "";
+ }
+
+ std::ostringstream result;
+ result << *things.begin();
+ for (auto it = std::next(things.begin()); it != things.end(); ++it) {
+ result << separator << *it;
+ }
+ return result.str();
+}
+
+}
+
+
+bool FP2GLESWorkarounds::isExperimentalGLES3Enabled()
+{
+ return fp2_experimental_gles3;
+}
+
+EGLBoolean FP2GLESWorkarounds::eglGetConfigAttrib(
+ const EGLDisplay /* display */,
+ const EGLConfig /* config */,
+ const EGLint attribute,
+ EGLint * value,
+ const EGLBoolean driverResult)
+{
+ if (!value) {
+ return driverResult;
+ }
+
+ if ((attribute == EGL_RENDERABLE_TYPE) && !fp2_experimental_gles3) {
+ *value = *value & ~EGL_OPENGL_ES3_BIT_KHR;
+ }
+
+ return driverResult;
+}
+
+const GLubyte * FP2GLESWorkarounds::glGetString(const GLenum name, const GLubyte * driverString)
+{
+ if (fp2_experimental_gles3) {
+ return driverString;
+ }
+
+ if (name == GL_VERSION) {
+ struct VersionStringReplacement {
+ const char * broken_version_prefix;
+ const size_t check_length;
+ const char * fake_version_string;
+ };
+ const VersionStringReplacement versionStringReplacements[] = {
+ { "OpenGL ES 3.", 12, "OpenGL ES 2.0" },
+ { "OpenGL ES-CM 3.", 15, "OpenGL ES-CM 2.0" },
+ { NULL, 0, NULL }
+ };
+ const VersionStringReplacement * r = versionStringReplacements;
+ for (; r->broken_version_prefix != NULL; ++r) {
+ if (strncmp((const char *)driverString,
+ r->broken_version_prefix, r->check_length) == 0) {
+ return (const GLubyte *)r->fake_version_string;
+ }
+ }
+ return driverString;
+ }
+
+ return driverString;
+}
+
+
+bool FP2GLESWorkarounds::checkValidGlTexImage2D(GLenum /*target*/,
+ GLint /*level*/,
+ GLint internalformat,
+ GLsizei /*width*/,
+ GLsizei /*height*/,
+ GLint /*border*/,
+ GLenum format,
+ GLenum /*type*/,
+ const void * /*data*/)
+{
+ if (!isValidTextureFormat(format, internalformat)) {
+ // glTexImage2D and related report GL_INVALID_OPERATION if format,
+ // internal format and type don't match the expected combinations.
+ android::egl_set_framework_error_for_currrent_context(GL_INVALID_OPERATION);
+ return false;
+ }
+
+ return true;
+}
+
+bool FP2GLESWorkarounds::checkValidGlTexSubImage2D(GLenum /*target*/,
+ GLint /*level*/,
+ GLint /*xoffset*/,
+ GLint /*yoffset*/,
+ GLsizei /*width*/,
+ GLsizei /*height*/,
+ GLenum format,
+ GLenum /*type*/,
+ const void * /*data*/)
+{
+ if (!isValidTextureFormat(format, 0)) {
+ android::egl_set_framework_error_for_currrent_context(GL_INVALID_OPERATION);
+ return false;
+ }
+
+ return true;
+}
+
+bool FP2GLESWorkarounds::checkValidGlTexImage3D(GLenum /*target*/, GLint /*level*/,
+ GLint internalformat, GLsizei /*width*/, GLsizei /*height*/, GLsizei /*depth*/,
+ GLint /*border*/, GLenum format, GLenum /*type*/, const void */*pixels*/) {
+ if (!isValidTextureFormat(format, internalformat)) {
+ android::egl_set_framework_error_for_currrent_context(GL_INVALID_OPERATION);
+ return false;
+ }
+ return true;
+}
+
+bool FP2GLESWorkarounds::checkValidGlTexSubImage3D(GLenum /*target*/, GLint /*level*/,
+ GLint /*xoffset*/, GLint /*yoffset*/, GLint /*zoffset*/, GLsizei /*width*/, GLsizei /*height*/,
+ GLsizei /*depth*/, GLenum format, GLenum /*type*/,
+ const void */*pixels*/) {
+ if (!isValidTextureFormat(format, 0)) {
+ android::egl_set_framework_error_for_currrent_context(GL_INVALID_OPERATION);
+ return false;
+ }
+ return true;
+}
+
+bool FP2GLESWorkarounds::checkValidGlRenderbufferStorage(GLenum /*target*/,
+ GLenum internalformat,
+ GLsizei /*width*/,
+ GLsizei /*height*/)
+{
+ if (!isValidRenderbufferFormat(internalformat)) {
+ // glRenderbufferStorage reports GL_INVALID_ENUM in case of wrong
+ // internal format.
+ android::egl_set_framework_error_for_currrent_context(GL_INVALID_ENUM);
+ return false;
+ }
+
+ return true;
+}
+
+namespace {
+
+enum class FixResult {
+ noMatch,
+ applied,
+ unsupportedCode,
+};
+
+/** Replace a function call on a vec3 by separate calls on each component.
+ *
+ * This helps fixing issues with dFdx/dFdy in GLSL on Adreno 330 that produces
+ * wrong results only when called on a vec3.
+ *
+ * Parameters:
+ * pos: Starting position to search from; will be updated to the end of the
+ * current search.
+ */
+FixResult replace_func_vec3_call(std::string& source, const std::string& funcName, size_t& pos) {
+ const size_t callStartPos = source.find(funcName, pos);
+ if (callStartPos == std::string::npos) {
+ return FixResult::noMatch;
+ }
+ // Find opening and closing brackets
+ pos = callStartPos + funcName.length();
+ for (; pos < source.length(); ++pos) {
+ switch (source[pos]) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ continue;
+ case '(':
+ break;
+ default:
+ return FixResult::unsupportedCode;
+ }
+ break;
+ }
+ if (pos == source.length()) {
+ pos = std::string::npos;
+ return FixResult::unsupportedCode;
+ }
+ const size_t openingPos = pos;
+ const size_t closingPos = source.find(")", openingPos);
+ if (closingPos == std::string::npos) {
+ return FixResult::unsupportedCode;
+ }
+
+ // Extract the parameter part of call like `func(var.xyz)`:
+ const std::string paramStr = source.substr(
+ openingPos + 1, closingPos - openingPos - 1);
+ // Match `some_variable_42.swizzle`, where `swizzle` can use either of the
+ // sets `xyzw`, `rgba` or `stpq` (according to GLSL specs). We are looking
+ // for cases where the result is a vec3, thus typically `.xyz`.
+ static const std::regex paramRegex(
+ R"(\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\.\s*([xyzwrgbastpq]{3,3})\s*)");
+ std::smatch match;
+ if (!std::regex_match(paramStr, match, paramRegex) || (match.size() != 3)) {
+ return FixResult::unsupportedCode;
+ }
+ const std::string& var = match[1];
+ const std::string& swizzle = match[2];
+ const std::string fixedCall = "vec3("
+ + funcName + "(" + var + "." + swizzle[0] + "), "
+ + funcName + "(" + var + "." + swizzle[1] + "), "
+ + funcName + "(" + var + "." + swizzle[2] + "))";
+ source.replace(callStartPos, closingPos - callStartPos + 1, fixedCall);
+ pos = callStartPos + fixedCall.length();
+ return FixResult::applied;
+}
+
+}
+
+bool FP2GLESWorkarounds::adjustShaderString(std::string& shaderString) {
+ // This is an extremely simplistic implementation. To do this properly, we
+ // would need a complete syntax analysis.
+ int appliedCount = 0;
+ int unsupportedCount = 0;
+ auto lines = SplitString(shaderString, "\n\r");
+ for (auto & line : lines) {
+ if (line.length() > 0 && line[0] == '#') {
+ continue;
+ }
+ const auto commentStart = line.find("//");
+ if (commentStart != std::string::npos) {
+ line.resize(commentStart);
+ }
+ auto loopFix = [&appliedCount, &unsupportedCount] (
+ std::string& source, const std::string& funcName)
+ {
+ size_t pos = 0;
+ while (pos != source.length()) {
+ switch (replace_func_vec3_call(source, funcName, pos)) {
+ case FixResult::unsupportedCode:
+ ++unsupportedCount;
+ // fall-through
+ case FixResult::noMatch:
+ return;
+ case FixResult::applied:
+ ++appliedCount;
+ break;
+ }
+ }
+ };
+ loopFix(line, "dFdx");
+ loopFix(line, "dFdy");
+ }
+ if (appliedCount) {
+ shaderString = JoinStrings(lines, "\n");
+ }
+ if (appliedCount != 0 || unsupportedCount != 0) {
+ ALOGI("glShaderSource: Found calls to dFdx/dFdy with vec3 parameter "
+ "(broken on current Adreno 330 driver):");
+ ALOGI(" Fixed cases: %i", appliedCount);
+ ALOGI(" Unsupported code/syntax: %i", unsupportedCount);
+ }
+ return appliedCount != 0;
+}
+
+void FP2GLESWorkarounds::filterEGLContextExtensions(
+ std::string * gl_extensions,
+ std::vector<std::string> * tokenized_gl_extensions)
+{
+ if (fp2_experimental_gles3) {
+ return;
+ }
+
+ // EGL stores extensions both in a string (separated by space) and a vector.
+ // Filter unstable extensions from both lists.
+ for (auto && ext : unstableGLExtension())
+ {
+ const auto it = std::find(
+ tokenized_gl_extensions->begin(),
+ tokenized_gl_extensions->end(),
+ ext + " "
+ );
+ if (it != tokenized_gl_extensions->end()) {
+ tokenized_gl_extensions->erase(it);
+ }
+ }
+ *gl_extensions = filterStableExtensions(*gl_extensions);
+}
diff --git a/opengl/libs/gles_workarounds.h b/opengl/libs/gles_workarounds.h
new file mode 100644
index 0000000..224c51a
--- /dev/null
+++ b/opengl/libs/gles_workarounds.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2020, Fairphone B.V.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+// Do not include the version-specific GL headers here. Instead, forward-declare
+// the required type declarations:
+using EGLBoolean = unsigned int;
+using EGLConfig = void*;
+using EGLDisplay = void*;
+using EGLint = int32_t;
+using GLenum = unsigned int;
+using GLint = int32_t;
+using GLfloat = float;
+using GLsizei = int32_t;
+using GLubyte = uint8_t;
+
+
+/** Workaround broken and incomplete graphics driver behavior on the FP2.
+ *
+ * The OpenGL ES implementation of the driver for the Fairphone FP2 is
+ * incomplete, unstable, and causes various tests to fail on Android 7 and 9.
+ * Falling back to OpenGL ES 2.0 instead of 3.0 is more stable in many
+ * situations. Therefore, OpenGL ES 3.0 is treated as experimental feature from
+ * Android 7 onwards.
+ *
+ * This class gathers fixes and workarounds for bugs and incomplete
+ * implementations in the outdated graphics driver. Based on the
+ * ro.opengles.version system property, it operates in two modes:
+ *
+ * - OpenGL ES 2.0: Hide meta data that hints to OpenGL ES 3.0 support in
+ * strings and feature flags.
+ * - Experimental OpenGL ES 3.0: Keep exposing GLES support as reported by the
+ * driver and only try applying fixes where possible.
+ */
+class FP2GLESWorkarounds {
+public:
+ /** Check whether experimental OpenGL ES 3.0 support is enabled in the system.*/
+ static bool isExperimentalGLES3Enabled();
+
+ // Global GL constants
+
+ /** Actual values for GL_ALIASED_POINT_SIZE_RANGE
+ *
+ * The driver reports range (1.0, 1023.0), but it actually renders points up
+ * to size 4092. This causes conformance tests to fail, because the driver
+ * is supposed to clamp requested point sizes to the range it reports. Fix
+ * this by reporting values that match the actual behavior of the driver.
+ * Test: dEQP-GLES2.functional.rasterization.limits#points
+ */
+ static constexpr GLfloat GL_ALIASED_POINT_SIZE_MIN = 1.f;
+ static constexpr GLfloat GL_ALIASED_POINT_SIZE_MAX = 4092.f;
+
+ /** Hide GLES3 support from context attributes if needed.
+ *
+ * Remove EGL_OPENGL_ES3_BIT_KHR from EGL_RENDERABLE_TYPE unless
+ * experimental GLES3 support is enabled.
+ */
+ static EGLBoolean eglGetConfigAttrib(
+ EGLDisplay display,
+ EGLConfig config,
+ EGLint attribute,
+ EGLint * value,
+ EGLBoolean driverResult);
+
+ /** Adjust driver-reported strings if needed.
+ *
+ * Following changes are made, unless experimental GLES3 support is enabled:
+ * - Adjust version strings to report OpenGL ES 2.0 only.
+ */
+ static const GLubyte * glGetString(GLenum name, const GLubyte * driverString);
+
+
+ /** Check for valid texture parameters and set GL error flag if needed.
+ *
+ * This checks if the requested texture is valid according to the current
+ * set of exposed driver features. If it is not valid, this will set the
+ * value returned by the next call to glGetError() at framework level
+ * accordingly.
+ */
+ static bool checkValidGlTexImage2D(GLenum target, GLint level, GLint internalformat,
+ GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * data);
+ static bool checkValidGlTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+ GLsizei width, GLsizei height, GLenum format, GLenum type, const void * data);
+ static bool checkValidGlTexImage3D(GLenum target, GLint level, GLint internalformat,
+ GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type,
+ const void *pixels);
+ static bool checkValidGlTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
+ GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type,
+ const void *pixels);
+ static bool checkValidGlRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width,
+ GLsizei height);
+
+ /** Apply workarounds to shader sources.
+ *
+ * Modify `shaderString` to fix known issues with Adreno 330 drivers.
+ * `shaderString` must be a copy of one of the strings passed into a call to
+ * `glShaderSource`. It will be modified in-place.
+ *
+ * Returns true if modifications were done.
+ */
+ static bool adjustShaderString(std::string& shaderString);
+
+ /** Filter extension list in internal EGL data structures.
+ *
+ * OpenGL (ES) extensions need to be handled by thread and context. Use
+ * EGL's existing logic for that and filter their internal data structures
+ * directly.
+ *
+ * Note: Add a call to this function in egl_object.cpp directly, so that the
+ * extensions are filtered direct where EGL's internal data structures are
+ * initialized.
+ */
+ static void filterEGLContextExtensions(
+ std::string * gl_extensions,
+ std::vector<std::string> * tokenized_gl_extensions);
+};