Add a RenderBuffer object to store stencil buffers.
Bug #7146141

This change is needed to add a render buffer cache to avoid
creating and destroying stencil buffers on every frame.

This change also allows the renderer to use a 1 bit or 4 bit
stencil buffer whenever possible.

Finally this change fixes a bug introduced by a previous CL
which causes the stencil buffer to not be updated in certain
conditions. The fix relies on a new optional parameter in
drawColorRects() that can be used to avoid performing a
quickReject on rectangles generated by the clip region.

Change-Id: I2f55a8e807009887b276a83cde9f53fd5c01199f
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 549edd2..7dce1ca 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -14,6 +14,7 @@
 		DisplayListLogBuffer.cpp \
 		DisplayListRenderer.cpp \
 		Dither.cpp \
+		Extensions.cpp \
 		FboCache.cpp \
 		GradientCache.cpp \
 		Layer.cpp \
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 1a86b3a..492bb7d1 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -47,7 +47,7 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-Caches::Caches(): Singleton<Caches>(), mInitialized(false) {
+Caches::Caches(): Singleton<Caches>(), mExtensions(Extensions::getInstance()), mInitialized(false) {
     init();
     initFont();
     initExtensions();
@@ -100,7 +100,7 @@
 }
 
 void Caches::initExtensions() {
-    if (extensions.hasDebugMarker()) {
+    if (mExtensions.hasDebugMarker()) {
         eventMark = glInsertEventMarkerEXT;
         startMark = glPushGroupMarkerEXT;
         endMark = glPopGroupMarkerEXT;
@@ -110,7 +110,7 @@
         endMark = endMarkNull;
     }
 
-    if (extensions.hasDebugLabel()) {
+    if (mExtensions.hasDebugLabel()) {
         setLabel = glLabelObjectEXT;
         getLabel = glGetObjectLabelEXT;
     } else {
@@ -470,13 +470,13 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void Caches::startTiling(GLuint x, GLuint y, GLuint width, GLuint height, bool discard) {
-    if (extensions.hasTiledRendering() && !debugOverdraw) {
+    if (mExtensions.hasTiledRendering() && !debugOverdraw) {
         glStartTilingQCOM(x, y, width, height, (discard ? GL_NONE : GL_COLOR_BUFFER_BIT0_QCOM));
     }
 }
 
 void Caches::endTiling() {
-    if (extensions.hasTiledRendering() && !debugOverdraw) {
+    if (mExtensions.hasTiledRendering() && !debugOverdraw) {
         glEndTilingQCOM(GL_COLOR_BUFFER_BIT0_QCOM);
     }
 }
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 1c4d05f..f1d0285 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -25,7 +25,6 @@
 
 #include <cutils/compiler.h>
 
-#include "Extensions.h"
 #include "FontRenderer.h"
 #include "GammaFontRenderer.h"
 #include "TextureCache.h"
@@ -244,9 +243,6 @@
     // VBO to draw with
     GLuint meshBuffer;
 
-    // GL extensions
-    Extensions extensions;
-
     // Misc
     GLint maxTextureSize;
     bool debugLayersUpdates;
@@ -312,6 +308,8 @@
     GLint mScissorWidth;
     GLint mScissorHeight;
 
+    Extensions& mExtensions;
+
     // Used to render layers
     TextureVertex* mRegionMesh;
     GLuint mRegionMeshIndices;
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index dfc4e25c..5f8baac 100644
--- a/libs/hwui/Debug.h
+++ b/libs/hwui/Debug.h
@@ -44,6 +44,9 @@
 // Turn on to display info about layers
 #define DEBUG_LAYERS 0
 
+// Turn on to make stencil operations easier to debug
+#define DEBUG_STENCIL 0
+
 // Turn on to display debug info about 9patch objects
 #define DEBUG_PATCHES 0
 // Turn on to "explode" 9patch objects
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
new file mode 100644
index 0000000..edc90fb
--- /dev/null
+++ b/libs/hwui/Extensions.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2013 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 "Debug.h"
+#include "Extensions.h"
+
+namespace android {
+
+using namespace uirenderer;
+ANDROID_SINGLETON_STATIC_INSTANCE(Extensions);
+
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#if DEBUG_EXTENSIONS
+    #define EXT_LOGD(...) ALOGD(__VA_ARGS__)
+#else
+    #define EXT_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors
+///////////////////////////////////////////////////////////////////////////////
+
+Extensions::Extensions(): Singleton<Extensions>() {
+    const char* buffer = (const char*) glGetString(GL_EXTENSIONS);
+    const char* current = buffer;
+    const char* head = current;
+    EXT_LOGD("Available GL extensions:");
+    do {
+        head = strchr(current, ' ');
+        String8 s(current, head ? head - current : strlen(current));
+        if (s.length()) {
+            mExtensionList.add(s);
+            EXT_LOGD("  %s", s.string());
+        }
+        current = head + 1;
+    } while (head);
+
+    mHasNPot = hasExtension("GL_OES_texture_npot");
+    mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch");
+    mHasDiscardFramebuffer = hasExtension("GL_EXT_discard_framebuffer");
+    mHasDebugMarker = hasExtension("GL_EXT_debug_marker");
+    mHasDebugLabel = hasExtension("GL_EXT_debug_label");
+    mHasTiledRendering = hasExtension("GL_QCOM_tiled_rendering");
+    mHas1BitStencil = hasExtension("GL_OES_stencil1");
+    mHas4BitStencil = hasExtension("GL_OES_stencil4");
+
+    mExtensions = strdup(buffer);
+}
+
+Extensions::~Extensions() {
+   free(mExtensions);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Methods
+///////////////////////////////////////////////////////////////////////////////
+
+bool Extensions::hasExtension(const char* extension) const {
+   const String8 s(extension);
+   return mExtensionList.indexOf(s) >= 0;
+}
+
+void Extensions::dump() const {
+   ALOGD("Supported extensions:\n%s", mExtensions);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index bdaa3cc..a069a6a 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -17,62 +17,24 @@
 #ifndef ANDROID_HWUI_EXTENSIONS_H
 #define ANDROID_HWUI_EXTENSIONS_H
 
+#include <utils/Singleton.h>
 #include <utils/SortedVector.h>
 #include <utils/String8.h>
 
 #include <GLES2/gl2.h>
 #include <GLES2/gl2ext.h>
 
-#include "Debug.h"
-
 namespace android {
 namespace uirenderer {
 
 ///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-// Debug
-#if DEBUG_EXTENSIONS
-    #define EXT_LOGD(...) ALOGD(__VA_ARGS__)
-#else
-    #define EXT_LOGD(...)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
 // Classes
 ///////////////////////////////////////////////////////////////////////////////
 
-class Extensions {
+class Extensions: public Singleton<Extensions> {
 public:
-    Extensions() {
-        const char* buffer = (const char*) glGetString(GL_EXTENSIONS);
-        const char* current = buffer;
-        const char* head = current;
-        EXT_LOGD("Available GL extensions:");
-        do {
-            head = strchr(current, ' ');
-            String8 s(current, head ? head - current : strlen(current));
-            if (s.length()) {
-                mExtensionList.add(s);
-                EXT_LOGD("  %s", s.string());
-            }
-            current = head + 1;
-        } while (head);
-
-        mHasNPot = hasExtension("GL_OES_texture_npot");
-        mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch");
-        mHasDiscardFramebuffer = hasExtension("GL_EXT_discard_framebuffer");
-        mHasDebugMarker = hasExtension("GL_EXT_debug_marker");
-        mHasDebugLabel = hasExtension("GL_EXT_debug_label");
-        mHasTiledRendering = hasExtension("GL_QCOM_tiled_rendering");
-
-        mExtensions = strdup(buffer);
-    }
-
-    ~Extensions() {
-        free(mExtensions);
-    }
+    Extensions();
+    ~Extensions();
 
     inline bool hasNPot() const { return mHasNPot; }
     inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; }
@@ -80,17 +42,16 @@
     inline bool hasDebugMarker() const { return mHasDebugMarker; }
     inline bool hasDebugLabel() const { return mHasDebugLabel; }
     inline bool hasTiledRendering() const { return mHasTiledRendering; }
+    inline bool has1BitStencil() const { return mHas1BitStencil; }
+    inline bool has4BitStencil() const { return mHas4BitStencil; }
 
-    bool hasExtension(const char* extension) const {
-        const String8 s(extension);
-        return mExtensionList.indexOf(s) >= 0;
-    }
+    bool hasExtension(const char* extension) const;
 
-    void dump() {
-        ALOGD("Supported extensions:\n%s", mExtensions);
-    }
+    void dump() const;
 
 private:
+    friend class Singleton<Extensions>;
+
     SortedVector<String8> mExtensionList;
 
     char* mExtensions;
@@ -101,6 +62,8 @@
     bool mHasDebugMarker;
     bool mHasDebugLabel;
     bool mHasTiledRendering;
+    bool mHas1BitStencil;
+    bool mHas4BitStencil;
 }; // class Extensions
 
 }; // namespace uirenderer
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 154c0ec..78f9cf5 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -152,7 +152,7 @@
         GradientInfo& info) {
     uint32_t width = 256 * (count - 1);
 
-    if (!Caches::getInstance().extensions.hasNPot()) {
+    if (!Extensions::getInstance().hasNPot()) {
         width = 1 << (31 - __builtin_clz(width));
     }
 
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 79dbfb0..9247b1d 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -41,7 +41,7 @@
     renderer = NULL;
     displayList = NULL;
     fbo = 0;
-    stencil = 0;
+    stencil = NULL;
     debugDrawUpdate = false;
     Caches::getInstance().resourceCache.incrementRefcount(this);
 }
@@ -87,8 +87,8 @@
     }
 
     if (stencil) {
-        bindStencilRenderBuffer();
-        allocateStencilRenderBuffer();
+        stencil->bind();
+        stencil->resize(desiredWidth, desiredHeight);
 
         if (glGetError() != GL_NO_ERROR) {
             setSize(oldWidth, oldHeight);
@@ -108,8 +108,8 @@
         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
         if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
 
-        glDeleteRenderbuffers(1, &stencil);
-        stencil = 0;
+        delete stencil;
+        stencil = NULL;
     }
 
     if (fbo) {
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index e8a85fd..664b2f8 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -26,6 +26,7 @@
 #include <SkXfermode.h>
 
 #include "Rect.h"
+#include "RenderBuffer.h"
 #include "SkiaColorFilter.h"
 #include "Texture.h"
 #include "Vertex.h"
@@ -86,11 +87,11 @@
         deferredUpdateScheduled = true;
     }
 
-    inline uint32_t getWidth() {
+    inline uint32_t getWidth() const {
         return texture.width;
     }
 
-    inline uint32_t getHeight() {
+    inline uint32_t getHeight() const {
         return texture.height;
     }
 
@@ -116,7 +117,7 @@
         texture.blend = blend;
     }
 
-    inline bool isBlend() {
+    inline bool isBlend() const {
         return texture.blend;
     }
 
@@ -129,11 +130,11 @@
         this->mode = mode;
     }
 
-    inline int getAlpha() {
+    inline int getAlpha() const {
         return alpha;
     }
 
-    inline SkXfermode::Mode getMode() {
+    inline SkXfermode::Mode getMode() const {
         return mode;
     }
 
@@ -141,7 +142,7 @@
         this->empty = empty;
     }
 
-    inline bool isEmpty() {
+    inline bool isEmpty() const {
         return empty;
     }
 
@@ -149,23 +150,29 @@
         this->fbo = fbo;
     }
 
-    inline GLuint getFbo() {
+    inline GLuint getFbo() const {
         return fbo;
     }
 
-    inline void setStencilRenderBuffer(GLuint renderBuffer) {
-        this->stencil = renderBuffer;
+    inline void setStencilRenderBuffer(RenderBuffer* renderBuffer) {
+        if (RenderBuffer::isStencilBuffer(renderBuffer->getFormat())) {
+            this->stencil = renderBuffer;
+            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
+                    GL_RENDERBUFFER, stencil->getName());
+        } else {
+            ALOGE("The specified render buffer is not a stencil buffer");
+        }
     }
 
-    inline GLuint getStencilRenderBuffer() {
+    inline RenderBuffer* getStencilRenderBuffer() const {
         return stencil;
     }
 
-    inline GLuint getTexture() {
+    inline GLuint getTexture() const {
         return texture.id;
     }
 
-    inline GLenum getRenderTarget() {
+    inline GLenum getRenderTarget() const {
         return renderTarget;
     }
 
@@ -181,7 +188,7 @@
         texture.setFilter(filter, bindTexture, force, renderTarget);
     }
 
-    inline bool isCacheable() {
+    inline bool isCacheable() const {
         return cacheable;
     }
 
@@ -189,7 +196,7 @@
         this->cacheable = cacheable;
     }
 
-    inline bool isDirty() {
+    inline bool isDirty() const {
         return dirty;
     }
 
@@ -197,7 +204,7 @@
         this->dirty = dirty;
     }
 
-    inline bool isTextureLayer() {
+    inline bool isTextureLayer() const {
         return textureLayer;
     }
 
@@ -205,21 +212,21 @@
         this->textureLayer = textureLayer;
     }
 
-    inline SkiaColorFilter* getColorFilter() {
+    inline SkiaColorFilter* getColorFilter() const {
         return colorFilter;
     }
 
     ANDROID_API void setColorFilter(SkiaColorFilter* filter);
 
-    inline void bindTexture() {
+    inline void bindTexture() const {
         if (texture.id) {
             glBindTexture(renderTarget, texture.id);
         }
     }
 
-    inline void bindStencilRenderBuffer() {
+    inline void bindStencilRenderBuffer() const {
         if (stencil) {
-            glBindRenderbuffer(GL_RENDERBUFFER, stencil);
+            stencil->bind();
         }
     }
 
@@ -255,12 +262,6 @@
         }
     }
 
-    inline void allocateStencilRenderBuffer() {
-        if (stencil) {
-            glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, getWidth(), getHeight());
-        }
-    }
-
     inline mat4& getTexTransform() {
         return texTransform;
     }
@@ -317,10 +318,9 @@
     GLuint fbo;
 
     /**
-     * Name of the render buffer used as the stencil buffer. If the
-     * name is 0, this layer does not have a stencil buffer.
+     * The render buffer used as the stencil buffer.
      */
-    GLuint stencil;
+    RenderBuffer* stencil;
 
     /**
      * Indicates whether this layer has been used already.
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index c8a8ed4..bc660cd 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -351,7 +351,7 @@
     if (layer && fbo) {
         // If possible, discard any enqueud operations on deferred
         // rendering architectures
-        if (Caches::getInstance().extensions.hasDiscardFramebuffer()) {
+        if (Extensions::getInstance().hasDiscardFramebuffer()) {
             GLuint previousFbo;
             glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
             if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo);
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 45569ac..3cec87d 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -109,7 +109,8 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-OpenGLRenderer::OpenGLRenderer(): mCaches(Caches::getInstance()) {
+OpenGLRenderer::OpenGLRenderer():
+        mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) {
     mShader = NULL;
     mColorFilter = NULL;
     mHasShadow = false;
@@ -216,7 +217,7 @@
     // If we know that we are going to redraw the entire framebuffer,
     // perform a discard to let the driver know we don't need to preserve
     // the back buffer for this frame.
-    if (mCaches.extensions.hasDiscardFramebuffer() &&
+    if (mExtensions.hasDiscardFramebuffer() &&
             left <= 0.0f && top <= 0.0f && right >= mWidth && bottom >= mHeight) {
         const bool isFbo = getTargetFbo() == 0;
         const GLenum attachments[] = {
@@ -1119,7 +1120,7 @@
         it.next();
     }
 
-    drawColorRects(rects.array(), count, color, mode, true, dirty);
+    drawColorRects(rects.array(), count, color, mode, true, dirty, false);
 }
 
 void OpenGLRenderer::dirtyLayer(const float left, const float top,
@@ -1269,15 +1270,12 @@
         // 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);
+        RenderBuffer* buffer = new RenderBuffer(
+                Stencil::getSmallestStencilFormat(), layer->getWidth(), layer->getHeight());
+        buffer->bind();
+        buffer->allocate();
 
         layer->setStencilRenderBuffer(buffer);
-        layer->bindStencilRenderBuffer();
-        layer->allocateStencilRenderBuffer();
-
-        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, buffer);
 
         startTiling(layer->clipRect, layer->layer.getHeight());
     }
@@ -1525,13 +1523,13 @@
 
 void OpenGLRenderer::setupDrawShader() {
     if (mShader) {
-        mShader->describe(mDescription, mCaches.extensions);
+        mShader->describe(mDescription, mExtensions);
     }
 }
 
 void OpenGLRenderer::setupDrawColorFilter() {
     if (mColorFilter) {
-        mColorFilter->describe(mDescription, mCaches.extensions);
+        mColorFilter->describe(mDescription, mExtensions);
     }
 }
 
@@ -3207,7 +3205,7 @@
 }
 
 status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color,
-        SkXfermode::Mode mode, bool ignoreTransform, bool dirty) {
+        SkXfermode::Mode mode, bool ignoreTransform, bool dirty, bool clip) {
 
     float left = FLT_MAX;
     float top = FLT_MAX;
@@ -3241,7 +3239,7 @@
         }
     }
 
-    if (count == 0 || quickReject(left, top, right, bottom)) {
+    if (count == 0 || (clip && quickReject(left, top, right, bottom))) {
         return DrawGlInfo::kStatusDone;
     }
 
@@ -3386,7 +3384,7 @@
         // If the blend mode cannot be implemented using shaders, fall
         // back to the default SrcOver blend mode instead
         if (CC_UNLIKELY(mode > SkXfermode::kScreen_Mode)) {
-            if (CC_UNLIKELY(mCaches.extensions.hasFramebufferFetch())) {
+            if (CC_UNLIKELY(mExtensions.hasFramebufferFetch())) {
                 description.framebufferMode = mode;
                 description.swapSrcDst = swapSrcDst;
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 750b3d2..81af7b7 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -566,9 +566,11 @@
      * @param mode The Skia xfermode to use
      * @param ignoreTransform True if the current transform should be ignored
      * @param dirty True if calling this method should dirty the current layer
+     * @param clip True if the rects should be clipped, false otherwise
      */
     status_t drawColorRects(const float* rects, int count, int color,
-            SkXfermode::Mode mode, bool ignoreTransform = false, bool dirty = true);
+            SkXfermode::Mode mode, bool ignoreTransform = false,
+            bool dirty = true, bool clip = true);
 
     /**
      * Draws the shape represented by the specified path texture.
@@ -881,6 +883,7 @@
 
     // Various caches
     Caches& mCaches;
+    Extensions& mExtensions;
 
     // List of rectangles to clear after saveLayer() is invoked
     Vector<Rect*> mLayers;
diff --git a/libs/hwui/RenderBuffer.h b/libs/hwui/RenderBuffer.h
new file mode 100644
index 0000000..927f265
--- /dev/null
+++ b/libs/hwui/RenderBuffer.h
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ANDROID_HWUI_RENDER_BUFFER_H
+#define ANDROID_HWUI_RENDER_BUFFER_H
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Represents an OpenGL render buffer. Render buffers are attached
+ * to layers to perform stencil work.
+ */
+struct RenderBuffer {
+    /**
+     * Creates a new render buffer in the specified format and dimensions.
+     * The format must be one of the formats allowed by glRenderbufferStorage().
+     */
+    RenderBuffer(GLenum format, uint32_t width, uint32_t height):
+        mFormat(format), mWidth(width), mHeight(height), mAllocated(false) {
+
+        glGenRenderbuffers(1, &mName);
+    }
+
+    ~RenderBuffer() {
+        if (mName && mAllocated) {
+            glDeleteRenderbuffers(1, &mName);
+        }
+    }
+
+    /**
+     * Returns the GL name of this render buffer.
+     */
+    GLuint getName() const {
+        return mName;
+    }
+
+    /**
+     * Returns the format of this render buffer.
+     */
+    GLenum getFormat() const {
+        return mFormat;
+    }
+
+    /**
+     * Binds this render buffer to the current GL context.
+     */
+    void bind() const {
+        glBindRenderbuffer(GL_RENDERBUFFER, mName);
+    }
+
+    /**
+     * Indicates whether this render buffer has allocated its
+     * storage. See allocate() and resize().
+     */
+    bool isAllocated() const {
+        return mAllocated;
+    }
+
+    /**
+     * Allocates this render buffer's storage if needed.
+     * This method doesn't do anything if isAllocated() returns true.
+     */
+    void allocate() {
+        if (!mAllocated) {
+            glRenderbufferStorage(GL_RENDERBUFFER, mFormat, mWidth, mHeight);
+            mAllocated = true;
+        }
+    }
+
+    /**
+     * Resizes this render buffer. If the buffer was previously allocated,
+     * the storage is re-allocated wit the new specified dimensions. If the
+     * buffer wasn't previously allocated, the buffer remains unallocated.
+     */
+    void resize(uint32_t width, uint32_t height) {
+        if (isAllocated() && (width != mWidth || height != mHeight)) {
+            glRenderbufferStorage(GL_RENDERBUFFER, mFormat, width, height);
+        }
+
+        mWidth = width;
+        mHeight = height;
+    }
+
+    /**
+     * Returns the width of the render buffer in pixels.
+     */
+    uint32_t getWidth() const {
+        return mWidth;
+    }
+
+    /**
+     * Returns the height of the render buffer in pixels.
+     */
+    uint32_t getHeight() const {
+        return mHeight;
+    }
+
+    /**
+     * Returns the size of this render buffer in bytes.
+     */
+    uint32_t getSize() const {
+        // Round to the nearest byte
+        return (uint32_t) ((mWidth * mHeight * formatSize(mFormat)) / 8.0f + 0.5f);
+    }
+
+    /**
+     * Returns the number of bits per component in the specified format.
+     * The format must be one of the formats allowed by glRenderbufferStorage().
+     */
+    static uint32_t formatSize(GLenum format) {
+        switch (format) {
+            case GL_STENCIL_INDEX8:
+                return 8;
+            case GL_STENCIL_INDEX1_OES:
+                return 1;
+            case GL_STENCIL_INDEX4_OES:
+                return 4;
+            case GL_DEPTH_COMPONENT16:
+            case GL_RGBA4:
+            case GL_RGB565:
+            case GL_RGB5_A1:
+                return 16;
+        }
+        return 0;
+    }
+
+    /**
+     * Indicates whether the specified format represents a stencil buffer.
+     */
+    static bool isStencilBuffer(GLenum format) {
+        switch (format) {
+            case GL_STENCIL_INDEX8:
+            case GL_STENCIL_INDEX1_OES:
+            case GL_STENCIL_INDEX4_OES:
+                return true;
+        }
+        return false;
+    }
+
+private:
+    GLenum mFormat;
+
+    uint32_t mWidth;
+    uint32_t mHeight;
+
+    bool mAllocated;
+
+    GLuint mName;
+}; // struct RenderBuffer
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_RENDER_BUFFER_H
diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp
index 87ed825..19a5db7 100644
--- a/libs/hwui/Snapshot.cpp
+++ b/libs/hwui/Snapshot.cpp
@@ -183,6 +183,9 @@
 }
 
 void Snapshot::resetClip(float left, float top, float right, float bottom) {
+    // TODO: This is incorrect, when we start rendering into a new layer,
+    // we may have to modify the previous snapshot's clip rect and clip
+    // region if the previous restore() call did not restore the clip
     clipRect = &mClipRectRoot;
     clipRegion = &mClipRegionRoot;
     setClip(left, top, right, bottom);
diff --git a/libs/hwui/Stencil.cpp b/libs/hwui/Stencil.cpp
index 4fcd51d..ba2e6f2 100644
--- a/libs/hwui/Stencil.cpp
+++ b/libs/hwui/Stencil.cpp
@@ -14,14 +14,23 @@
  * limitations under the License.
  */
 
-#include <GLES2/gl2.h>
-
+#include "Extensions.h"
 #include "Properties.h"
 #include "Stencil.h"
 
+#include <GLES2/gl2ext.h>
+
 namespace android {
 namespace uirenderer {
 
+#if DEBUG_STENCIL
+#define STENCIL_WRITE_VALUE 0xff
+#define STENCIL_MASK_VALUE 0xff
+#else
+#define STENCIL_WRITE_VALUE 0x1
+#define STENCIL_MASK_VALUE 0x1
+#endif
+
 Stencil::Stencil(): mState(kDisabled) {
 }
 
@@ -29,6 +38,18 @@
     return STENCIL_BUFFER_SIZE;
 }
 
+GLenum Stencil::getSmallestStencilFormat() {
+#if !DEBUG_STENCIL
+    const Extensions& extensions = Extensions::getInstance();
+    if (extensions.has1BitStencil()) {
+        return GL_STENCIL_INDEX1_OES;
+    } else if (extensions.has4BitStencil()) {
+        return GL_STENCIL_INDEX4_OES;
+    }
+#endif
+    return GL_STENCIL_INDEX8;
+}
+
 void Stencil::clear() {
     glClearStencil(0);
     glClear(GL_STENCIL_BUFFER_BIT);
@@ -37,7 +58,7 @@
 void Stencil::enableTest() {
     if (mState != kTest) {
         enable();
-        glStencilFunc(GL_EQUAL, 0xff, 0xff);
+        glStencilFunc(GL_EQUAL, STENCIL_WRITE_VALUE, STENCIL_MASK_VALUE);
         // We only want to test, let's keep everything
         glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
         glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
@@ -48,7 +69,7 @@
 void Stencil::enableWrite() {
     if (mState != kWrite) {
         enable();
-        glStencilFunc(GL_ALWAYS, 0xff, 0xff);
+        glStencilFunc(GL_ALWAYS, STENCIL_WRITE_VALUE, STENCIL_MASK_VALUE);
         // The test always passes so the first two values are meaningless
         glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
         glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
diff --git a/libs/hwui/Stencil.h b/libs/hwui/Stencil.h
index 2f8a66a..047f7ee 100644
--- a/libs/hwui/Stencil.h
+++ b/libs/hwui/Stencil.h
@@ -21,6 +21,8 @@
     #define LOG_TAG "OpenGLRenderer"
 #endif
 
+#include <GLES2/gl2.h>
+
 #include <cutils/compiler.h>
 
 namespace android {
@@ -41,6 +43,11 @@
     ANDROID_API static uint32_t getStencilSize();
 
     /**
+     * Returns the smallest stencil format accepted by render buffers.
+     */
+    static GLenum getSmallestStencilFormat();
+
+    /**
      * Clears the stencil buffer.
      */
     void clear();
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index abf2d98..5cff5a5 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -219,7 +219,7 @@
 
     // We could also enable mipmapping if both bitmap dimensions are powers
     // of 2 but we'd have to deal with size changes. Let's keep this simple
-    const bool canMipMap = Caches::getInstance().extensions.hasNPot();
+    const bool canMipMap = Extensions::getInstance().hasNPot();
 
     // If the texture had mipmap enabled but not anymore,
     // force a glTexImage2D to discard the mipmap levels