Better error handling in the OpenGL renderer.
Add a glGetError() check on every frame
Don't attempt to create textures larger than the maximum size allowed
Change-Id: Iee4afae16089406dbe8bf10fc93b674f1271a0ca
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 11a5d69..e0c4b14 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -148,9 +148,15 @@
void onPreDraw() {
nPrepare(mRenderer);
}
-
+
private native void nPrepare(int renderer);
+ void onPostDraw() {
+ nFinish(mRenderer);
+ }
+
+ private native void nFinish(int renderer);
+
@Override
public boolean acquireContext() {
if (!mContextLocked) {
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 5d85076a..3796994 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -210,7 +210,7 @@
* is invoked and the requested flag is turned off. The error code is
* also logged as a warning.
*/
- void checkErrors() {
+ void checkEglErrors() {
if (isEnabled()) {
int error = sEgl.eglGetError();
if (error != EGL10.EGL_SUCCESS) {
@@ -221,7 +221,7 @@
// we'll try again if it was context lost
setRequested(false);
}
- Log.w(LOG_TAG, "OpenGL error: " + error);
+ Log.w(LOG_TAG, "EGL error: " + Integer.toHexString(error));
}
}
}
@@ -348,7 +348,7 @@
void initializeIfNeeded(int width, int height, View.AttachInfo attachInfo,
SurfaceHolder holder) {
if (isRequested()) {
- checkErrors();
+ checkEglErrors();
super.initializeIfNeeded(width, height, attachInfo, holder);
}
}
@@ -386,6 +386,9 @@
void onPreDraw() {
}
+ void onPostDraw() {
+ }
+
/**
* Defines the EGL configuration for this renderer. The default configuration
* is RGBX, no depth, no stencil.
@@ -418,10 +421,12 @@
canvas.restoreToCount(saveCount);
}
+ onPostDraw();
+
attachInfo.mIgnoreDirtyState = false;
sEgl.eglSwapBuffers(sEglDisplay, mEglSurface);
- checkErrors();
+ checkEglErrors();
}
}
@@ -570,6 +575,11 @@
mGlCanvas.onPreDraw();
}
+ @Override
+ void onPostDraw() {
+ mGlCanvas.onPostDraw();
+ }
+
static HardwareRenderer create(boolean translucent) {
if (GLES20Canvas.isAvailable()) {
return new Gl20Renderer(translucent);
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index aa71746..a61a9d1 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -84,6 +84,11 @@
renderer->prepare();
}
+static void android_view_GLES20Canvas_finish(JNIEnv* env, jobject canvas,
+ OpenGLRenderer* renderer) {
+ renderer->finish();
+}
+
static void android_view_GLES20Canvas_acquireContext(JNIEnv* env, jobject canvas,
OpenGLRenderer* renderer) {
renderer->acquireContext();
@@ -384,6 +389,7 @@
{ "nDestroyRenderer", "(I)V", (void*) android_view_GLES20Canvas_destroyRenderer },
{ "nSetViewport", "(III)V", (void*) android_view_GLES20Canvas_setViewport },
{ "nPrepare", "(I)V", (void*) android_view_GLES20Canvas_prepare },
+ { "nFinish", "(I)V", (void*) android_view_GLES20Canvas_finish },
{ "nAcquireContext", "(I)V", (void*) android_view_GLES20Canvas_acquireContext },
{ "nReleaseContext", "(I)V", (void*) android_view_GLES20Canvas_releaseContext },
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 1a18766..39c3111 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -110,11 +110,9 @@
layer = new Layer;
layer->blend = true;
- // Generate the texture in which the FBO will draw
glGenTextures(1, &layer->texture);
glBindTexture(GL_TEXTURE_2D, layer->texture);
- // The FBO will not be scaled, so we can use lower quality filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index f187d3e..670d049 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -124,6 +124,8 @@
if (maxTextureUnits < REQUIRED_TEXTURE_UNITS_COUNT) {
LOGW("At least %d texture units are required!", REQUIRED_TEXTURE_UNITS_COUNT);
}
+
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
}
OpenGLRenderer::~OpenGLRenderer() {
@@ -161,6 +163,15 @@
mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight);
}
+void OpenGLRenderer::finish() {
+#if DEBUG_OPENGL
+ GLenum status = GL_NO_ERROR;
+ while ((status = glGetError()) != GL_NO_ERROR) {
+ LOGD("GL error from OpenGLRenderer: 0x%x", status);
+ }
+#endif
+}
+
void OpenGLRenderer::acquireContext() {
if (mCaches.currentProgram) {
if (mCaches.currentProgram->isInUse()) {
@@ -342,7 +353,10 @@
// Layers only make sense if they are in the framebuffer's bounds
bounds.intersect(*mSnapshot->clipRect);
- if (bounds.isEmpty()) return false;
+ if (bounds.isEmpty() || bounds.getWidth() > mMaxTextureSize ||
+ bounds.getHeight() > mMaxTextureSize) {
+ return false;
+ }
LayerSize size(bounds.getWidth(), bounds.getHeight());
Layer* layer = mCaches.layerCache.get(size);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 49a69f9..387fb12 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -45,6 +45,13 @@
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#define DEBUG_OPENGL 1
+
+///////////////////////////////////////////////////////////////////////////////
// Renderer
///////////////////////////////////////////////////////////////////////////////
@@ -59,6 +66,7 @@
void setViewport(int width, int height);
void prepare();
+ void finish();
void acquireContext();
void releaseContext();
@@ -395,6 +403,8 @@
// List of rectangles to clear due to calls to saveLayer()
Vector<Rect*> mLayers;
+ GLint mMaxTextureSize;
+
}; // class OpenGLRenderer
}; // namespace uirenderer