Fix graphical corruption on QCOM GPU
Bug #7146141

The GL_QCOM_tiled_rendering extension requires careful use of
start/endTiling when attaching a renderbuffer dynamically.

Change-Id: I20036683ed3909ffaf40cc3d57a25257e35b6fa2
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index a551b3f..e8a85fd 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -277,6 +277,10 @@
      * Texture coordinates of the layer.
      */
     Rect texCoords;
+    /**
+     * Clipping rectangle.
+     */
+    Rect clipRect;
 
     /**
      * Dirty region indicating what parts of the layer
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 61bedbb..c8a8ed4 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -62,6 +62,7 @@
         android::Rect r(dirty.left, dirty.top, dirty.right, dirty.bottom);
         mLayer->region.subtractSelf(r);
     }
+    mLayer->clipRect.set(dirty);
 
     return OpenGLRenderer::prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, opaque);
 }
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index f55bc9d..1fa1b20 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -169,8 +169,8 @@
     return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);
 }
 
-status_t OpenGLRenderer::prepareDirty(float left, float top, float right, float bottom,
-        bool opaque) {
+status_t OpenGLRenderer::prepareDirty(float left, float top,
+        float right, float bottom, bool opaque) {
     mCaches.clearGarbage();
 
     mSnapshot = new Snapshot(mFirstSnapshot,
@@ -207,7 +207,7 @@
     if (mCaches.extensions.hasDiscardFramebuffer() &&
             left <= 0.0f && top <= 0.0f && right >= mWidth && bottom >= mHeight) {
         const GLenum attachments[] = { getTargetFbo() == 0 ? (const GLenum) GL_COLOR_EXT :
-                (const GLenum) GL_COLOR_ATTACHMENT0 };
+                (const GLenum) GL_COLOR_ATTACHMENT0, GL_STENCIL_EXT };
         glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
     }
 }
@@ -237,12 +237,18 @@
 void OpenGLRenderer::startTiling(const sp<Snapshot>& s, bool opaque) {
     if (!mSuppressTiling) {
         Rect* clip = mTilingSnapshot->clipRect;
-        if (s->flags & Snapshot::kFlagIsFboLayer) {
-            clip = s->clipRect;
+        if (s->flags & Snapshot::kFlagFboTarget) {
+            clip = &s->layer->clipRect;
         }
 
-        mCaches.startTiling(clip->left, s->height - clip->bottom,
-                clip->right - clip->left, clip->bottom - clip->top, opaque);
+        startTiling(*clip, s->height, opaque);
+    }
+}
+
+void OpenGLRenderer::startTiling(const Rect& clip, int windowHeight, bool opaque) {
+    if (!mSuppressTiling) {
+        mCaches.startTiling(clip.left, windowHeight - clip.bottom,
+                    clip.right - clip.left, clip.bottom - clip.top, opaque);
     }
 }
 
@@ -782,18 +788,17 @@
 }
 
 bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip, GLuint previousFbo) {
+    layer->clipRect.set(clip);
     layer->setFbo(mCaches.fboCache.get());
 
     mSnapshot->region = &mSnapshot->layer->region;
-    mSnapshot->flags |= Snapshot::kFlagFboTarget;
-
-    mSnapshot->flags |= Snapshot::kFlagIsFboLayer;
+    mSnapshot->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer |
+            Snapshot::kFlagDirtyOrtho;
     mSnapshot->fbo = layer->getFbo();
     mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
     mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
     mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
     mSnapshot->height = bounds.getHeight();
-    mSnapshot->flags |= Snapshot::kFlagDirtyOrtho;
     mSnapshot->orthoMatrix.load(mOrthoMatrix);
 
     endTiling();
@@ -811,7 +816,7 @@
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
             layer->getTexture(), 0);
 
-    startTiling(mSnapshot);
+    startTiling(mSnapshot, !layer->isBlend());
 
     // Clear the FBO, expand the clear region by 1 to get nice bilinear filtering
     mCaches.enableScissor();
@@ -1245,6 +1250,11 @@
 void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) {
     // The layer's FBO is already bound when we reach this stage
     if (!layer->getStencilRenderBuffer()) {
+        // GL_QCOM_tiled_rendering doesn't like it if a renderbuffer
+        // is attached after we initiated tiling. We must turn it off,
+        // attach the new render buffer then turn tiling back on
+        endTiling();
+
         // TODO: See Layer::removeFbo(). The stencil renderbuffer should be cached
         GLuint buffer;
         glGenRenderbuffers(1, &buffer);
@@ -1254,6 +1264,8 @@
         layer->allocateStencilRenderBuffer();
 
         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer);
+
+        startTiling(layer->clipRect, layer->layer.getHeight(), !layer->isBlend());
     }
 }
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index d4e1eb5..594580e 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -400,12 +400,21 @@
 
     /**
      * Tells the GPU what part of the screen is about to be redrawn.
+     * This method will use the clip rect that we started drawing the
+     * frame with.
      * This method needs to be invoked every time getTargetFbo() is
      * bound again.
      */
     void startTiling(const sp<Snapshot>& snapshot, bool opaque = false);
 
     /**
+     * Tells the GPU what part of the screen is about to be redrawn.
+     * This method needs to be invoked every time getTargetFbo() is
+     * bound again.
+     */
+    void startTiling(const Rect& clip, int windowHeight, bool opaque = false);
+
+    /**
      * Tells the GPU that we are done drawing the frame or that we
      * are switching to another render target.
      */