Support for stencil clipping in layers

bug:22480459

Change-Id: Ic9e8652379524ccc46d8722ce49f9190b08a2abc
diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp
index 757c12b..a0d5fae 100644
--- a/libs/hwui/BakedOpRenderer.cpp
+++ b/libs/hwui/BakedOpRenderer.cpp
@@ -60,10 +60,18 @@
 }
 
 void BakedOpRenderer::endLayer() {
-    mRenderTarget.offscreenBuffer->updateMeshFromRegion();
-    mRenderTarget.offscreenBuffer = nullptr;
+    if (mRenderTarget.stencil) {
+        // if stencil was used for clipping, detach it and return it to pool
+        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
+        LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "glfbrb endlayer failed");
+        mCaches.renderBufferCache.put(mRenderTarget.stencil);
+        mRenderTarget.stencil = nullptr;
+    }
     mRenderTarget.lastStencilClip = nullptr;
 
+    mRenderTarget.offscreenBuffer->updateMeshFromRegion();
+    mRenderTarget.offscreenBuffer = nullptr; // It's in drawLayerOp's hands now.
+
     // Detach the texture from the FBO
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
     LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "endLayer FAILED");
@@ -75,7 +83,6 @@
     LOG_ALWAYS_FATAL_IF(mRenderTarget.frameBufferId != 0, "primary framebufferId must be 0");
     mRenderState.bindFramebuffer(0);
     setViewport(width, height);
-    mCaches.clearGarbage();
 
     if (!mOpaque) {
         clearColorBuffer(repaintRect);
@@ -113,6 +120,7 @@
         mRenderState.stencil().disable();
     }
 
+    mCaches.clearGarbage();
     mCaches.pathCache.trim();
     mCaches.tessellationCache.trim();
 
@@ -232,28 +240,45 @@
     mRenderState.scissor().setEnabled(clip != nullptr);
     if (clip) {
         mRenderState.scissor().set(mRenderTarget.viewportHeight, clip->rect);
-        if (CC_LIKELY(!Properties::debugOverdraw)) {
-            // only modify stencil mode and content when it's not used for overdraw visualization
-            if (CC_UNLIKELY(clip->mode != ClipMode::Rectangle)) {
-                // NOTE: this pointer check is only safe for non-rect clips,
-                // since rect clips may be created on the stack
-                if (mRenderTarget.lastStencilClip != clip) {
-                    // Stencil needed, but current stencil isn't up to date
-                    mRenderTarget.lastStencilClip = clip;
+    }
 
-                    if (mRenderTarget.offscreenBuffer) {
-                        LOG_ALWAYS_FATAL("prepare layer stencil");
-                    }
+    if (CC_LIKELY(!Properties::debugOverdraw)) {
+        // only modify stencil mode and content when it's not used for overdraw visualization
+        if (CC_UNLIKELY(clip && clip->mode != ClipMode::Rectangle)) {
+            // NOTE: this pointer check is only safe for non-rect clips,
+            // since rect clips may be created on the stack
+            if (mRenderTarget.lastStencilClip != clip) {
+                // Stencil needed, but current stencil isn't up to date
+                mRenderTarget.lastStencilClip = clip;
 
-                    if (clip->mode == ClipMode::RectangleList) {
-                        setupStencilRectList(clip);
-                    } else {
-                        setupStencilRegion(clip);
-                    }
+                if (mRenderTarget.frameBufferId != 0 && !mRenderTarget.stencil) {
+                    OffscreenBuffer* layer = mRenderTarget.offscreenBuffer;
+                    mRenderTarget.stencil = mCaches.renderBufferCache.get(
+                            Stencil::getLayerStencilFormat(),
+                            layer->texture.width, layer->texture.height);
+                    // stencil is bound + allocated - associate it with current FBO
+                    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+                            GL_RENDERBUFFER, mRenderTarget.stencil->getName());
+                }
+
+                if (clip->mode == ClipMode::RectangleList) {
+                    setupStencilRectList(clip);
+                } else {
+                    setupStencilRegion(clip);
                 }
             } else {
-                mRenderState.stencil().disable();
+                // stencil is up to date - just need to ensure it's enabled (since an unclipped
+                // or scissor-only clipped op may have been drawn, disabling the stencil)
+                int incrementThreshold = 0;
+                if (CC_LIKELY(clip->mode == ClipMode::RectangleList)) {
+                    auto&& rectList = reinterpret_cast<const ClipRectList*>(clip)->rectList;
+                    incrementThreshold = rectList.getTransformedRectanglesCount();
+                }
+                mRenderState.stencil().enableTest(incrementThreshold);
             }
+        } else {
+            // either scissor or no clip, so disable stencil test
+            mRenderState.stencil().disable();
         }
     }
 
diff --git a/libs/hwui/BakedOpRenderer.h b/libs/hwui/BakedOpRenderer.h
index 5bd2c0a..65e8b29 100644
--- a/libs/hwui/BakedOpRenderer.h
+++ b/libs/hwui/BakedOpRenderer.h
@@ -95,12 +95,24 @@
     // render target state - setup by start/end layer/frame
     // only valid to use in between start/end pairs.
     struct {
+        // If not drawing to a layer: fbo = 0, offscreenBuffer = null,
+        // Otherwise these refer to currently painting layer's state
         GLuint frameBufferId = 0;
         OffscreenBuffer* offscreenBuffer = nullptr;
+
+        // Used when drawing to a layer and using stencil clipping. otherwise null.
+        RenderBuffer* stencil = nullptr;
+
+        // value representing the ClipRectList* or ClipRegion* currently stored in
+        // the stencil of the current render target
+        const ClipBase* lastStencilClip = nullptr;
+
+        // Size of renderable region in current render target - for layers, may not match actual
+        // bounds of FBO texture. offscreenBuffer->texture has this information.
         uint32_t viewportWidth = 0;
         uint32_t viewportHeight = 0;
+
         Matrix4 orthoMatrix;
-        const ClipBase* lastStencilClip = nullptr;
     } mRenderTarget;
 
     const LightInfo mLightInfo;
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 2507ff3..45fc16c 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -78,7 +78,7 @@
     mOutGlop->mesh.vertices = {
             vbo,
             VertexAttribFlags::TextureCoord,
-            nullptr, nullptr, nullptr,
+            nullptr, (const void*) kMeshTextureOffset, nullptr,
             kTextureVertexStride };
     mOutGlop->mesh.elementCount = elementCount;
     return *this;
diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h
index 3bfe10d..1971530 100644
--- a/libs/hwui/RecordedOp.h
+++ b/libs/hwui/RecordedOp.h
@@ -391,9 +391,9 @@
     LayerOp(BASE_PARAMS, OffscreenBuffer** layerHandle)
             : SUPER_PAINTLESS(LayerOp)
             , layerHandle(layerHandle)
-            , alpha(paint->getAlpha() / 255.0f)
+            , alpha(paint ? paint->getAlpha() / 255.0f : 1.0f)
             , mode(PaintUtils::getXfermodeDirect(paint))
-            , colorFilter(paint->getColorFilter())
+            , colorFilter(paint ? paint->getColorFilter() : nullptr)
             , destroy(true) {}
 
     LayerOp(RenderNode& node)
diff --git a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
index 38788f0..db6402c 100644
--- a/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/ClippingAnimation.cpp
@@ -30,8 +30,6 @@
     sp<RenderNode> card;
     void createContent(int width, int height, TestCanvas& canvas) override {
         canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode);
-        canvas.insertReorderBarrier(true);
-
         card = TestUtils::createNode(0, 0, 200, 400,
                 [](RenderProperties& props, TestCanvas& canvas) {
             canvas.save(SkCanvas::kMatrixClip_SaveFlag);
@@ -53,10 +51,12 @@
                 canvas.drawColor(Color::Red_500, SkXfermode::kSrcOver_Mode);
             }
             canvas.restore();
+
+            // put on a layer, to test stencil attachment
+            props.mutateLayerProperties().setType(LayerType::RenderLayer);
+            props.setAlpha(0.9f);
         });
         canvas.drawRenderNode(card.get());
-
-        canvas.insertReorderBarrier(false);
     }
     void doFrame(int frameNr) override {
         int curFrame = frameNr % 150;