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.
*/
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
index 3d3d709..774811e 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ClipRegionActivity.java
@@ -61,6 +61,7 @@
public RegionView(Context c) {
super(c);
+ setAlpha(0.5f);
}
public float getClipPosition() {