OSDN Git Service

Fix flickering when layers resize in-place
authorChris Craik <ccraik@google.com>
Sat, 12 Mar 2016 03:16:21 +0000 (19:16 -0800)
committerChris Craik <ccraik@google.com>
Mon, 14 Mar 2016 20:26:25 +0000 (13:26 -0700)
bug:27248275
Change-Id: Ia11c93ebc1097f3735071204b6f14ca079bb9fc4

libs/hwui/Android.mk
libs/hwui/BakedOpRenderer.cpp
libs/hwui/ClipArea.cpp
libs/hwui/ClipArea.h
libs/hwui/LayerBuilder.cpp
libs/hwui/debug/nullgles.cpp
libs/hwui/renderstate/OffscreenBufferPool.cpp
libs/hwui/tests/unit/BakedOpRendererTests.cpp [new file with mode: 0644]
libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp

index f6e3b50..ca07738 100644 (file)
@@ -255,6 +255,7 @@ LOCAL_SRC_FILES += \
 ifeq (true, $(HWUI_NEW_OPS))
     LOCAL_SRC_FILES += \
         tests/unit/BakedOpDispatcherTests.cpp \
+        tests/unit/BakedOpRendererTests.cpp \
         tests/unit/BakedOpStateTests.cpp \
         tests/unit/FrameBuilderTests.cpp \
         tests/unit/LeakCheckTests.cpp \
index da5ecca..bb3ea3f 100644 (file)
@@ -40,6 +40,16 @@ OffscreenBuffer* BakedOpRenderer::startTemporaryLayer(uint32_t width, uint32_t h
 void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const Rect& repaintRect) {
     LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
 
+    // subtract repaintRect from region, since it will be regenerated
+    if (repaintRect.contains(0, 0,
+                offscreenBuffer->viewportWidth, offscreenBuffer->viewportHeight)) {
+        // repaint full layer, so throw away entire region
+        offscreenBuffer->region.clear();
+    } else {
+        offscreenBuffer->region.subtractSelf(android::Rect(repaintRect.left, repaintRect.top,
+                repaintRect.right, repaintRect.bottom));
+    }
+
     mRenderTarget.offscreenBuffer = offscreenBuffer;
 
     // create and bind framebuffer
index afe9807..f886dda 100644 (file)
@@ -41,6 +41,10 @@ Rect transformAndCalculateBounds(const Rect& r, const Matrix4& transform) {
     return transformedBounds;
 }
 
+void ClipBase::dump() const {
+    ALOGD("mode %d" RECT_STRING, mode, RECT_ARGS(rect));
+}
+
 /*
  * TransformedRectangle
  */
index 479796d..1654eb8 100644 (file)
@@ -106,6 +106,8 @@ struct ClipBase {
     // Bounds of the clipping area, used to define the scissor, and define which
     // portion of the stencil is updated/used
     Rect rect;
+
+    void dump() const;
 };
 
 struct ClipRect : ClipBase {
index c5af279..e6a95ff 100644 (file)
@@ -349,8 +349,9 @@ void LayerBuilder::replayBakedOpsImpl(void* arg,
 }
 
 void LayerBuilder::dump() const {
-    ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p",
-            this, width, height, offscreenBuffer, beginLayerOp, renderNode);
+    ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p (%s)",
+            this, width, height, offscreenBuffer, beginLayerOp,
+            renderNode, renderNode ? renderNode->getName() : "-");
     for (const BatchBase* batch : mBatches) {
         batch->dump();
     }
index ffb0649..8689f98 100644 (file)
@@ -133,6 +133,15 @@ void glGetIntegerv(GLenum pname, GLint *data) {
     }
 }
 
+GLenum glCheckFramebufferStatus(GLenum target) {
+    switch (target) {
+    case GL_FRAMEBUFFER:
+        return GL_FRAMEBUFFER_COMPLETE;
+    default:
+        return 0; // error case
+    }
+}
+
 const char* getString(GLenum name) {
     switch (name) {
     case GL_VENDOR:
index 5f984b5..bb1a044 100644 (file)
@@ -164,6 +164,9 @@ OffscreenBuffer* OffscreenBufferPool::resize(OffscreenBuffer* layer,
         // resize in place
         layer->viewportWidth = width;
         layer->viewportHeight = height;
+
+        // entire area will be repainted (and may be smaller) so clear usage region
+        layer->region.clear();
         return layer;
     }
     putOrDelete(layer);
diff --git a/libs/hwui/tests/unit/BakedOpRendererTests.cpp b/libs/hwui/tests/unit/BakedOpRendererTests.cpp
new file mode 100644 (file)
index 0000000..59bd75e
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <BakedOpRenderer.h>
+#include <tests/common/TestUtils.h>
+
+using namespace android::uirenderer;
+
+const BakedOpRenderer::LightInfo sLightInfo = { 128, 128 };
+
+RENDERTHREAD_TEST(BakedOpRenderer, startRepaintLayer_clear) {
+    BakedOpRenderer renderer(Caches::getInstance(), renderThread.renderState(), true, sLightInfo);
+    OffscreenBuffer layer(renderThread.renderState(), Caches::getInstance(), 200u, 200u);
+
+    layer.dirty(Rect(200, 200));
+    {
+        renderer.startRepaintLayer(&layer, Rect(200, 200));
+        EXPECT_TRUE(layer.region.isEmpty()) << "Repaint full layer should clear region";
+        renderer.endLayer();
+    }
+
+    layer.dirty(Rect(200, 200));
+    {
+        renderer.startRepaintLayer(&layer, Rect(100, 200)); // repainting left side
+        EXPECT_TRUE(layer.region.isRect());
+        //ALOGD("bounds %d %d %d %d", RECT_ARGS(layer.region.getBounds()));
+        EXPECT_EQ(android::Rect(100, 0, 200, 200), layer.region.getBounds())
+                << "Left side being repainted, so right side should be clear";
+        renderer.endLayer();
+    }
+
+    // right side is now only dirty portion
+    {
+        renderer.startRepaintLayer(&layer, Rect(100, 0, 200, 200)); // repainting right side
+        EXPECT_TRUE(layer.region.isEmpty())
+                << "Now right side being repainted, so region should be entirely clear";
+        renderer.endLayer();
+    }
+}
index 0c6eb57..37a485e 100644 (file)
@@ -103,9 +103,11 @@ TEST(OffscreenBufferPool, resize) {
         OffscreenBufferPool pool;
 
         auto layer = pool.get(thread.renderState(), 64u, 64u);
+        layer->dirty(Rect(64, 64));
 
         // resize in place
         ASSERT_EQ(layer, pool.resize(layer, 60u, 55u));
+        EXPECT_TRUE(layer->region.isEmpty()) << "In place resize should clear usage region";
         EXPECT_EQ(60u, layer->viewportWidth);
         EXPECT_EQ(55u, layer->viewportHeight);
         EXPECT_EQ(64u, layer->texture.width());
@@ -113,9 +115,13 @@ TEST(OffscreenBufferPool, resize) {
 
         // resized to use different object in pool
         auto layer2 = pool.get(thread.renderState(), 128u, 128u);
+        layer2->dirty(Rect(128, 128));
+        EXPECT_FALSE(layer2->region.isEmpty());
         pool.putOrDelete(layer2);
         ASSERT_EQ(1u, pool.getCount());
+
         ASSERT_EQ(layer2, pool.resize(layer, 120u, 125u));
+        EXPECT_TRUE(layer2->region.isEmpty()) << "Swap resize should clear usage region";
         EXPECT_EQ(120u, layer2->viewportWidth);
         EXPECT_EQ(125u, layer2->viewportHeight);
         EXPECT_EQ(128u, layer2->texture.width());