diff options
author | 2013-02-12 16:08:55 -0800 | |
---|---|---|
committer | 2013-02-13 11:27:05 -0800 | |
commit | 8d4aeb7111afac0c3c7e56d4ad5d92f9cfce2ffd (patch) | |
tree | d526296ca8e54842b15b60adaad484ee29feccf2 | |
parent | d1a84242a7ba807310d8f8d389796c7163cb97a0 (diff) |
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
-rw-r--r-- | core/java/android/view/HardwareRenderer.java | 2 | ||||
-rw-r--r-- | libs/hwui/Android.mk | 1 | ||||
-rw-r--r-- | libs/hwui/Caches.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/Caches.h | 2 | ||||
-rw-r--r-- | libs/hwui/Debug.h | 3 | ||||
-rw-r--r-- | libs/hwui/Layer.cpp | 3 | ||||
-rw-r--r-- | libs/hwui/LayerCache.cpp | 1 | ||||
-rw-r--r-- | libs/hwui/LayerCache.h | 22 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 5 | ||||
-rw-r--r-- | libs/hwui/Properties.h | 6 | ||||
-rw-r--r-- | libs/hwui/RenderBuffer.h | 25 | ||||
-rw-r--r-- | libs/hwui/RenderBufferCache.cpp | 166 | ||||
-rw-r--r-- | libs/hwui/RenderBufferCache.h | 130 |
13 files changed, 347 insertions, 23 deletions
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index e0d48a4dfa28..7c4bcfde5a53 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -48,7 +48,7 @@ import java.util.concurrent.locks.ReentrantLock; 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 2111a56578c2..d533fa0b9ec1 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -28,6 +28,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) 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 492bb7d171f5..74201d1fda58 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -186,6 +186,8 @@ void Caches::dumpMemoryUsage(String8 &log) { 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 @@ void Caches::dumpMemoryUsage(String8 &log) { 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 @@ void Caches::flush(FlushMode mode) { // fall through case kFlushMode_Layers: layerCache.clear(); + renderBufferCache.clear(); break; } diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 0fa54fb9f2f2..d70c0e3fe77a 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 @@ public: 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 5f8baacb710c..20eb5e123e37 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 9247b1da3df2..1899002004c4 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -101,14 +101,13 @@ bool Layer::resize(const uint32_t width, const uint32_t height) { 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 427846459162..a0709afa010f 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 7720b4232ae9..221bfe000de1 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 @@ public: * 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 @@ public: */ void dump(); +private: struct LayerEntry { LayerEntry(): mLayer(NULL), mWidth(0), mHeight(0) { @@ -115,12 +115,19 @@ public: 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 @@ private: 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 62f268dab291..2431e54cde8f 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -1273,11 +1273,8 @@ void OpenGLRenderer::attachStencilBufferToLayer(Layer* layer) { // 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 1e8765bae9a2..0c7524203826 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 @@ enum DebugLevel { // 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 @@ enum DebugLevel { #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 927f2659abd0..a9ad3d7df5dd 100644 --- a/libs/hwui/RenderBuffer.h +++ b/libs/hwui/RenderBuffer.h @@ -39,7 +39,7 @@ struct RenderBuffer { } ~RenderBuffer() { - if (mName && mAllocated) { + if (mName) { glDeleteRenderbuffers(1, &mName); } } @@ -154,6 +154,29 @@ struct RenderBuffer { 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 000000000000..830a13ab28a7 --- /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 000000000000..af8060fe2d21 --- /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 |