diff options
author | Romain Guy <romainguy@google.com> | 2010-10-05 18:14:38 -0700 |
---|---|---|
committer | Romain Guy <romainguy@google.com> | 2010-10-05 18:14:38 -0700 |
commit | eb99356a0548684a501766e6a524529ab93304c8 (patch) | |
tree | 7beb40beea2f12944057cd025ca8b212d3a01855 /libs | |
parent | 7adaf3d1aa18c7e521f7154e545fe52d329763c3 (diff) | |
download | frameworks_base-eb99356a0548684a501766e6a524529ab93304c8.zip frameworks_base-eb99356a0548684a501766e6a524529ab93304c8.tar.gz frameworks_base-eb99356a0548684a501766e6a524529ab93304c8.tar.bz2 |
Optimize saveLayer() when the clip flag is set.
This speeds up applications, especially Launcher.
Diffstat (limited to 'libs')
-rw-r--r-- | libs/hwui/FboCache.cpp | 23 | ||||
-rw-r--r-- | libs/hwui/FboCache.h | 2 | ||||
-rw-r--r-- | libs/hwui/GenerationCache.h | 6 | ||||
-rw-r--r-- | libs/hwui/Layer.h | 22 | ||||
-rw-r--r-- | libs/hwui/LayerCache.cpp | 16 | ||||
-rw-r--r-- | libs/hwui/LayerCache.h | 2 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 160 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.h | 3 | ||||
-rw-r--r-- | libs/hwui/Properties.h | 2 | ||||
-rw-r--r-- | libs/hwui/Snapshot.h | 48 |
10 files changed, 220 insertions, 64 deletions
diff --git a/libs/hwui/FboCache.cpp b/libs/hwui/FboCache.cpp index 77fbda2..2ef71c2 100644 --- a/libs/hwui/FboCache.cpp +++ b/libs/hwui/FboCache.cpp @@ -16,6 +16,8 @@ #define LOG_TAG "OpenGLRenderer" +#include <stdlib.h> + #include "FboCache.h" #include "Properties.h" @@ -57,14 +59,31 @@ uint32_t FboCache::getMaxSize() { /////////////////////////////////////////////////////////////////////////////// void FboCache::clear() { - + for (size_t i = 0; i < mCache.size(); i++) { + const GLuint fbo = mCache.itemAt(i); + glDeleteFramebuffers(1, &fbo); + } + mCache.clear(); } GLuint FboCache::get() { - return 0; + GLuint fbo; + if (mCache.size() > 0) { + fbo = mCache.itemAt(mCache.size() - 1); + mCache.removeAt(mCache.size() - 1); + } else { + glGenFramebuffers(1, &fbo); + } + return fbo; } bool FboCache::put(GLuint fbo) { + if (mCache.size() < mMaxSize) { + mCache.add(fbo); + return true; + } + + glDeleteFramebuffers(1, &fbo); return false; } diff --git a/libs/hwui/FboCache.h b/libs/hwui/FboCache.h index 66f66ea..ec4afe9 100644 --- a/libs/hwui/FboCache.h +++ b/libs/hwui/FboCache.h @@ -21,8 +21,6 @@ #include <utils/SortedVector.h> -#include "GenerationCache.h" - namespace android { namespace uirenderer { diff --git a/libs/hwui/GenerationCache.h b/libs/hwui/GenerationCache.h index 070e33f..35c6bea 100644 --- a/libs/hwui/GenerationCache.h +++ b/libs/hwui/GenerationCache.h @@ -65,6 +65,7 @@ public: void put(K key, V value); V remove(K key); V removeOldest(); + V getValueAt(uint32_t index) const; uint32_t size() const; @@ -128,6 +129,11 @@ K GenerationCache<K, V>::getKeyAt(uint32_t index) const { } template<typename K, typename V> +V GenerationCache<K, V>::getValueAt(uint32_t index) const { + return mCache.valueAt(index); +} + +template<typename K, typename V> V GenerationCache<K, V>::get(K key) { ssize_t index = mCache.indexOfKey(key); if (index >= 0) { diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index a0cc5d6..6024765 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -32,21 +32,14 @@ namespace uirenderer { * Dimensions of a layer. */ struct LayerSize { - LayerSize(): width(0), height(0), id(0) { } - LayerSize(const uint32_t width, const uint32_t height): width(width), height(height), id(0) { } - LayerSize(const LayerSize& size): width(size.width), height(size.height), id(size.id) { } + 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; - // Incremental id used by the layer cache to store multiple - // LayerSize with the same dimensions - uint32_t id; - bool operator<(const LayerSize& rhs) const { - if (id != 0 && rhs.id != 0 && id != rhs.id) { - return id < rhs.id; - } if (width == rhs.width) { return height < rhs.height; } @@ -54,12 +47,12 @@ struct LayerSize { } bool operator==(const LayerSize& rhs) const { - return id == rhs.id && width == rhs.width && height == rhs.height; + return width == rhs.width && height == rhs.height; } }; // struct LayerSize /** - * A layer has dimensions and is backed by an OpenGL texture. + * A layer has dimensions and is backed by an OpenGL texture or FBO. */ struct Layer { /** @@ -71,6 +64,11 @@ struct Layer { */ GLuint texture; /** + * Name of the FBO used to render the layer. If the name is 0 + * this layer is not backed by an FBO, but a simple texture. + */ + GLuint fbo; + /** * Opacity of the layer. */ int alpha; diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index 8c70cf9..2183718 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -32,7 +32,7 @@ namespace uirenderer { LayerCache::LayerCache(): mCache(GenerationCache<LayerSize, Layer*>::kUnlimitedCapacity), - mIdGenerator(1), mSize(0), mMaxSize(MB(DEFAULT_LAYER_CACHE_SIZE)) { + mSize(0), mMaxSize(MB(DEFAULT_LAYER_CACHE_SIZE)) { char property[PROPERTY_VALUE_MAX]; if (property_get(PROPERTY_LAYER_CACHE_SIZE, property, NULL) > 0) { LOGD(" Setting layer cache size to %sMB", property); @@ -44,7 +44,7 @@ LayerCache::LayerCache(): LayerCache::LayerCache(uint32_t maxByteSize): mCache(GenerationCache<LayerSize, Layer*>::kUnlimitedCapacity), - mIdGenerator(1), mSize(0), mMaxSize(maxByteSize) { + mSize(0), mMaxSize(maxByteSize) { } LayerCache::~LayerCache() { @@ -110,6 +110,7 @@ Layer* LayerCache::get(LayerSize& size) { layer = new Layer; layer->blend = true; layer->empty = true; + layer->fbo = 0; glGenTextures(1, &layer->texture); glBindTexture(GL_TEXTURE_2D, layer->texture); @@ -121,6 +122,14 @@ Layer* LayerCache::get(LayerSize& size) { 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 DEBUG_LAYERS + uint32_t size = mCache.size(); + for (uint32_t i = 0; i < size; i++) { + LayerSize ls = mCache.getKeyAt(i); + LAYER_LOGD(" Layer size %dx%d", ls.width, ls.height); + } +#endif } return layer; @@ -133,9 +142,10 @@ bool LayerCache::put(LayerSize& layerSize, Layer* layer) { while (mSize + size > mMaxSize) { Layer* oldest = mCache.removeOldest(); deleteLayer(oldest); + LAYER_LOGD(" Deleting layer %.2fx%.2f", oldest->layer.getWidth(), + oldest->layer.getHeight()); } - layerSize.id = mIdGenerator++; mCache.put(layerSize, layer); mSize += size; diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h index c0c7542..cbb7ae2 100644 --- a/libs/hwui/LayerCache.h +++ b/libs/hwui/LayerCache.h @@ -64,6 +64,7 @@ public: * @param size The dimensions of the desired layer */ Layer* get(LayerSize& size); + /** * Adds the layer to the cache. The layer will not be added if there is * not enough space available. @@ -96,7 +97,6 @@ private: void deleteLayer(Layer* layer); GenerationCache<LayerSize, Layer*> mCache; - uint32_t mIdGenerator; uint32_t mSize; uint32_t mMaxSize; diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index e3790f5..ee5fe22 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -145,6 +145,9 @@ void OpenGLRenderer::setViewport(int width, int height) { mWidth = width; mHeight = height; + + mFirstSnapshot->height = height; + mFirstSnapshot->viewport.set(0, 0, width, height); } void OpenGLRenderer::prepare() { @@ -185,7 +188,7 @@ void OpenGLRenderer::acquireContext() { } void OpenGLRenderer::releaseContext() { - glViewport(0, 0, mWidth, mHeight); + glViewport(0, 0, mSnapshot->viewport.getWidth(), mSnapshot->viewport.getHeight()); glEnable(GL_SCISSOR_TEST); setScissorFromClip(); @@ -237,10 +240,17 @@ int OpenGLRenderer::saveSnapshot(int flags) { bool OpenGLRenderer::restoreSnapshot() { bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet; bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer; + bool restoreOrtho = mSnapshot->flags & Snapshot::kFlagDirtyOrtho; sp<Snapshot> current = mSnapshot; sp<Snapshot> previous = mSnapshot->previous; + if (restoreOrtho) { + Rect& r = previous->viewport; + glViewport(r.left, r.top, r.right, r.bottom); + mOrthoMatrix.load(current->orthoMatrix); + } + mSaveCount--; mSnapshot = previous; @@ -261,7 +271,8 @@ bool OpenGLRenderer::restoreSnapshot() { int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom, const SkPaint* p, int flags) { - int count = saveSnapshot(flags); + const GLuint previousFbo = mSnapshot->fbo; + const int count = saveSnapshot(flags); int alpha = 255; SkXfermode::Mode mode; @@ -281,7 +292,7 @@ int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom, mode = SkXfermode::kSrcOver_Mode; } - createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags); + createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags, previousFbo); return count; } @@ -346,17 +357,21 @@ int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bot * something actually gets drawn are the layers regions cleared. */ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, - float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) { - LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top); + float right, float bottom, int alpha, SkXfermode::Mode mode, + int flags, GLuint previousFbo) { + LAYER_LOGD("Requesting layer %.2fx%.2f", right - left, bottom - top); LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize()); + const bool fboLayer = flags & SkCanvas::kClipToLayer_SaveFlag; + // Window coordinates of the layer Rect bounds(left, top, right, bottom); - mSnapshot->transform->mapRect(bounds); - - // Layers only make sense if they are in the framebuffer's bounds - bounds.intersect(*mSnapshot->clipRect); - bounds.snapToPixelBoundaries(); + if (!fboLayer) { + mSnapshot->transform->mapRect(bounds); + // Layers only make sense if they are in the framebuffer's bounds + bounds.intersect(*mSnapshot->clipRect); + bounds.snapToPixelBoundaries(); + } if (bounds.isEmpty() || bounds.getWidth() > mMaxTextureSize || bounds.getHeight() > mMaxTextureSize) { @@ -379,29 +394,77 @@ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, snapshot->flags |= Snapshot::kFlagIsLayer; snapshot->layer = layer; - // Copy the framebuffer into the layer - glBindTexture(GL_TEXTURE_2D, layer->texture); + if (fboLayer) { + layer->fbo = mCaches.fboCache.get(); - // TODO: Workaround for b/3054204 - glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom, - bounds.getWidth(), bounds.getHeight(), 0); + snapshot->flags |= Snapshot::kFlagIsFboLayer; + snapshot->fbo = layer->fbo; + snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); + snapshot->resetClip(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); + snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); + snapshot->height = bounds.getHeight(); + snapshot->flags |= Snapshot::kFlagDirtyOrtho; + snapshot->orthoMatrix.load(mOrthoMatrix); - // TODO: Waiting for b/3054204 to be fixed -// if (layer->empty) { -// glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom, -// bounds.getWidth(), bounds.getHeight(), 0); -// layer->empty = false; -// } else { -// glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left, mHeight - bounds.bottom, -// bounds.getWidth(), bounds.getHeight()); -// } - - if (flags & SkCanvas::kClipToLayer_SaveFlag && mSnapshot->clipTransformed(bounds)) { setScissorFromClip(); - } - // Enqueue the buffer coordinates to clear the corresponding region later - mLayers.push(new Rect(bounds)); + // Bind texture to FBO + glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); + glBindTexture(GL_TEXTURE_2D, layer->texture); + + // Initialize the texture if needed + if (layer->empty) { + layer->empty = false; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width, size.height, 0, + GL_RGBA, GL_UNSIGNED_BYTE, NULL); + } + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, + layer->texture, 0); + + GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + if (status != GL_FRAMEBUFFER_COMPLETE) { + LOGE("Framebuffer incomplete (GL error code 0x%x)", status); + + glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + glDeleteTextures(1, &layer->texture); + mCaches.fboCache.put(layer->fbo); + + delete layer; + + return false; + } + + // Clear the FBO + glDisable(GL_SCISSOR_TEST); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + glEnable(GL_SCISSOR_TEST); + + // Change the ortho projection + glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); + mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); + } else { + // Copy the framebuffer into the layer + glBindTexture(GL_TEXTURE_2D, layer->texture); + + // TODO: Workaround for b/3054204 + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom, + bounds.getWidth(), bounds.getHeight(), 0); + + // TODO: Waiting for b/3054204 to be fixed + // if (layer->empty) { + // glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom, + // bounds.getWidth(), bounds.getHeight(), 0); + // layer->empty = false; + // } else { + // glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left, mHeight - bounds.bottom, + // bounds.getWidth(), bounds.getHeight()); + // } + + // Enqueue the buffer coordinates to clear the corresponding region later + mLayers.push(new Rect(bounds)); + } return true; } @@ -415,14 +478,21 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { return; } + const bool fboLayer = current->flags & SkCanvas::kClipToLayer_SaveFlag; + + if (fboLayer) { + // Unbind current FBO and restore previous one + glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo); + } + // Restore the clip from the previous snapshot const Rect& clip = *previous->clipRect; - glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight()); + glScissor(clip.left, previous->height - clip.bottom, clip.getWidth(), clip.getHeight()); Layer* layer = current->layer; const Rect& rect = layer->layer; - if (layer->alpha < 255) { + if (!fboLayer && layer->alpha < 255) { drawColorRect(rect.left, rect.top, rect.right, rect.bottom, layer->alpha << 24, SkXfermode::kDstIn_Mode, true); } @@ -430,20 +500,32 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { // Layers are already drawn with a top-left origin, don't flip the texture resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f); - drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture, - 1.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0], - &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, true, true); + if (fboLayer) { + drawTextureRect(rect.left, rect.top, rect.right, rect.bottom, + layer->texture, layer->alpha / 255.0f, layer->mode, layer->blend); + } else { + drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture, + 1.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0], + &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, true, true); + } resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); + if (fboLayer) { + // Detach the texture from the FBO + glBindFramebuffer(GL_FRAMEBUFFER, current->fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); + glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo); + + // Put the FBO name back in the cache, if it doesn't fit, it will be destroyed + mCaches.fboCache.put(current->fbo); + } + LayerSize size(rect.getWidth(), rect.getHeight()); - // Failing to add the layer to the cache should happen only if the - // layer is too large + // Failing to add the layer to the cache should happen only if the layer is too large if (!mCaches.layerCache.put(size, layer)) { LAYER_LOGD("Deleting layer"); - glDeleteTextures(1, &layer->texture); - delete layer; } } @@ -503,7 +585,7 @@ void OpenGLRenderer::concatMatrix(SkMatrix* matrix) { void OpenGLRenderer::setScissorFromClip() { const Rect& clip = *mSnapshot->clipRect; - glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight()); + glScissor(clip.left, mSnapshot->height - clip.bottom, clip.getWidth(), clip.getHeight()); } const Rect& OpenGLRenderer::getClipBounds() { diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 1974cf0..e3d4653 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -167,11 +167,12 @@ private: * @param alpha The translucency of the layer * @param mode The blending mode of the layer * @param flags The layer save flags + * @param previousFbo The name of the current framebuffer * * @return True if the layer was successfully created, false otherwise */ bool createLayer(sp<Snapshot> snapshot, float left, float top, float right, float bottom, - int alpha, SkXfermode::Mode mode, int flags); + int alpha, SkXfermode::Mode mode, int flags, GLuint previousFbo); /** * Clears all the regions corresponding to the current list of layers. diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index d573805..3012824 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -45,7 +45,7 @@ #define MB(s) s * 1024 * 1024 #define DEFAULT_TEXTURE_CACHE_SIZE 18.0f -#define DEFAULT_LAYER_CACHE_SIZE 4.0f +#define DEFAULT_LAYER_CACHE_SIZE 8.0f #define DEFAULT_PATH_CACHE_SIZE 5.0f #define DEFAULT_PATCH_CACHE_SIZE 100 #define DEFAULT_GRADIENT_CACHE_SIZE 0.5f diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index c736a1c..3d74b4c 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -43,7 +43,7 @@ namespace uirenderer { */ class Snapshot: public LightRefBase<Snapshot> { public: - Snapshot(): flags(0), previous(NULL), layer(NULL) { + Snapshot(): flags(0), previous(NULL), layer(NULL), fbo(0) { transform = &mTransformRoot; clipRect = &mClipRectRoot; } @@ -53,7 +53,8 @@ public: * the previous snapshot. */ Snapshot(const sp<Snapshot>& s, int saveFlags): - flags(0), previous(s), layer(NULL) { + flags(0), previous(s), layer(NULL), + fbo(s->fbo), viewport(s->viewport), height(s->height) { if (saveFlags & SkCanvas::kMatrix_SaveFlag) { mTransformRoot.load(*s->transform); transform = &mTransformRoot; @@ -91,9 +92,19 @@ public: */ kFlagIsLayer = 0x2, /** + * Indicates that this snapshot is a special type of layer + * backed by an FBO. This flag only makes sense when the + * flag kFlagIsLayer is also set. + */ + kFlagIsFboLayer = 0x4, + /** * Indicates that the local clip should be recomputed. */ - kFlagDirtyLocalClip = 0x4, + kFlagDirtyLocalClip = 0x8, + /** + * Indicates that this snapshot has changed the ortho matrix. + */ + kFlagDirtyOrtho = 0x10, }; /** @@ -169,6 +180,17 @@ public: return mLocalClip; } + void resetTransform(float x, float y, float z) { + transform = &mTransformRoot; + transform->loadTranslate(x, y, z); + } + + void resetClip(float left, float top, float right, float bottom) { + clipRect = &mClipRectRoot; + clipRect->set(left, top, right, bottom); + flags |= Snapshot::kFlagClipSet | Snapshot::kFlagDirtyLocalClip; + } + /** * Dirty flags. */ @@ -185,6 +207,26 @@ public: Layer* layer; /** + * Only set when the flag kFlagIsFboLayer is set. + */ + GLuint fbo; + + /** + * Current viewport. + */ + Rect viewport; + + /** + * Height of the framebuffer the snapshot is rendering into. + */ + int height; + + /** + * Contains the previous ortho matrix. + */ + mat4 orthoMatrix; + + /** * Local transformation. Holds the current translation, scale and * rotation values. */ |