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
This commit is contained in:
Romain Guy
2013-02-12 16:08:55 -08:00
parent d1a84242a7
commit 8d4aeb7111
13 changed files with 347 additions and 23 deletions

View File

@@ -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
*/

View File

@@ -28,6 +28,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
PathTessellator.cpp \
Program.cpp \
ProgramCache.cpp \
RenderBufferCache.cpp \
ResourceCache.cpp \
ShapeCache.cpp \
SkiaColorFilter.cpp \

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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;
}

View File

@@ -21,7 +21,6 @@
#include <utils/Log.h>
#include "Caches.h"
#include "Debug.h"
#include "LayerCache.h"
#include "Properties.h"

View File

@@ -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

View File

@@ -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());

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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