diff options
author | 2010-07-06 11:39:32 -0700 | |
---|---|---|
committer | 2010-07-06 12:00:33 -0700 | |
commit | dda570201ac851dd85af3861f7e575721d3345da (patch) | |
tree | 795e46c7cae42b22814af1ef2ac70c083debd746 | |
parent | 216108c492fef05261c70f1018d94cef0c3b23fd (diff) |
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
-rw-r--r-- | libs/hwui/Android.mk | 1 | ||||
-rw-r--r-- | libs/hwui/GenerationCache.h | 60 | ||||
-rw-r--r-- | libs/hwui/Layer.h | 90 | ||||
-rw-r--r-- | libs/hwui/LayerCache.cpp | 114 | ||||
-rw-r--r-- | libs/hwui/LayerCache.h | 78 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 140 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.h | 3 | ||||
-rw-r--r-- | libs/hwui/Snapshot.h | 34 | ||||
-rw-r--r-- | libs/hwui/TextureCache.cpp | 4 | ||||
-rw-r--r-- | libs/hwui/TextureCache.h | 4 |
10 files changed, 427 insertions, 101 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 95ebda1938ab..394aecae718d 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -2,6 +2,7 @@ LOCAL_PATH:= $(call my-dir) 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 56693dac736e..d92a0bb91867 100644 --- a/libs/hwui/GenerationCache.h +++ b/libs/hwui/GenerationCache.h @@ -27,7 +27,7 @@ template<typename EntryKey, typename EntryValue> 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 @@ public: 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 @@ private: 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 @@ uint32_t GenerationCache<K, V>::size() const { } 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 @@ void GenerationCache<K, V>::clear() { } 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 @@ V* GenerationCache<K, V>::get(K* key) { } 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 @@ void GenerationCache<K, V>::addToCache(sp<Entry<K*, V*> > entry, K* key, V* valu } 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 @@ void GenerationCache<K, V>::attachToCache(sp<Entry<K*, V*> > entry) { } 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 000000000000..70d64ca4b914 --- /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 000000000000..0d3214dcbfa3 --- /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 000000000000..369684854689 --- /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 9df1c6765c50..9f2cc2403505 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -34,19 +34,30 @@ namespace uirenderer { // 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 @@ static const Blender gBlends[] = { // 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 @@ OpenGLRenderer::~OpenGLRenderer() { LOGD("Destroy OpenGLRenderer"); mTextureCache.clear(); + mLayerCache.clear(); } /////////////////////////////////////////////////////////////////////////////// @@ -205,6 +229,11 @@ bool OpenGLRenderer::restoreSnapshot() { } 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 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { 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(rect.left, rect.top, rect.right, rect.bottom, + layer->texture, layer->alpha, layer->mode, layer->blend, true); - drawTextureRect(layer.left, layer.top, layer.right, layer.bottom, - current->texture, current->alpha, current->mode, true, true); + LayerSize size(rect.getWidth(), rect.getHeight()); + if (!mLayerCache.put(size, layer)) { + LAYER_LOGD("Deleting layer"); - // TODO Don't delete these things, but cache them - glDeleteFramebuffers(1, ¤t->fbo); - glDeleteTextures(1, ¤t->texture); + glDeleteFramebuffers(1, &layer->fbo); + glDeleteTextures(1, &layer->texture); + + delete layer; + } } /////////////////////////////////////////////////////////////////////////////// @@ -262,43 +299,62 @@ int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bot 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); + + // 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); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // TODO VERY IMPORTANT: Fix TextView to not call saveLayer() all the time - // Bind texture to FBO - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - snapshot->texture, 0); + const GLsizei width = right - left; + const GLsizei height = bottom - top; - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - LOGD("Framebuffer incomplete (GL error code 0x%x)", status); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); - glDeleteFramebuffers(1, &snapshot->fbo); - glDeleteTextures(1, &snapshot->texture); + // Bind texture to FBO + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + layer->texture, 0); - return false; + 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 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, 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 bd5f84fbf83b..fa9592ab03ce 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 @@ private: // 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 d1809f35e1e0..17ca440da753 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 @@ namespace uirenderer { */ 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 @@ public: 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 @@ public: 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 93ee138a373c..612f04e5570e 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -28,7 +28,7 @@ namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// 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 @@ void TextureCache::setMaxSize(uint32_t maxSize) { // 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 250efc56f60f..bed11915c581 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -39,7 +39,7 @@ public: * 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 @@ private: */ void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false); - GenerationCache<SkBitmap, Texture> mCache; + GenerationCache<SkBitmap*, Texture*> mCache; uint32_t mSize; uint32_t mMaxSize; |