Fix framebuffer incomplete errors

bug:29127615

Primarily fixes case where 0 dimensioned layers could be
created/updated. Additionally, adds more logging in incomplete
framebuffer cases, if they still occur.

Change-Id: Ib90dbbafd6905aca3c8f46e64064e13a308f713d
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index faf6a52..c9d4af6 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -256,6 +256,7 @@
     tests/unit/MatrixTests.cpp \
     tests/unit/OffscreenBufferPoolTests.cpp \
     tests/unit/RenderNodeTests.cpp \
+    tests/unit/RenderPropertiesTests.cpp \
     tests/unit/SkiaBehaviorTests.cpp \
     tests/unit/SnapshotTests.cpp \
     tests/unit/StringUtilsTests.cpp \
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index ea2e15b..6db345a 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -66,8 +66,13 @@
             offscreenBuffer->texture.id(), 0);
     GL_CHECKPOINT(LOW);
 
-    LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE,
-            "framebuffer incomplete!");
+    int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    LOG_ALWAYS_FATAL_IF(status != GL_FRAMEBUFFER_COMPLETE,
+            "framebuffer incomplete, status %d, textureId %d, size %dx%d",
+            status,
+            offscreenBuffer->texture.id(),
+            offscreenBuffer->texture.width(),
+            offscreenBuffer->texture.height());
 
     // Change the viewport & ortho projection
     setViewport(offscreenBuffer->viewportWidth, offscreenBuffer->viewportHeight);
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index cb8e55f..b8a5ce6 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -86,7 +86,9 @@
             ATRACE_FORMAT("Optimize HW Layer DisplayList %s %ux%u",
                     layerNode->getName(), layerNode->getWidth(), layerNode->getHeight());
 
-            const Rect& layerDamage = layers.entries()[i].damage;
+            Rect layerDamage = layers.entries()[i].damage;
+            // TODO: ensure layer damage can't be larger than layer
+            layerDamage.doIntersect(0, 0, layer->viewportWidth, layer->viewportHeight);
             layerNode->computeOrdering();
 
             // map current light center into RenderNode's coordinate space
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 6e848fd..be2dab9 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -319,6 +319,8 @@
         transformUpdateNeeded = true;
     } else if (!layerMatchesWidthAndHeight(mLayer, getWidth(), getHeight())) {
 #if HWUI_NEW_OPS
+        // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
+        // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
         RenderState& renderState = mLayer->renderState;
         if (properties().fitsOnLayer()) {
             mLayer = renderState.layerPool().resize(mLayer, getWidth(), getHeight());
diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h
index 3952798..696cc29 100644
--- a/libs/hwui/RenderProperties.h
+++ b/libs/hwui/RenderProperties.h
@@ -611,7 +611,9 @@
     bool fitsOnLayer() const {
         const DeviceInfo* deviceInfo = DeviceInfo::get();
         return mPrimitiveFields.mWidth <= deviceInfo->maxTextureSize()
-                        && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize();
+                        && mPrimitiveFields.mHeight <= deviceInfo->maxTextureSize()
+                        && mPrimitiveFields.mWidth > 0
+                        && mPrimitiveFields.mHeight > 0;
     }
 
     bool promotedToLayer() const {
diff --git a/libs/hwui/tests/unit/RenderPropertiesTests.cpp b/libs/hwui/tests/unit/RenderPropertiesTests.cpp
new file mode 100644
index 0000000..9001098
--- /dev/null
+++ b/libs/hwui/tests/unit/RenderPropertiesTests.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 <RenderProperties.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+TEST(RenderProperties, layerValidity) {
+    DeviceInfo::initialize();
+
+    const int maxTextureSize = DeviceInfo::get()->maxTextureSize();
+    ASSERT_LE(2048, maxTextureSize);
+    ASSERT_GT(100000, maxTextureSize);
+
+    RenderProperties props;
+
+    // simple cases that all should fit on layers
+    props.setLeftTopRightBottom(0, 0, 100, 100);
+    ASSERT_TRUE(props.fitsOnLayer());
+    props.setLeftTopRightBottom(100, 2000, 300, 4000);
+    ASSERT_TRUE(props.fitsOnLayer());
+    props.setLeftTopRightBottom(-10, -10, 510, 512);
+    ASSERT_TRUE(props.fitsOnLayer());
+
+    // Too big - can't have layer bigger than max texture size
+    props.setLeftTopRightBottom(0, 0, maxTextureSize + 1, maxTextureSize + 1);
+    ASSERT_FALSE(props.fitsOnLayer());
+
+    // Too small - can't have 0 dimen layer
+    props.setLeftTopRightBottom(0, 0, 100, 0);
+    ASSERT_FALSE(props.fitsOnLayer());
+}