Add a render buffer cache to reuse stencil buffers
Bug #7146141
This new cache is used in a similar way to LayerCache. It helps
reuse already allocated stencil buffers and thus avoid churning
memory on every frame.
Change-Id: I19551d72da52c40039e65904563600e492c8b193
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index e0d48a4..7c4bcfd 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -48,7 +48,7 @@
import static javax.microedition.khronos.egl.EGL10.*;
/**
- * Interface for rendering a ViewAncestor using hardware acceleration.
+ * Interface for rendering a view hierarchy using hardware acceleration.
*
* @hide
*/
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 2111a56..d533fa0 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -28,6 +28,7 @@
PathTessellator.cpp \
Program.cpp \
ProgramCache.cpp \
+ RenderBufferCache.cpp \
ResourceCache.cpp \
ShapeCache.cpp \
SkiaColorFilter.cpp \
diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp
index 492bb7d..74201d1 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -186,6 +186,8 @@
textureCache.getSize(), textureCache.getMaxSize());
log.appendFormat(" LayerCache %8d / %8d\n",
layerCache.getSize(), layerCache.getMaxSize());
+ log.appendFormat(" RenderBufferCache %8d / %8d\n",
+ renderBufferCache.getSize(), renderBufferCache.getMaxSize());
log.appendFormat(" GradientCache %8d / %8d\n",
gradientCache.getSize(), gradientCache.getMaxSize());
log.appendFormat(" PathCache %8d / %8d\n",
@@ -215,6 +217,7 @@
uint32_t total = 0;
total += textureCache.getSize();
total += layerCache.getSize();
+ total += renderBufferCache.getSize();
total += gradientCache.getSize();
total += pathCache.getSize();
total += dropShadowCache.getSize();
@@ -298,6 +301,7 @@
// fall through
case kFlushMode_Layers:
layerCache.clear();
+ renderBufferCache.clear();
break;
}
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 0fa54fb..d70c0e3 100644
--- a/libs/hwui/Caches.h
+++ b/libs/hwui/Caches.h
@@ -29,6 +29,7 @@
#include "GammaFontRenderer.h"
#include "TextureCache.h"
#include "LayerCache.h"
+#include "RenderBufferCache.h"
#include "GradientCache.h"
#include "PatchCache.h"
#include "ProgramCache.h"
@@ -249,6 +250,7 @@
TextureCache textureCache;
LayerCache layerCache;
+ RenderBufferCache renderBufferCache;
GradientCache gradientCache;
ProgramCache programCache;
PathCache pathCache;
diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h
index 5f8baac..20eb5e1 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 display info about render buffers
+#define DEBUG_RENDER_BUFFERS 0
+
// Turn on to make stencil operations easier to debug
#define DEBUG_STENCIL 0
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 9247b1d..1899002 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -101,14 +101,13 @@
void Layer::removeFbo(bool flush) {
if (stencil) {
- // TODO: recycle & cache instead of simply deleting
GLuint previousFbo;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
- delete stencil;
+ Caches::getInstance().renderBufferCache.put(stencil);
stencil = NULL;
}
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 4278464..a0709af 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -21,7 +21,6 @@
#include <utils/Log.h>
#include "Caches.h"
-#include "Debug.h"
#include "LayerCache.h"
#include "Properties.h"
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index 7720b42..221bfe0 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -19,7 +19,6 @@
#include "Debug.h"
#include "Layer.h"
-#include "Properties.h"
#include "utils/SortedList.h"
namespace android {
@@ -54,7 +53,7 @@
* size of the cache goes down.
*
* @param width The desired width of the layer
- * @param width The desired height of the layer
+ * @param height The desired height of the layer
*/
Layer* get(const uint32_t width, const uint32_t height);
@@ -91,6 +90,7 @@
*/
void dump();
+private:
struct LayerEntry {
LayerEntry():
mLayer(NULL), mWidth(0), mHeight(0) {
@@ -115,12 +115,19 @@
return compare(*this, other) != 0;
}
+ friend inline int strictly_order_type(const LayerEntry& lhs, const LayerEntry& rhs) {
+ return LayerEntry::compare(lhs, rhs) < 0;
+ }
+
+ friend inline int compare_type(const LayerEntry& lhs, const LayerEntry& rhs) {
+ return LayerEntry::compare(lhs, rhs);
+ }
+
Layer* mLayer;
uint32_t mWidth;
uint32_t mHeight;
}; // struct LayerEntry
-private:
void deleteLayer(Layer* layer);
SortedList<LayerEntry> mCache;
@@ -129,15 +136,6 @@
uint32_t mMaxSize;
}; // class LayerCache
-inline int strictly_order_type(const LayerCache::LayerEntry& lhs,
- const LayerCache::LayerEntry& rhs) {
- return LayerCache::LayerEntry::compare(lhs, rhs) < 0;
-}
-
-inline int compare_type(const LayerCache::LayerEntry& lhs, const LayerCache::LayerEntry& rhs) {
- return LayerCache::LayerEntry::compare(lhs, rhs);
-}
-
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 62f268d..2431e54 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1273,11 +1273,8 @@
// attach the new render buffer then turn tiling back on
endTiling();
- RenderBuffer* buffer = new RenderBuffer(
+ RenderBuffer* buffer = mCaches.renderBufferCache.get(
Stencil::getSmallestStencilFormat(), layer->getWidth(), layer->getHeight());
- buffer->bind();
- buffer->allocate();
-
layer->setStencilRenderBuffer(buffer);
startTiling(layer->clipRect, layer->layer.getHeight());
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 1e8765b..0c75242 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -34,9 +34,9 @@
// Textures used by layers must have dimensions multiples of this number
#define LAYER_SIZE 64
-// Defines the size in bits of the stencil buffer
+// Defines the size in bits of the stencil buffer for the framebuffer
// Note: Only 1 bit is required for clipping but more bits are required
-// to properly implement the winding fill rule when rasterizing paths
+// to properly implement overdraw debugging
#define STENCIL_BUFFER_SIZE 8
/**
@@ -85,6 +85,7 @@
// These properties are defined in mega-bytes
#define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
#define PROPERTY_LAYER_CACHE_SIZE "ro.hwui.layer_cache_size"
+#define PROPERTY_RENDER_BUFFER_CACHE_SIZE "ro.hwui.r_buffer_cache_size"
#define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size"
#define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size"
#define PROPERTY_SHAPE_CACHE_SIZE "ro.hwui.shape_cache_size"
@@ -130,6 +131,7 @@
#define DEFAULT_TEXTURE_CACHE_SIZE 24.0f
#define DEFAULT_LAYER_CACHE_SIZE 16.0f
+#define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f
#define DEFAULT_PATH_CACHE_SIZE 4.0f
#define DEFAULT_SHAPE_CACHE_SIZE 1.0f
#define DEFAULT_PATCH_CACHE_SIZE 512
diff --git a/libs/hwui/RenderBuffer.h b/libs/hwui/RenderBuffer.h
index 927f265..a9ad3d7 100644
--- a/libs/hwui/RenderBuffer.h
+++ b/libs/hwui/RenderBuffer.h
@@ -39,7 +39,7 @@
}
~RenderBuffer() {
- if (mName && mAllocated) {
+ if (mName) {
glDeleteRenderbuffers(1, &mName);
}
}
@@ -154,6 +154,29 @@
return false;
}
+ /**
+ * Returns the name of the specified render buffer format.
+ */
+ static const char* formatName(GLenum format) {
+ switch (format) {
+ case GL_STENCIL_INDEX8:
+ return "STENCIL_8";
+ case GL_STENCIL_INDEX1_OES:
+ return "STENCIL_1";
+ case GL_STENCIL_INDEX4_OES:
+ return "STENCIL_4";
+ case GL_DEPTH_COMPONENT16:
+ return "DEPTH_16";
+ case GL_RGBA4:
+ return "RGBA_444";
+ case GL_RGB565:
+ return "RGB_565";
+ case GL_RGB5_A1:
+ return "RGBA_5551";
+ }
+ return "Unknown";
+ }
+
private:
GLenum mFormat;
diff --git a/libs/hwui/RenderBufferCache.cpp b/libs/hwui/RenderBufferCache.cpp
new file mode 100644
index 0000000..830a13a
--- /dev/null
+++ b/libs/hwui/RenderBufferCache.cpp
@@ -0,0 +1,166 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "OpenGLRenderer"
+
+#include <utils/Log.h>
+
+#include "Debug.h"
+#include "Properties.h"
+#include "RenderBufferCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+// Debug
+#if DEBUG_RENDER_BUFFERS
+ #define RENDER_BUFFER_LOGD(...) ALOGD(__VA_ARGS__)
+#else
+ #define RENDER_BUFFER_LOGD(...)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+RenderBufferCache::RenderBufferCache(): mSize(0), mMaxSize(MB(DEFAULT_RENDER_BUFFER_CACHE_SIZE)) {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_RENDER_BUFFER_CACHE_SIZE, property, NULL) > 0) {
+ INIT_LOGD(" Setting render buffer cache size to %sMB", property);
+ setMaxSize(MB(atof(property)));
+ } else {
+ INIT_LOGD(" Using default render buffer cache size of %.2fMB",
+ DEFAULT_RENDER_BUFFER_CACHE_SIZE);
+ }
+}
+
+RenderBufferCache::~RenderBufferCache() {
+ clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t RenderBufferCache::getSize() {
+ return mSize;
+}
+
+uint32_t RenderBufferCache::getMaxSize() {
+ return mMaxSize;
+}
+
+void RenderBufferCache::setMaxSize(uint32_t maxSize) {
+ clear();
+ mMaxSize = maxSize;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+int RenderBufferCache::RenderBufferEntry::compare(
+ const RenderBufferCache::RenderBufferEntry& lhs,
+ const RenderBufferCache::RenderBufferEntry& rhs) {
+ int deltaInt = int(lhs.mWidth) - int(rhs.mWidth);
+ if (deltaInt != 0) return deltaInt;
+
+ deltaInt = int(lhs.mHeight) - int(rhs.mHeight);
+ if (deltaInt != 0) return deltaInt;
+
+ return int(lhs.mFormat) - int(rhs.mFormat);
+}
+
+void RenderBufferCache::deleteBuffer(RenderBuffer* buffer) {
+ if (buffer) {
+ RENDER_BUFFER_LOGD("Deleted %s render buffer (%dx%d)",
+ RenderBuffer::formatName(buffer->getFormat()),
+ buffer->getWidth(), buffer->getHeight());
+
+ mSize -= buffer->getSize();
+ delete buffer;
+ }
+}
+
+void RenderBufferCache::clear() {
+ size_t count = mCache.size();
+ for (size_t i = 0; i < count; i++) {
+ deleteBuffer(mCache.itemAt(i).mBuffer);
+ }
+ mCache.clear();
+}
+
+RenderBuffer* RenderBufferCache::get(GLenum format, const uint32_t width, const uint32_t height) {
+ RenderBuffer* buffer = NULL;
+
+ RenderBufferEntry entry(format, width, height);
+ ssize_t index = mCache.indexOf(entry);
+
+ if (index >= 0) {
+ entry = mCache.itemAt(index);
+ mCache.removeAt(index);
+
+ buffer = entry.mBuffer;
+ mSize -= buffer->getSize();
+
+ RENDER_BUFFER_LOGD("Found %s render buffer (%dx%d)",
+ RenderBuffer::formatName(format), width, height);
+ } else {
+ buffer = new RenderBuffer(format, width, height);
+
+ RENDER_BUFFER_LOGD("Created new %s render buffer (%dx%d)",
+ RenderBuffer::formatName(format), width, height);
+ }
+
+ buffer->bind();
+ buffer->allocate();
+
+ return buffer;
+}
+
+bool RenderBufferCache::put(RenderBuffer* buffer) {
+ if (!buffer) return false;
+
+ const uint32_t size = buffer->getSize();
+ if (size < mMaxSize) {
+ while (mSize + size > mMaxSize) {
+ size_t position = 0;
+
+ RenderBuffer* victim = mCache.itemAt(position).mBuffer;
+ deleteBuffer(victim);
+ mCache.removeAt(position);
+ }
+
+ RenderBufferEntry entry(buffer);
+
+ mCache.add(entry);
+ mSize += size;
+
+ RENDER_BUFFER_LOGD("Added %s render buffer (%dx%d)",
+ RenderBuffer::formatName(buffer->getFormat()),
+ buffer->getWidth(), buffer->getHeight());
+
+ return true;
+ }
+ return false;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/RenderBufferCache.h b/libs/hwui/RenderBufferCache.h
new file mode 100644
index 0000000..af8060f
--- /dev/null
+++ b/libs/hwui/RenderBufferCache.h
@@ -0,0 +1,130 @@
+/*
+ * 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_CACHE_H
+#define ANDROID_HWUI_RENDER_BUFFER_CACHE_H
+
+#include <GLES2/gl2.h>
+
+#include "RenderBuffer.h"
+#include "utils/SortedList.h"
+
+namespace android {
+namespace uirenderer {
+
+class RenderBufferCache {
+public:
+ RenderBufferCache();
+ ~RenderBufferCache();
+
+ /**
+ * Returns a buffer with the exact specified dimensions. If no suitable
+ * buffer can be found, a new one is created and returned. If creating a
+ * new buffer fails, NULL is returned.
+ *
+ * When a buffer is obtained from the cache, it is removed and the total
+ * size of the cache goes down.
+ *
+ * The returned buffer is always allocated and bound
+ * (see RenderBuffer::isAllocated()).
+ *
+ * @param format The desired render buffer format
+ * @param width The desired width of the buffer
+ * @param height The desired height of the buffer
+ */
+ RenderBuffer* get(GLenum format, const uint32_t width, const uint32_t height);
+
+ /**
+ * Adds the buffer to the cache. The buffer will not be added if there is
+ * not enough space available. Adding a buffer can cause other buffer to
+ * be removed from the cache.
+ *
+ * @param buffer The render buffer to add to the cache
+ *
+ * @return True if the buffer was added, false otherwise.
+ */
+ bool put(RenderBuffer* buffer);
+ /**
+ * Clears the cache. This causes all layers to be deleted.
+ */
+ void clear();
+
+ /**
+ * Sets the maximum size of the cache in bytes.
+ */
+ void setMaxSize(uint32_t maxSize);
+ /**
+ * Returns the maximum size of the cache in bytes.
+ */
+ uint32_t getMaxSize();
+ /**
+ * Returns the current size of the cache in bytes.
+ */
+ uint32_t getSize();
+
+private:
+ struct RenderBufferEntry {
+ RenderBufferEntry():
+ mBuffer(NULL), mWidth(0), mHeight(0) {
+ }
+
+ RenderBufferEntry(GLenum format, const uint32_t width, const uint32_t height):
+ mBuffer(NULL), mFormat(format), mWidth(width), mHeight(height) {
+ }
+
+ RenderBufferEntry(RenderBuffer* buffer):
+ mBuffer(buffer), mFormat(buffer->getFormat()),
+ mWidth(buffer->getWidth()), mHeight(buffer->getHeight()) {
+ }
+
+ static int compare(const RenderBufferEntry& lhs, const RenderBufferEntry& rhs);
+
+ bool operator==(const RenderBufferEntry& other) const {
+ return compare(*this, other) == 0;
+ }
+
+ bool operator!=(const RenderBufferEntry& other) const {
+ return compare(*this, other) != 0;
+ }
+
+ friend inline int strictly_order_type(const RenderBufferEntry& lhs,
+ const RenderBufferEntry& rhs) {
+ return RenderBufferEntry::compare(lhs, rhs) < 0;
+ }
+
+ friend inline int compare_type(const RenderBufferEntry& lhs,
+ const RenderBufferEntry& rhs) {
+ return RenderBufferEntry::compare(lhs, rhs);
+ }
+
+ RenderBuffer* mBuffer;
+ GLenum mFormat;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ }; // struct RenderBufferEntry
+
+ void deleteBuffer(RenderBuffer* buffer);
+
+ SortedList<RenderBufferEntry> mCache;
+
+ uint32_t mSize;
+ uint32_t mMaxSize;
+}; // class RenderBufferCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_RENDER_BUFFER_CACHE_H