Add a layer (FBO) cache.
The cache is used to draw layers so that a new
texture does not have to be recreated every time
a call to saveLayer() happens.
The FBO cache used a KeyedVector, which is a bad
idea. The cache should be able to store several
FBOs of the same size (this happens a lot during
scrolling with fading edges for instance.) This
will be changed in a future CL.
Change-Id: Ic316189e625f0dbcf0d273a71cc981a433d48726
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 95ebda1..394aeca 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -2,6 +2,7 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
+ LayerCache.cpp \
Matrix.cpp \
OpenGLRenderer.cpp \
Program.cpp \
diff --git a/libs/hwui/GenerationCache.h b/libs/hwui/GenerationCache.h
index 56693da..d92a0bb 100644
--- a/libs/hwui/GenerationCache.h
+++ b/libs/hwui/GenerationCache.h
@@ -27,7 +27,7 @@
class OnEntryRemoved {
public:
virtual ~OnEntryRemoved() { };
- virtual void operator()(EntryKey key, EntryValue value) = 0;
+ virtual void operator()(EntryKey& key, EntryValue& value) = 0;
}; // class OnEntryRemoved
template<typename K, typename V>
@@ -40,15 +40,15 @@
kUnlimitedCapacity,
};
- void setOnEntryRemovedListener(OnEntryRemoved<K*, V*>* listener);
+ void setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener);
void clear();
- bool contains(K* key) const;
- V* get(K* key);
- void put(K* key, V* value);
- V* remove(K* key);
- void removeOldest();
+ bool contains(K key) const;
+ V get(K key);
+ void put(K key, V value);
+ V remove(K key);
+ V removeOldest();
uint32_t size() const;
@@ -68,18 +68,18 @@
sp<Entry<EntryKey, EntryValue> > child;
}; // struct Entry
- void addToCache(sp<Entry<K*, V*> > entry, K* key, V* value);
- void attachToCache(sp<Entry<K*, V*> > entry);
- void detachFromCache(sp<Entry<K*, V*> > entry);
+ void addToCache(sp<Entry<K, V> > entry, K key, V value);
+ void attachToCache(sp<Entry<K, V> > entry);
+ void detachFromCache(sp<Entry<K, V> > entry);
uint32_t mMaxCapacity;
- OnEntryRemoved<K*, V*>* mListener;
+ OnEntryRemoved<K, V>* mListener;
- KeyedVector<K*, sp<Entry<K*, V*> > > mCache;
+ KeyedVector<K, sp<Entry<K, V> > > mCache;
- sp<Entry<K*, V*> > mOldest;
- sp<Entry<K*, V*> > mYougest;
+ sp<Entry<K, V> > mOldest;
+ sp<Entry<K, V> > mYougest;
}; // class GenerationCache
template<typename K, typename V>
@@ -88,7 +88,7 @@
}
template<typename K, typename V>
-void GenerationCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K*, V*>* listener) {
+void GenerationCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) {
mListener = listener;
}
@@ -106,15 +106,15 @@
}
template<typename K, typename V>
-bool GenerationCache<K, V>::contains(K* key) const {
+bool GenerationCache<K, V>::contains(K key) const {
return mCache.indexOfKey(key) >= 0;
}
template<typename K, typename V>
-V* GenerationCache<K, V>::get(K* key) {
+V GenerationCache<K, V>::get(K key) {
ssize_t index = mCache.indexOfKey(key);
if (index >= 0) {
- sp<Entry<K*, V*> > entry = mCache.valueAt(index);
+ sp<Entry<K, V> > entry = mCache.valueAt(index);
if (entry.get()) {
detachFromCache(entry);
attachToCache(entry);
@@ -126,24 +126,24 @@
}
template<typename K, typename V>
-void GenerationCache<K, V>::put(K* key, V* value) {
+void GenerationCache<K, V>::put(K key, V value) {
if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) {
removeOldest();
}
ssize_t index = mCache.indexOfKey(key);
if (index >= 0) {
- sp<Entry<K*, V*> > entry = mCache.valueAt(index);
+ sp<Entry<K, V> > entry = mCache.valueAt(index);
detachFromCache(entry);
addToCache(entry, key, value);
} else {
- sp<Entry<K*, V*> > entry = new Entry<K*, V*>;
+ sp<Entry<K, V> > entry = new Entry<K, V>;
addToCache(entry, key, value);
}
}
template<typename K, typename V>
-void GenerationCache<K, V>::addToCache(sp<Entry<K*, V*> > entry, K* key, V* value) {
+void GenerationCache<K, V>::addToCache(sp<Entry<K, V> > entry, K key, V value) {
entry->key = key;
entry->value = value;
mCache.add(key, entry);
@@ -151,29 +151,33 @@
}
template<typename K, typename V>
-V* GenerationCache<K, V>::remove(K* key) {
+V GenerationCache<K, V>::remove(K key) {
ssize_t index = mCache.indexOfKey(key);
if (index >= 0) {
- sp<Entry<K*, V*> > entry = mCache.valueAt(index);
+ sp<Entry<K, V> > entry = mCache.valueAt(index);
if (mListener) {
(*mListener)(entry->key, entry->value);
}
mCache.removeItemsAt(index, 1);
detachFromCache(entry);
+
+ return entry->value;
}
return NULL;
}
template<typename K, typename V>
-void GenerationCache<K, V>::removeOldest() {
+V GenerationCache<K, V>::removeOldest() {
if (mOldest.get()) {
- remove(mOldest->key);
+ return remove(mOldest->key);
}
+
+ return NULL;
}
template<typename K, typename V>
-void GenerationCache<K, V>::attachToCache(sp<Entry<K*, V*> > entry) {
+void GenerationCache<K, V>::attachToCache(sp<Entry<K, V> > entry) {
if (!mYougest.get()) {
mYougest = mOldest = entry;
} else {
@@ -184,7 +188,7 @@
}
template<typename K, typename V>
-void GenerationCache<K, V>::detachFromCache(sp<Entry<K*, V*> > entry) {
+void GenerationCache<K, V>::detachFromCache(sp<Entry<K, V> > entry) {
if (entry->parent.get()) {
entry->parent->child = entry->child;
}
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
new file mode 100644
index 0000000..70d64ca
--- /dev/null
+++ b/libs/hwui/Layer.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2010 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_UI_LAYER_H
+#define ANDROID_UI_LAYER_H
+
+#include <GLES2/gl2.h>
+
+#include <SkXfermode.h>
+
+#include "Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Dimensions of a layer.
+ */
+struct LayerSize {
+ LayerSize(): width(0), height(0) { }
+ LayerSize(const uint32_t width, const uint32_t height): width(width), height(height) { }
+ LayerSize(const LayerSize& size): width(size.width), height(size.height) { }
+
+ uint32_t width;
+ uint32_t height;
+
+ bool operator<(const LayerSize& rhs) const {
+ if (width == rhs.width) {
+ return height < rhs.height;
+ }
+ return width < rhs.width;
+ }
+
+ bool operator==(const LayerSize& rhs) const {
+ return width == rhs.width && height == rhs.height;
+ }
+};
+
+/**
+ * A layer has dimensions and is backed by an OpenGL texture.
+ */
+struct Layer {
+ /**
+ * Coordinates of the layer corresponding to this snapshot.
+ * Only set when the flag kFlagIsLayer is set.
+ */
+ Rect layer;
+ /**
+ * Name of the texture used to render the layer.
+ * Only set when the flag kFlagIsLayer is set.
+ */
+ GLuint texture;
+ /**
+ * Name of the FBO used to render the layer.
+ * Only set when the flag kFlagIsLayer is set.
+ */
+ GLuint fbo;
+ /**
+ * Opacity of the layer.
+ * Only set when the flag kFlagIsLayer is set.
+ */
+ float alpha;
+ /**
+ * Blending mode of the layer.
+ * Only set when the flag kFlagIsLayer is set.
+ */
+ SkXfermode::Mode mode;
+ /**
+ * Indicates whether this layer should be blended.
+ */
+ bool blend;
+}; // struct Layer
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_LAYER_H
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
new file mode 100644
index 0000000..0d3214d
--- /dev/null
+++ b/libs/hwui/LayerCache.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2010 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 <GLES2/gl2.h>
+
+#include "LayerCache.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Constructors/destructor
+///////////////////////////////////////////////////////////////////////////////
+
+LayerCache::LayerCache(uint32_t maxByteSize):
+ mCache(GenerationCache<LayerSize, Layer*>::kUnlimitedCapacity),
+ mSize(0), mMaxSize(maxByteSize) {
+}
+
+LayerCache::~LayerCache() {
+ mCache.setOnEntryRemovedListener(this);
+ mCache.clear();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Size management
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t LayerCache::getSize() {
+ return mSize;
+}
+
+uint32_t LayerCache::getMaxSize() {
+ return mMaxSize;
+}
+
+void LayerCache::setMaxSize(uint32_t maxSize) {
+ mMaxSize = maxSize;
+ while (mSize > mMaxSize) {
+ Layer* oldest = mCache.removeOldest();
+ deleteLayer(oldest);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Callbacks
+///////////////////////////////////////////////////////////////////////////////
+
+void LayerCache::operator()(LayerSize& size, Layer*& layer) {
+ deleteLayer(layer);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
+
+void LayerCache::deleteLayer(Layer* layer) {
+ if (layer) {
+ mSize -= layer->layer.getWidth() * layer->layer.getHeight() * 4;
+
+ glDeleteFramebuffers(1, &layer->fbo);
+ glDeleteTextures(1, &layer->texture);
+ delete layer;
+ }
+}
+
+void LayerCache::clear() {
+ mCache.setOnEntryRemovedListener(this);
+ mCache.clear();
+ mCache.setOnEntryRemovedListener(NULL);
+}
+
+Layer* LayerCache::get(LayerSize& size) {
+ Layer* layer = mCache.remove(size);
+ if (layer) {
+ mSize -= layer->layer.getWidth() * layer->layer.getHeight() * 4;
+ }
+ return layer;
+}
+
+bool LayerCache::put(LayerSize& layerSize, Layer* layer) {
+ const uint32_t size = layerSize.width * layerSize.height * 4;
+ // Don't even try to cache a layer that's bigger than the cache
+ if (size < mMaxSize) {
+ while (mSize + size > mMaxSize) {
+ Layer* oldest = mCache.removeOldest();
+ deleteLayer(oldest);
+ }
+
+ mCache.put(layerSize, layer);
+ mSize += size;
+
+ return true;
+ }
+ return false;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
new file mode 100644
index 0000000..3696848
--- /dev/null
+++ b/libs/hwui/LayerCache.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 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_UI_LAYER_CACHE_H
+#define ANDROID_UI_LAYER_CACHE_H
+
+#include "Layer.h"
+#include "GenerationCache.h"
+
+namespace android {
+namespace uirenderer {
+
+class LayerCache: public OnEntryRemoved<LayerSize, Layer*> {
+public:
+ LayerCache(uint32_t maxByteSize);
+ ~LayerCache();
+
+ /**
+ * Used as a callback when an entry is removed from the cache.
+ * Do not invoke directly.
+ */
+ void operator()(LayerSize& bitmap, Layer*& texture);
+
+ /**
+ * Returns the layer of specified dimensions, NULL if cannot be found.
+ */
+ Layer* get(LayerSize& size);
+ /**
+ * Adds the layer to the cache. The layer will not be added if there is
+ * not enough space available.
+ *
+ * @return True if the layer was added, false otherwise.
+ */
+ bool put(LayerSize& size, Layer* layer);
+ /**
+ * 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:
+ void deleteLayer(Layer* layer);
+
+ GenerationCache<LayerSize, Layer*> mCache;
+
+ uint32_t mSize;
+ uint32_t mMaxSize;
+}; // class LayerCache
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_UI_LAYER_CACHE_H
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 9df1c67..9f2cc24 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -34,19 +34,30 @@
// Defines
///////////////////////////////////////////////////////////////////////////////
+// Debug
+#define DEBUG_LAYERS 0
+
// 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 DEFAULT_TEXTURE_CACHE_SIZE 20
+#define DEFAULT_LAYER_CACHE_SIZE 10
+
// Converts a number of mega-bytes into bytes
#define MB(s) s * 1024 * 1024
-#define DEFAULT_TEXTURE_CACHE_SIZE MB(20)
-#define DEFAULT_LAYER_CACHE_SIZE MB(10)
-
+// Generates simple and textured vertices
#define SV(x, y) { { x, y } }
#define FV(x, y, u, v) { { x, y }, { u, v } }
+// Debug
+#ifdef DEBUG_LAYERS
+ #define LAYER_LOGD(...) LOGD(__VA_ARGS__)
+#else
+ #define LAYER_LOGD(...)
+#endif
+
///////////////////////////////////////////////////////////////////////////////
// Globals
///////////////////////////////////////////////////////////////////////////////
@@ -92,12 +103,24 @@
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-OpenGLRenderer::OpenGLRenderer(): mTextureCache(DEFAULT_TEXTURE_CACHE_SIZE) {
+OpenGLRenderer::OpenGLRenderer():
+ mTextureCache(MB(DEFAULT_TEXTURE_CACHE_SIZE)),
+ mLayerCache(MB(DEFAULT_LAYER_CACHE_SIZE)) {
LOGD("Create OpenGLRenderer");
char property[PROPERTY_VALUE_MAX];
if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) {
+ LOGD(" Setting texture cache size to %sMB", property);
mTextureCache.setMaxSize(MB(atoi(property)));
+ } else {
+ LOGD(" Using default texture cache size of %dMB", DEFAULT_TEXTURE_CACHE_SIZE);
+ }
+
+ if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) {
+ LOGD(" Setting layer cache size to %sMB", property);
+ mLayerCache.setMaxSize(MB(atoi(property)));
+ } else {
+ LOGD(" Using default layer cache size of %dMB", DEFAULT_LAYER_CACHE_SIZE);
}
mDrawColorShader = new DrawColorProgram;
@@ -110,6 +133,7 @@
LOGD("Destroy OpenGLRenderer");
mTextureCache.clear();
+ mLayerCache.clear();
}
///////////////////////////////////////////////////////////////////////////////
@@ -205,6 +229,11 @@
}
void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
+ if (!current->layer) {
+ LOGE("Attempting to compose a layer that does not exist");
+ return;
+ }
+
// Unbind current FBO and restore previous one
// Most of the time, previous->fbo will be 0 to bind the default buffer
glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
@@ -213,17 +242,25 @@
const Rect& clip = previous->getMappedClip();
glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight());
+ Layer* layer = current->layer;
+
// Compute the correct texture coordinates for the FBO texture
// The texture is currently as big as the window but drawn with
// a quad of the appropriate size
- const Rect& layer = current->layer;
+ const Rect& rect = layer->layer;
- drawTextureRect(layer.left, layer.top, layer.right, layer.bottom,
- current->texture, current->alpha, current->mode, true, true);
+ drawTextureRect(rect.left, rect.top, rect.right, rect.bottom,
+ layer->texture, layer->alpha, layer->mode, layer->blend, true);
- // TODO Don't delete these things, but cache them
- glDeleteFramebuffers(1, ¤t->fbo);
- glDeleteTextures(1, ¤t->texture);
+ LayerSize size(rect.getWidth(), rect.getHeight());
+ if (!mLayerCache.put(size, layer)) {
+ LAYER_LOGD("Deleting layer");
+
+ glDeleteFramebuffers(1, &layer->fbo);
+ glDeleteTextures(1, &layer->texture);
+
+ delete layer;
+ }
}
///////////////////////////////////////////////////////////////////////////////
@@ -262,43 +299,62 @@
bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top,
float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) {
- // Generate the FBO and attach the texture
- glGenFramebuffers(1, &snapshot->fbo);
- glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
- // Generate the texture in which the FBO will draw
- glGenTextures(1, &snapshot->texture);
- glBindTexture(GL_TEXTURE_2D, snapshot->texture);
+ LayerSize size(right - left, bottom - top);
+ Layer* layer = mLayerCache.get(size);
- // The FBO will not be scaled, so we can use lower quality filtering
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ LAYER_LOGD("Requesting layer %dx%d", size.width, size.height);
+ LAYER_LOGD("Layer cache size = %d", mLayerCache.getSize());
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ if (!layer) {
+ LAYER_LOGD("Creating new layer");
- // TODO VERY IMPORTANT: Fix TextView to not call saveLayer() all the time
- // TODO Use an FBO cache
+ layer = new Layer;
+ layer->blend = true;
- const GLsizei width = right - left;
- const GLsizei height = bottom - top;
+ // Generate the FBO and attach the texture
+ glGenFramebuffers(1, &layer->fbo);
+ glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
- const GLint format = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) ? GL_RGBA : GL_RGB;
- glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, NULL);
- glBindTexture(GL_TEXTURE_2D, 0);
+ // Generate the texture in which the FBO will draw
+ glGenTextures(1, &layer->texture);
+ glBindTexture(GL_TEXTURE_2D, layer->texture);
- // Bind texture to FBO
- glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
- snapshot->texture, 0);
+ // The FBO will not be scaled, so we can use lower quality filtering
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
- if (status != GL_FRAMEBUFFER_COMPLETE) {
- LOGD("Framebuffer incomplete (GL error code 0x%x)", status);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- glDeleteFramebuffers(1, &snapshot->fbo);
- glDeleteTextures(1, &snapshot->texture);
+ // TODO VERY IMPORTANT: Fix TextView to not call saveLayer() all the time
- return false;
+ const GLsizei width = right - left;
+ const GLsizei height = bottom - top;
+
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ // Bind texture to FBO
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
+ layer->texture, 0);
+
+ GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
+ if (status != GL_FRAMEBUFFER_COMPLETE) {
+ LOGD("Framebuffer incomplete (GL error code 0x%x)", status);
+
+ GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0;
+ glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+
+ glDeleteFramebuffers(1, &layer->fbo);
+ glDeleteTextures(1, &layer->texture);
+ delete layer;
+
+ return false;
+ }
+ } else {
+ LAYER_LOGD("Reusing layer");
+ glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
}
// Clear the FBO
@@ -307,10 +363,14 @@
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
+ // Save the layer in the snapshot
snapshot->flags |= Snapshot::kFlagIsLayer;
- snapshot->mode = mode;
- snapshot->alpha = alpha / 255.0f;
- snapshot->layer.set(left, top, right, bottom);
+ layer->mode = mode;
+ layer->alpha = alpha / 255.0f;
+ layer->layer.set(left, top, right, bottom);
+
+ snapshot->layer = layer;
+ snapshot->fbo = layer->fbo;
// Creates a new snapshot to draw into the FBO
saveSnapshot();
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index bd5f84f..fa9592a 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -32,7 +32,9 @@
#include "Rect.h"
#include "Snapshot.h"
#include "Texture.h"
+#include "Layer.h"
#include "TextureCache.h"
+#include "LayerCache.h"
namespace android {
namespace uirenderer {
@@ -240,6 +242,7 @@
// Used to cache all drawBitmap textures
TextureCache mTextureCache;
+ LayerCache mLayerCache;
}; // class OpenGLRenderer
}; // namespace uirenderer
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index d1809f3..17ca440 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -20,10 +20,9 @@
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
-#include <SkXfermode.h>
-
#include <utils/RefBase.h>
+#include "Layer.h"
#include "Matrix.h"
#include "Rect.h"
@@ -41,8 +40,7 @@
*/
class Snapshot: public LightRefBase<Snapshot> {
public:
- Snapshot() {
- }
+ Snapshot(): layer(NULL), fbo(0) { }
/**
* Copies the specified snapshot. Only the transform and clip rectangle
@@ -56,10 +54,8 @@
clipRect(s->clipRect),
flags(kFlagDirtyTransform),
previous(s),
- layer(0.0f, 0.0f, 0.0f, 0.0f),
- texture(0),
- fbo(0),
- alpha(255) {
+ layer(NULL),
+ fbo(s->fbo) {
}
/**
@@ -126,30 +122,10 @@
sp<Snapshot> previous;
/**
- * Coordinates of the layer corresponding to this snapshot.
* Only set when the flag kFlagIsLayer is set.
*/
- Rect layer;
- /**
- * Name of the texture used to render the layer.
- * Only set when the flag kFlagIsLayer is set.
- */
- GLuint texture;
- /**
- * Name of the FBO used to render the layer.
- * Only set when the flag kFlagIsLayer is set.
- */
+ Layer* layer;
GLuint fbo;
- /**
- * Opacity of the layer.
- * Only set when the flag kFlagIsLayer is set.
- */
- float alpha;
- /**
- * Blending mode of the layer.
- * Only set when the flag kFlagIsLayer is set.
- */
- SkXfermode::Mode mode;
/**
* Contains the previous ortho matrix.
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 93ee138..612f04e 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -28,7 +28,7 @@
///////////////////////////////////////////////////////////////////////////////
TextureCache::TextureCache(uint32_t maxByteSize):
- mCache(GenerationCache<SkBitmap, Texture>::kUnlimitedCapacity),
+ mCache(GenerationCache<SkBitmap*, Texture*>::kUnlimitedCapacity),
mSize(0), mMaxSize(maxByteSize) {
mCache.setOnEntryRemovedListener(this);
}
@@ -60,7 +60,7 @@
// Callbacks
///////////////////////////////////////////////////////////////////////////////
-void TextureCache::operator()(SkBitmap* bitmap, Texture* texture) {
+void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) {
if (bitmap) {
const uint32_t size = bitmap->rowBytes() * bitmap->height();
mSize -= size;
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 250efc5..bed1191 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -39,7 +39,7 @@
* Used as a callback when an entry is removed from the cache.
* Do not invoke directly.
*/
- void operator()(SkBitmap* bitmap, Texture* texture);
+ void operator()(SkBitmap*& bitmap, Texture*& texture);
/**
* Returns the texture associated with the specified bitmap. If the texture
@@ -78,7 +78,7 @@
*/
void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false);
- GenerationCache<SkBitmap, Texture> mCache;
+ GenerationCache<SkBitmap*, Texture*> mCache;
uint32_t mSize;
uint32_t mMaxSize;