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