Add region-tracking to OffscreenBuffers

Change-Id: I024c7219c080b9a89888517f5a89d49dfe8065ba
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 7055839..2fca5ea 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -20,6 +20,7 @@
 #include "Glop.h"
 #include "GlopBuilder.h"
 #include "renderstate/RenderState.h"
+#include "utils/FatVector.h"
 #include "utils/GLUtils.h"
 
 namespace android {
@@ -29,12 +30,13 @@
 // OffscreenBuffer
 ////////////////////////////////////////////////////////////////////////////////
 
-OffscreenBuffer::OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t textureHeight,
+OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches,
+        uint32_t textureWidth, uint32_t textureHeight,
         uint32_t viewportWidth, uint32_t viewportHeight)
-        : viewportWidth(viewportWidth)
+        : renderState(renderState)
+        , viewportWidth(viewportWidth)
         , viewportHeight(viewportHeight)
-        , texture(caches)
-        , texCoords(0, viewportHeight / float(textureHeight), viewportWidth / float(textureWidth), 0) {
+        , texture(caches) {
     texture.width = textureWidth;
     texture.height = textureHeight;
 
@@ -50,19 +52,58 @@
             GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
 }
 
+void OffscreenBuffer::updateMeshFromRegion() {
+    // avoid T-junctions as they cause artifacts in between the resultant
+    // geometry when complex transforms occur.
+    // TODO: generate the safeRegion only if necessary based on drawing transform
+    Region safeRegion = Region::createTJunctionFreeRegion(region);
+
+    size_t count;
+    const android::Rect* rects = safeRegion.getArray(&count);
+
+    const float texX = 1.0f / float(viewportWidth);
+    const float texY = 1.0f / float(viewportHeight);
+
+    FatVector<TextureVertex, 64> meshVector(count * 4); // uses heap if more than 64 vertices needed
+    TextureVertex* mesh = &meshVector[0];
+    for (size_t i = 0; i < count; i++) {
+        const android::Rect* r = &rects[i];
+
+        const float u1 = r->left * texX;
+        const float v1 = (viewportHeight - r->top) * texY;
+        const float u2 = r->right * texX;
+        const float v2 = (viewportHeight - r->bottom) * texY;
+
+        TextureVertex::set(mesh++, r->left, r->top, u1, v1);
+        TextureVertex::set(mesh++, r->right, r->top, u2, v1);
+        TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
+        TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
+    }
+    elementCount = count * 6;
+    renderState.meshState().genOrUpdateMeshBuffer(&vbo,
+            sizeof(TextureVertex) * count * 4,
+            &meshVector[0],
+            GL_DYNAMIC_DRAW); // TODO: GL_STATIC_DRAW if savelayer
+}
+
+OffscreenBuffer::~OffscreenBuffer() {
+    texture.deleteTexture();
+    renderState.meshState().deleteMeshBuffer(vbo);
+    elementCount = 0;
+    vbo = 0;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // BakedOpRenderer
 ////////////////////////////////////////////////////////////////////////////////
 
-OffscreenBuffer* BakedOpRenderer::createOffscreenBuffer(uint32_t width, uint32_t height) {
+OffscreenBuffer* BakedOpRenderer::createOffscreenBuffer(RenderState& renderState,
+        uint32_t width, uint32_t height) {
     // TODO: get from cache!
-    return new OffscreenBuffer(Caches::getInstance(), width, height, width, height);
+    return new OffscreenBuffer(renderState, Caches::getInstance(), width, height, width, height);
 }
 
 void BakedOpRenderer::destroyOffscreenBuffer(OffscreenBuffer* offscreenBuffer) {
-    // destroy and delete, since each clipped saveLayer is only drawn once.
-    offscreenBuffer->texture.deleteTexture();
-
     // TODO: return texture/offscreenbuffer to cache!
     delete offscreenBuffer;
 }
@@ -70,7 +111,7 @@
 OffscreenBuffer* BakedOpRenderer::createLayer(uint32_t width, uint32_t height) {
     LOG_ALWAYS_FATAL_IF(mRenderTarget.offscreenBuffer, "already has layer...");
 
-    OffscreenBuffer* buffer = createOffscreenBuffer(width, height);
+    OffscreenBuffer* buffer = createOffscreenBuffer(mRenderState, width, height);
     startLayer(buffer);
     return buffer;
 }
@@ -98,6 +139,7 @@
 }
 
 void BakedOpRenderer::endLayer() {
+    mRenderTarget.offscreenBuffer->updateMeshFromRegion();
     mRenderTarget.offscreenBuffer = nullptr;
 
     // Detach the texture from the FBO
@@ -162,6 +204,12 @@
         mRenderState.scissor().set(clip.left, mRenderTarget.viewportHeight - clip.bottom,
             clip.getWidth(), clip.getHeight());
     }
+    if (mRenderTarget.offscreenBuffer) { // TODO: not with multi-draw
+        // register layer damage to draw-back region
+        const Rect& uiDirty = state.computedState.clippedBounds;
+        android::Rect dirty(uiDirty.left, uiDirty.top, uiDirty.right, uiDirty.bottom);
+        mRenderTarget.offscreenBuffer->region.orSelf(dirty);
+    }
     mRenderState.render(glop, mRenderTarget.orthoMatrix);
     mHasDrawn = true;
 }
@@ -174,6 +222,14 @@
     LOG_ALWAYS_FATAL("unsupported operation");
 }
 
+void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer& renderer, const BeginLayerOp& op, const BakedOpState& state) {
+    LOG_ALWAYS_FATAL("unsupported operation");
+}
+
+void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer& renderer, const EndLayerOp& op, const BakedOpState& state) {
+    LOG_ALWAYS_FATAL("unsupported operation");
+}
+
 void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op, const BakedOpState& state) {
     renderer.caches().textureState().activateTexture(0); // TODO: should this be automatic, and/or elsewhere?
     Texture* texture = renderer.getTexture(op.bitmap);
@@ -217,28 +273,20 @@
     renderer.renderGlop(state, glop);
 }
 
-void BakedOpDispatcher::onBeginLayerOp(BakedOpRenderer& renderer, const BeginLayerOp& op, const BakedOpState& state) {
-    LOG_ALWAYS_FATAL("unsupported operation");
-}
-
-void BakedOpDispatcher::onEndLayerOp(BakedOpRenderer& renderer, const EndLayerOp& op, const BakedOpState& state) {
-    LOG_ALWAYS_FATAL("unsupported operation");
-}
-
 void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) {
     OffscreenBuffer* buffer = *op.layerHandle;
 
     // TODO: extend this to handle HW layers & paint properties which
     // reside in node.properties().layerProperties()
     float layerAlpha = op.alpha * state.alpha;
-    const bool tryToSnap = state.computedState.transform.isPureTranslate();
     Glop glop;
     GlopBuilder(renderer.renderState(), renderer.caches(), &glop)
             .setRoundRectClipState(state.roundRectClipState)
-            .setMeshTexturedUvQuad(nullptr, buffer->texCoords)
+            .setMeshTexturedIndexedVbo(buffer->vbo, buffer->elementCount)
             .setFillLayer(buffer->texture, op.colorFilter, layerAlpha, op.mode, Blend::ModeOrderSwap::NoSwap)
             .setTransform(state.computedState.transform, TransformFlags::None)
-            .setModelViewMapUnitToRectOptionalSnap(tryToSnap, op.unmappedBounds)
+            .setModelViewOffsetRectSnap(op.unmappedBounds.left, op.unmappedBounds.top,
+                    Rect(op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight()))
             .build();
     renderer.renderGlop(state, glop);
 
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index 722bf02..aa1e67d 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -35,13 +35,24 @@
  */
 class OffscreenBuffer {
 public:
-    OffscreenBuffer(Caches& caches, uint32_t textureWidth, uint32_t textureHeight,
+    OffscreenBuffer(RenderState& renderState, Caches& caches,
+            uint32_t textureWidth, uint32_t textureHeight,
             uint32_t viewportWidth, uint32_t viewportHeight);
+    ~OffscreenBuffer();
+
+    // must be called prior to rendering, to construct/update vertex buffer
+    void updateMeshFromRegion();
+
+    RenderState& renderState;
     uint32_t viewportWidth;
     uint32_t viewportHeight;
     Texture texture;
-    Rect texCoords;
+
+    // Portion of offscreen buffer that has been drawn to. Used to minimize drawing area when
+    // drawing back to screen / parent FBO.
     Region region;
+    GLsizei elementCount = 0;
+    GLuint vbo = 0;
 };
 
 /**
@@ -61,7 +72,8 @@
             , mOpaque(opaque) {
     }
 
-    static OffscreenBuffer* createOffscreenBuffer(uint32_t width, uint32_t height);
+    static OffscreenBuffer* createOffscreenBuffer(RenderState& renderState,
+            uint32_t width, uint32_t height);
     static void destroyOffscreenBuffer(OffscreenBuffer*);
 
     RenderState& renderState() { return mRenderState; }
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index d2da851..f3ac93b 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -70,6 +70,20 @@
 // Mesh
 ////////////////////////////////////////////////////////////////////////////////
 
+GlopBuilder& GlopBuilder::setMeshTexturedIndexedVbo(GLuint vbo, GLsizei elementCount) {
+    TRIGGER_STAGE(kMeshStage);
+
+    mOutGlop->mesh.primitiveMode = GL_TRIANGLES;
+    mOutGlop->mesh.indices = { mRenderState.meshState().getQuadListIBO(), nullptr };
+    mOutGlop->mesh.vertices = {
+            vbo,
+            VertexAttribFlags::TextureCoord,
+            nullptr, nullptr, nullptr,
+            kTextureVertexStride };
+    mOutGlop->mesh.elementCount = elementCount;
+    return *this;
+}
+
 GlopBuilder& GlopBuilder::setMeshUnitQuad() {
     TRIGGER_STAGE(kMeshStage);
 
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index 6f5802e..6270dcb 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -47,6 +47,7 @@
 public:
     GlopBuilder(RenderState& renderState, Caches& caches, Glop* outGlop);
 
+    GlopBuilder& setMeshTexturedIndexedVbo(GLuint vbo, GLsizei elementCount);
     GlopBuilder& setMeshUnitQuad();
     GlopBuilder& setMeshTexturedUnitQuad(const UvMapper* uvMapper);
     GlopBuilder& setMeshTexturedUvQuad(const UvMapper* uvMapper, const Rect uvs);
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index de02bb8..0601944 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -250,7 +250,7 @@
 
 layer_t* createLayer(RenderState& renderState, uint32_t width, uint32_t height) {
 #if HWUI_NEW_OPS
-    return BakedOpRenderer::createOffscreenBuffer(width, height);
+    return BakedOpRenderer::createOffscreenBuffer(renderState, width, height);
 #else
     return LayerRenderer::createRenderLayer(renderState, width, height);
 #endif
diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp
index 0521f65..03cb5ce 100644
--- a/libs/hwui/renderstate/MeshState.cpp
+++ b/libs/hwui/renderstate/MeshState.cpp
@@ -100,6 +100,24 @@
     return false;
 }
 
+void MeshState::genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size,
+        const void* data, GLenum usage) {
+    if (!*buffer) {
+        glGenBuffers(1, buffer);
+    }
+    bindMeshBuffer(*buffer);
+    glBufferData(GL_ARRAY_BUFFER, size, data, usage);
+}
+
+void MeshState::deleteMeshBuffer(GLuint buffer) {
+    if (buffer == mCurrentBuffer) {
+        // GL defines that deleting the currently bound VBO rebinds to 0 (no VBO).
+        // Reflect this in our cached value.
+        mCurrentBuffer = 0;
+    }
+    glDeleteBuffers(1, &buffer);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 // Vertices
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h
index e80f4d0..6c0fb78 100644
--- a/libs/hwui/renderstate/MeshState.h
+++ b/libs/hwui/renderstate/MeshState.h
@@ -75,6 +75,9 @@
      */
     bool unbindMeshBuffer();
 
+    void genOrUpdateMeshBuffer(GLuint* buffer, GLsizeiptr size, const void* data, GLenum usage);
+    void deleteMeshBuffer(GLuint);
+
     ///////////////////////////////////////////////////////////////////////////////
     // Vertices
     ///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/unit_tests/FatVectorTests.cpp b/libs/hwui/unit_tests/FatVectorTests.cpp
index fb760ac5..1f1f50a 100644
--- a/libs/hwui/unit_tests/FatVectorTests.cpp
+++ b/libs/hwui/unit_tests/FatVectorTests.cpp
@@ -56,6 +56,27 @@
     }
 }
 
+TEST(FatVector, preSizeConstructor) {
+    {
+        FatVector<int, 4> v(32);
+        EXPECT_EQ(32u, v.capacity());
+        EXPECT_EQ(32u, v.size());
+        EXPECT_FALSE(allocationIsInternal(v));
+    }
+    {
+        FatVector<int, 4> v(4);
+        EXPECT_EQ(4u, v.capacity());
+        EXPECT_EQ(4u, v.size());
+        EXPECT_TRUE(allocationIsInternal(v));
+    }
+    {
+        FatVector<int, 4> v(2);
+        EXPECT_EQ(4u, v.capacity());
+        EXPECT_EQ(2u, v.size());
+        EXPECT_TRUE(allocationIsInternal(v));
+    }
+}
+
 TEST(FatVector, shrink) {
     FatVector<int, 10> v;
     EXPECT_TRUE(allocationIsInternal(v));
diff --git a/libs/hwui/utils/FatVector.h b/libs/hwui/utils/FatVector.h
index c3c16c5a..315c249 100644
--- a/libs/hwui/utils/FatVector.h
+++ b/libs/hwui/utils/FatVector.h
@@ -91,6 +91,10 @@
             InlineStdAllocator<T, SIZE>(mAllocation)) {
         this->reserve(SIZE);
     }
+
+    FatVector(size_t capacity) : FatVector() {
+        this->resize(capacity);
+    }
 private:
     typename InlineStdAllocator<T, SIZE>::Allocation mAllocation;
 };