From 9ace8f5e79e76893fe4ca9e4d10f6c4056330485 Mon Sep 17 00:00:00 2001 From: Romain Guy Date: Thu, 7 Jul 2011 20:50:11 -0700 Subject: Use NEAREST filtering for layers whenever possible. Change-Id: Id5bee1bd4a322cf93e8000b08e18f1e1b058648e --- libs/hwui/Layer.h | 231 ++++++++++++++++++++++++++++++++++--------- libs/hwui/LayerCache.cpp | 50 +++++----- libs/hwui/LayerCache.h | 2 +- libs/hwui/LayerRenderer.cpp | 124 +++++++++++------------ libs/hwui/OpenGLRenderer.cpp | 156 ++++++++++++++++++----------- libs/hwui/Texture.h | 20 ++++ 6 files changed, 381 insertions(+), 202 deletions(-) (limited to 'libs') diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 0310bc3..3c2d80d 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -27,6 +27,7 @@ #include "Rect.h" #include "SkiaColorFilter.h" +#include "Texture.h" #include "Vertex.h" namespace android { @@ -40,14 +41,18 @@ namespace uirenderer { * A layer has dimensions and is backed by an OpenGL texture or FBO. */ struct Layer { - Layer(const uint32_t layerWidth, const uint32_t layerHeight): - width(layerWidth), height(layerHeight) { + Layer(const uint32_t layerWidth, const uint32_t layerHeight) { mesh = NULL; meshIndices = NULL; meshElementCount = 0; - isCacheable = true; - isTextureLayer = false; + cacheable = true; + textureLayer = false; renderTarget = GL_TEXTURE_2D; + texture.width = layerWidth; + texture.height = layerHeight; + colorFilter = NULL; + firstFilter = true; + firstWrap = true; } ~Layer() { @@ -64,12 +69,152 @@ struct Layer { regionRect.set(bounds.leftTop().x, bounds.leftTop().y, bounds.rightBottom().x, bounds.rightBottom().y); - const float texX = 1.0f / float(width); - const float texY = 1.0f / float(height); + const float texX = 1.0f / float(texture.width); + const float texY = 1.0f / float(texture.height); const float height = layer.getHeight(); texCoords.set( regionRect.left * texX, (height - regionRect.top) * texY, regionRect.right * texX, (height - regionRect.bottom) * texY); + + regionRect.translate(layer.left, layer.top); + } + + inline uint32_t getWidth() { + return texture.width; + } + + inline uint32_t getHeight() { + return texture.height; + } + + void setSize(uint32_t width, uint32_t height) { + texture.width = width; + texture.height = height; + } + + inline void setBlend(bool blend) { + texture.blend = blend; + } + + inline bool isBlend() { + return texture.blend; + } + + inline void setAlpha(int alpha) { + this->alpha = alpha; + } + + inline void setAlpha(int alpha, SkXfermode::Mode mode) { + this->alpha = alpha; + this->mode = mode; + } + + inline int getAlpha() { + return alpha; + } + + inline SkXfermode::Mode getMode() { + return mode; + } + + inline void setEmpty(bool empty) { + this->empty = empty; + } + + inline bool isEmpty() { + return empty; + } + + inline void setFbo(GLuint fbo) { + this->fbo = fbo; + } + + inline GLuint getFbo() { + return fbo; + } + + inline GLuint* getTexturePointer() { + return &texture.id; + } + + inline GLuint getTexture() { + return texture.id; + } + + inline GLenum getRenderTarget() { + return renderTarget; + } + + inline void setRenderTarget(GLenum renderTarget) { + this->renderTarget = renderTarget; + } + + void setWrap(GLenum wrapS, GLenum wrapT, bool bindTexture = false, bool force = false) { + if (firstWrap || force || wrapS != texture.wrapS || wrapT != texture.wrapT) { + firstWrap = true; + texture.setWrap(wrapS, wrapT); + if (bindTexture) { + glBindTexture(renderTarget, texture.id); + } + glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS); + glTexParameteri(renderTarget, GL_TEXTURE_WRAP_T, wrapT); + } + } + + void setFilter(GLenum min, GLenum mag, bool bindTexture = false, bool force = false) { + if (firstFilter || force || min != texture.minFilter || mag != texture.magFilter) { + firstFilter = false; + texture.setFilter(min, mag); + if (bindTexture) { + glBindTexture(renderTarget, texture.id); + } + glTexParameteri(renderTarget, GL_TEXTURE_MIN_FILTER, min); + glTexParameteri(renderTarget, GL_TEXTURE_MAG_FILTER, mag); + } + } + + inline bool isCacheable() { + return cacheable; + } + + inline void setCacheable(bool cacheable) { + this->cacheable = cacheable; + } + + inline bool isTextureLayer() { + return textureLayer; + } + + inline void setTextureLayer(bool textureLayer) { + this->textureLayer = textureLayer; + } + + inline SkiaColorFilter* getColorFilter() { + return colorFilter; + } + + inline void setColorFilter(SkiaColorFilter* filter) { + colorFilter = filter; + } + + inline void bindTexture() { + glBindTexture(renderTarget, texture.id); + } + + inline void generateTexture() { + glGenTextures(1, &texture.id); + } + + inline void deleteTexture() { + if (texture.id) glDeleteTextures(1, &texture.id); + } + + inline void allocateTexture(GLenum format, GLenum storage) { + glTexImage2D(renderTarget, 0, format, getWidth(), getHeight(), 0, format, storage, NULL); + } + + inline mat4& getTexTransform() { + return texTransform; } /** @@ -82,23 +227,29 @@ struct Layer { Rect texCoords; /** - * 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. + * Dirty region indicating what parts of the layer + * have been drawn. */ - GLuint fbo; - + Region region; /** - * Opacity of the layer. + * If the region is a rectangle, coordinates of the + * region are stored here. */ - int alpha; + Rect regionRect; + /** - * Blending mode of the layer. + * If the layer can be rendered as a mesh, this is non-null. */ - SkXfermode::Mode mode; + TextureVertex* mesh; + uint16_t* meshIndices; + GLsizei meshElementCount; + +private: /** - * Indicates whether this layer should be blended. + * 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. */ - bool blend; + GLuint fbo; /** * Indicates whether this layer has been used already. @@ -106,28 +257,25 @@ struct Layer { bool empty; /** - * Name of the texture used to render the layer. - */ - GLuint texture; - /** - * Width of the layer texture. + * The texture backing this layer. */ - uint32_t width; + Texture texture; + /** - * Height of the layer texture. + * If set to true (by default), the layer can be reused. */ - uint32_t height; + bool cacheable; /** - * Dirty region indicating what parts of the layer - * have been drawn. + * When set to true, this layer must be treated as a texture + * layer. */ - Region region; + bool textureLayer; + /** - * If the region is a rectangle, coordinates of the - * region are stored here. + * Indicates the render target. */ - Rect regionRect; + GLenum renderTarget; /** * Color filter used to draw this layer. Optional. @@ -135,32 +283,21 @@ struct Layer { SkiaColorFilter* colorFilter; /** - * If the layer can be rendered as a mesh, this is non-null. - */ - TextureVertex* mesh; - uint16_t* meshIndices; - GLsizei meshElementCount; - - /** - * If set to true (by default), the layer can be reused. + * Opacity of the layer. */ - bool isCacheable; - + int alpha; /** - * When set to true, this layer must be treated as a texture - * layer. + * Blending mode of the layer. */ - bool isTextureLayer; + SkXfermode::Mode mode; /** * Optional texture coordinates transform. */ mat4 texTransform; - /** - * Indicates the render target. - */ - GLenum renderTarget; + bool firstFilter; + bool firstWrap; }; // struct Layer }; // namespace uirenderer diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index b2d795f..1a15e87 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -68,8 +68,8 @@ void LayerCache::setMaxSize(uint32_t maxSize) { void LayerCache::deleteLayer(Layer* layer) { if (layer) { - mSize -= layer->width * layer->height * 4; - glDeleteTextures(1, &layer->texture); + mSize -= layer->getWidth() * layer->getHeight() * 4; + layer->deleteTexture(); delete layer; } } @@ -93,29 +93,23 @@ Layer* LayerCache::get(const uint32_t width, const uint32_t height) { mCache.removeAt(index); layer = entry.mLayer; - mSize -= layer->width * layer->height * 4; + mSize -= layer->getWidth() * layer->getHeight() * 4; - LAYER_LOGD("Reusing layer %dx%d", layer->width, layer->height); + LAYER_LOGD("Reusing layer %dx%d", layer->getWidth(), layer->getHeight()); } else { LAYER_LOGD("Creating new layer %dx%d", entry.mWidth, entry.mHeight); layer = new Layer(entry.mWidth, entry.mHeight); - layer->blend = true; - layer->empty = true; - layer->fbo = 0; - layer->colorFilter = NULL; - - glGenTextures(1, &layer->texture); - glBindTexture(GL_TEXTURE_2D, layer->texture); - + layer->setBlend(true); + layer->setEmpty(true); + layer->setFbo(0); + + layer->generateTexture(); + layer->bindTexture(); + layer->setFilter(GL_NEAREST, GL_NEAREST); + layer->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - 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 size_t size = mCache.size(); for (size_t i = 0; i < size; i++) { @@ -133,30 +127,30 @@ bool LayerCache::resize(Layer* layer, const uint32_t width, const uint32_t heigh // size already in the cache, and reuse it instead of creating a new one LayerEntry entry(width, height); - if (entry.mWidth <= layer->width && entry.mHeight <= layer->height) { + if (entry.mWidth <= layer->getWidth() && entry.mHeight <= layer->getHeight()) { return true; } - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, layer->texture); + uint32_t oldWidth = layer->getWidth(); + uint32_t oldHeight = layer->getHeight(); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, entry.mWidth, entry.mHeight, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glActiveTexture(GL_TEXTURE0); + layer->bindTexture(); + layer->setSize(entry.mWidth, entry.mHeight); + layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE); if (glGetError() != GL_NO_ERROR) { + layer->setSize(oldWidth, oldHeight); return false; } - layer->width = entry.mWidth; - layer->height = entry.mHeight; - return true; } bool LayerCache::put(Layer* layer) { - if (!layer->isCacheable) return false; + if (!layer->isCacheable()) return false; - const uint32_t size = layer->width * layer->height * 4; + const uint32_t size = layer->getWidth() * layer->getHeight() * 4; // Don't even try to cache a layer that's bigger than the cache if (size < mMaxSize) { // TODO: Use an LRU diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h index d2d5f39..81b8bf3 100644 --- a/libs/hwui/LayerCache.h +++ b/libs/hwui/LayerCache.h @@ -119,7 +119,7 @@ private: } LayerEntry(Layer* layer): - mLayer(layer), mWidth(layer->width), mHeight(layer->height) { + mLayer(layer), mWidth(layer->getWidth()), mHeight(layer->getHeight()) { } bool operator<(const LayerEntry& rhs) const { diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index e034a86..44f2a40 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -32,9 +32,9 @@ namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// void LayerRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) { - LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->fbo); + LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo()); - glBindFramebuffer(GL_FRAMEBUFFER, mLayer->fbo); + glBindFramebuffer(GL_FRAMEBUFFER, mLayer->getFbo()); const float width = mLayer->layer.getWidth(); const float height = mLayer->layer.getHeight(); @@ -62,14 +62,14 @@ void LayerRenderer::finish() { generateMesh(); - LAYER_RENDERER_LOGD("Finished rendering into layer, fbo = %d", mLayer->fbo); + LAYER_RENDERER_LOGD("Finished rendering into layer, fbo = %d", mLayer->getFbo()); // No need to unbind our FBO, this will be taken care of by the caller // who will invoke OpenGLRenderer::resume() } GLint LayerRenderer::getTargetFbo() { - return mLayer->fbo; + return mLayer->getFbo(); } /////////////////////////////////////////////////////////////////////////////// @@ -128,8 +128,8 @@ void LayerRenderer::generateMesh() { } mLayer->meshElementCount = elementCount; - const float texX = 1.0f / float(mLayer->width); - const float texY = 1.0f / float(mLayer->height); + const float texX = 1.0f / float(mLayer->getWidth()); + const float texY = 1.0f / float(mLayer->getHeight()); const float height = mLayer->layer.getHeight(); TextureVertex* mesh = mLayer->mesh; @@ -182,40 +182,41 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque return NULL; } - layer->fbo = fbo; + layer->setFbo(fbo); layer->layer.set(0.0f, 0.0f, width, height); - layer->texCoords.set(0.0f, height / float(layer->height), - width / float(layer->width), 0.0f); - layer->alpha = 255; - layer->mode = SkXfermode::kSrcOver_Mode; - layer->blend = !isOpaque; - layer->colorFilter = NULL; + layer->texCoords.set(0.0f, height / float(layer->getHeight()), + width / float(layer->getWidth()), 0.0f); + layer->setAlpha(255, SkXfermode::kSrcOver_Mode); + layer->setBlend(!isOpaque); + layer->setColorFilter(NULL); layer->region.clear(); GLuint previousFbo; glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); - glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); - glBindTexture(GL_TEXTURE_2D, layer->texture); + glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo()); + layer->bindTexture(); // Initialize the texture if needed - if (layer->empty) { - layer->empty = false; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + if (layer->isEmpty()) { + layer->setEmpty(false); + layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE); if (glGetError() != GL_NO_ERROR) { LOGD("Could not allocate texture"); + glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - glDeleteTextures(1, &layer->texture); Caches::getInstance().fboCache.put(fbo); + + layer->deleteTexture(); delete layer; + return NULL; } } glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - layer->texture, 0); + layer->getTexture(), 0); glDisable(GL_SCISSOR_TEST); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); @@ -229,14 +230,14 @@ Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) { if (layer) { - LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->fbo, width, height); + LAYER_RENDERER_LOGD("Resizing layer fbo = %d to %dx%d", layer->getFbo(), width, height); if (Caches::getInstance().layerCache.resize(layer, width, height)) { layer->layer.set(0.0f, 0.0f, width, height); - layer->texCoords.set(0.0f, height / float(layer->height), - width / float(layer->width), 0.0f); + layer->texCoords.set(0.0f, height / float(layer->getHeight()), + width / float(layer->getWidth()), 0.0f); } else { - if (layer->texture) glDeleteTextures(1, &layer->texture); + layer->deleteTexture(); delete layer; return false; } @@ -245,37 +246,23 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) { return true; } -static void setTextureParameters(Layer* layer) { - glBindTexture(layer->renderTarget, layer->texture); - - glTexParameteri(layer->renderTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(layer->renderTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glTexParameteri(layer->renderTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(layer->renderTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -} - Layer* LayerRenderer::createTextureLayer(bool isOpaque) { LAYER_RENDERER_LOGD("Creating new texture layer"); Layer* layer = new Layer(0, 0); - layer->isCacheable = false; - layer->isTextureLayer = true; - layer->blend = !isOpaque; - layer->empty = true; - layer->fbo = 0; - layer->colorFilter = NULL; - layer->fbo = 0; + layer->setCacheable(false); + layer->setTextureLayer(true); + layer->setBlend(!isOpaque); + layer->setEmpty(true); + layer->setFbo(0); + layer->setAlpha(255, SkXfermode::kSrcOver_Mode); layer->layer.set(0.0f, 0.0f, 0.0f, 0.0f); layer->texCoords.set(0.0f, 1.0f, 0.0f, 1.0f); - layer->alpha = 255; - layer->mode = SkXfermode::kSrcOver_Mode; - layer->colorFilter = NULL; layer->region.clear(); - layer->renderTarget = GL_NONE; // see ::updateTextureLayer() + layer->setRenderTarget(GL_NONE); // see ::updateTextureLayer() glActiveTexture(GL_TEXTURE0); - glGenTextures(1, &layer->texture); + layer->generateTexture(); return layer; } @@ -283,31 +270,32 @@ Layer* LayerRenderer::createTextureLayer(bool isOpaque) { void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, bool isOpaque, GLenum renderTarget, float* transform) { if (layer) { - layer->blend = !isOpaque; - layer->width = width; - layer->height = height; + layer->setBlend(!isOpaque); + layer->setSize(width, height); layer->layer.set(0.0f, 0.0f, width, height); layer->region.set(width, height); layer->regionRect.set(0.0f, 0.0f, width, height); - layer->texTransform.load(transform); + layer->getTexTransform().load(transform); - if (renderTarget != layer->renderTarget) { - layer->renderTarget = renderTarget; - setTextureParameters(layer); + if (renderTarget != layer->getRenderTarget()) { + layer->setRenderTarget(renderTarget); + layer->bindTexture(); + layer->setFilter(GL_NEAREST, GL_NEAREST, false, true); + layer->setWrap(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, false, true); } } } void LayerRenderer::destroyLayer(Layer* layer) { if (layer) { - LAYER_RENDERER_LOGD("Destroying layer, fbo = %d", layer->fbo); + LAYER_RENDERER_LOGD("Destroying layer, fbo = %d", layer->getFbo()); - if (layer->fbo) { - Caches::getInstance().fboCache.put(layer->fbo); + if (layer->getFbo()) { + Caches::getInstance().fboCache.put(layer->getFbo()); } if (!Caches::getInstance().layerCache.put(layer)) { - if (layer->texture) glDeleteTextures(1, &layer->texture); + layer->deleteTexture(); delete layer; } else { layer->region.clear(); @@ -317,7 +305,7 @@ void LayerRenderer::destroyLayer(Layer* layer) { void LayerRenderer::destroyLayerDeferred(Layer* layer) { if (layer) { - LAYER_RENDERER_LOGD("Deferring layer destruction, fbo = %d", layer->fbo); + LAYER_RENDERER_LOGD("Deferring layer destruction, fbo = %d", layer->getFbo()); Caches::getInstance().deleteLayerDeferred(layer); } @@ -325,7 +313,7 @@ void LayerRenderer::destroyLayerDeferred(Layer* layer) { bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { Caches& caches = Caches::getInstance(); - if (layer && layer->isTextureLayer && bitmap->width() <= caches.maxTextureSize && + if (layer && layer->isTextureLayer() && bitmap->width() <= caches.maxTextureSize && bitmap->height() <= caches.maxTextureSize) { GLuint fbo = caches.fboCache.get(); @@ -365,12 +353,11 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { break; } - float alpha = layer->alpha; - SkXfermode::Mode mode = layer->mode; + float alpha = layer->getAlpha(); + SkXfermode::Mode mode = layer->getMode(); - layer->mode = SkXfermode::kSrc_Mode; - layer->alpha = 255; - layer->fbo = fbo; + layer->setAlpha(255, SkXfermode::kSrc_Mode); + layer->setFbo(fbo); glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo); @@ -399,7 +386,7 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { LayerRenderer renderer(layer); renderer.setViewport(bitmap->width(), bitmap->height()); renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f, - bitmap->width(), bitmap->height(), !layer->blend); + bitmap->width(), bitmap->height(), !layer->isBlend()); if ((error = glGetError()) != GL_NO_ERROR) goto error; { @@ -424,9 +411,8 @@ error: #endif glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - layer->mode = mode; - layer->alpha = alpha; - layer->fbo = 0; + layer->setAlpha(alpha, mode); + layer->setFbo(0); glDeleteTextures(1, &texture); caches.fboCache.put(fbo); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 1c06a0b..cb5a82b 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -449,12 +449,11 @@ bool OpenGLRenderer::createLayer(sp snapshot, float left, float top, return false; } - layer->mode = mode; - layer->alpha = alpha; + layer->setAlpha(alpha, mode); layer->layer.set(bounds); - layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->height), - bounds.getWidth() / float(layer->width), 0.0f); - layer->colorFilter = mColorFilter; + layer->texCoords.set(0.0f, bounds.getHeight() / float(layer->getHeight()), + bounds.getWidth() / float(layer->getWidth()), 0.0f); + layer->setColorFilter(mColorFilter); // Save the layer in the snapshot snapshot->flags |= Snapshot::kFlagIsLayer; @@ -464,12 +463,13 @@ bool OpenGLRenderer::createLayer(sp snapshot, float left, float top, return createFboLayer(layer, bounds, snapshot, previousFbo); } else { // Copy the framebuffer into the layer - glBindTexture(GL_TEXTURE_2D, layer->texture); + layer->bindTexture(); if (!bounds.isEmpty()) { - if (layer->empty) { - glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, - snapshot->height - bounds.bottom, layer->width, layer->height, 0); - layer->empty = false; + if (layer->isEmpty()) { + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, + bounds.left, snapshot->height - bounds.bottom, + layer->getWidth(), layer->getHeight(), 0); + layer->setEmpty(false); } else { glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left, snapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight()); @@ -485,7 +485,7 @@ bool OpenGLRenderer::createLayer(sp snapshot, float left, float top, bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp snapshot, GLuint previousFbo) { - layer->fbo = mCaches.fboCache.get(); + layer->setFbo(mCaches.fboCache.get()); #if RENDER_LAYERS_AS_REGIONS snapshot->region = &snapshot->layer->region; @@ -507,7 +507,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp sna clip.translate(-bounds.left, -bounds.top); snapshot->flags |= Snapshot::kFlagIsFboLayer; - snapshot->fbo = layer->fbo; + snapshot->fbo = layer->getFbo(); snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); snapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); @@ -516,18 +516,17 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp sna snapshot->orthoMatrix.load(mOrthoMatrix); // Bind texture to FBO - glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); - glBindTexture(GL_TEXTURE_2D, layer->texture); + glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo()); + layer->bindTexture(); // Initialize the texture if needed - if (layer->empty) { - layer->empty = false; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, layer->width, layer->height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, NULL); + if (layer->isEmpty()) { + layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE); + layer->setEmpty(false); } glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - layer->texture, 0); + layer->getTexture(), 0); #if DEBUG_LAYERS_AS_REGIONS GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); @@ -535,8 +534,8 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, sp sna LOGE("Framebuffer incomplete (GL error code 0x%x)", status); glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - glDeleteTextures(1, &layer->texture); - mCaches.fboCache.put(layer->fbo); + layer->deleteTexture(); + mCaches.fboCache.put(layer->getFbo()); delete layer; @@ -578,11 +577,11 @@ void OpenGLRenderer::composeLayer(sp current, sp previous) { Layer* layer = current->layer; const Rect& rect = layer->layer; - if (!fboLayer && layer->alpha < 255) { + if (!fboLayer && layer->getAlpha() < 255) { drawColorRect(rect.left, rect.top, rect.right, rect.bottom, - layer->alpha << 24, SkXfermode::kDstIn_Mode, true); + layer->getAlpha() << 24, SkXfermode::kDstIn_Mode, true); // Required below, composeLayerRect() will divide by 255 - layer->alpha = 255; + layer->setAlpha(255); } mCaches.unbindMeshBuffer(); @@ -593,18 +592,16 @@ void OpenGLRenderer::composeLayer(sp current, sp previous) { // drawing only the dirty region if (fboLayer) { dirtyLayer(rect.left, rect.top, rect.right, rect.bottom, *previous->transform); - if (layer->colorFilter) { - setupColorFilter(layer->colorFilter); + if (layer->getColorFilter()) { + setupColorFilter(layer->getColorFilter()); } composeLayerRegion(layer, rect); - if (layer->colorFilter) { + if (layer->getColorFilter()) { resetColorFilter(); } - } else { - if (!rect.isEmpty()) { - dirtyLayer(rect.left, rect.top, rect.right, rect.bottom); - composeLayerRect(layer, rect, true); - } + } else if (!rect.isEmpty()) { + dirtyLayer(rect.left, rect.top, rect.right, rect.bottom); + composeLayerRect(layer, rect, true); } if (fboLayer) { @@ -622,16 +619,16 @@ void OpenGLRenderer::composeLayer(sp current, sp previous) { // Failing to add the layer to the cache should happen only if the layer is too large if (!mCaches.layerCache.put(layer)) { LAYER_LOGD("Deleting layer"); - glDeleteTextures(1, &layer->texture); + layer->deleteTexture(); delete layer; } } void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { - float alpha = layer->alpha / 255.0f; + float alpha = layer->getAlpha() / 255.0f; setupDraw(); - if (layer->renderTarget == GL_TEXTURE_2D) { + if (layer->getRenderTarget() == GL_TEXTURE_2D) { setupDrawWithTexture(); } else { setupDrawWithExternalTexture(); @@ -639,17 +636,26 @@ void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { setupDrawTextureTransform(); setupDrawColor(alpha, alpha, alpha, alpha); setupDrawColorFilter(); - setupDrawBlending(layer->blend || layer->alpha < 255, layer->mode); + setupDrawBlending(layer->isBlend() || alpha < 1.0f, layer->getMode()); setupDrawProgram(); - setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); - if (layer->renderTarget == GL_TEXTURE_2D) { - setupDrawTexture(layer->texture); + if (layer->getRenderTarget() == GL_TEXTURE_2D) { + setupDrawTexture(layer->getTexture()); } else { - setupDrawExternalTexture(layer->texture); + setupDrawExternalTexture(layer->getTexture()); } - setupDrawTextureTransformUniforms(layer->texTransform); + if (mSnapshot->transform->isPureTranslate()) { + const float x = (int) floorf(rect.left + mSnapshot->transform->getTranslateX() + 0.5f); + const float y = (int) floorf(rect.top + mSnapshot->transform->getTranslateY() + 0.5f); + + layer->setFilter(GL_NEAREST, GL_NEAREST); + setupDrawModelView(x, y, x + rect.getWidth(), y + rect.getHeight(), true); + } else { + layer->setFilter(GL_LINEAR, GL_LINEAR); + setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom); + } + setupDrawTextureTransformUniforms(layer->getTexTransform()); setupDrawMesh(&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]); glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); @@ -658,14 +664,32 @@ void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { } void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) { - if (!layer->isTextureLayer) { + if (!layer->isTextureLayer()) { const Rect& texCoords = layer->texCoords; resetDrawTextureTexCoords(texCoords.left, texCoords.top, texCoords.right, texCoords.bottom); - drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture, - layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0], - &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, swap, swap); + float x = rect.left; + float y = rect.top; + bool simpleTransform = mSnapshot->transform->isPureTranslate(); + + if (simpleTransform) { + // When we're swapping, the layer is already in screen coordinates + if (!swap) { + x = (int) floorf(rect.left + mSnapshot->transform->getTranslateX() + 0.5f); + y = (int) floorf(rect.top + mSnapshot->transform->getTranslateY() + 0.5f); + } + + layer->setFilter(GL_NEAREST, GL_NEAREST, true); + } else { + layer->setFilter(GL_LINEAR, GL_LINEAR, true); + } + + drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(), + layer->getTexture(), layer->getAlpha() / 255.0f, + layer->getMode(), layer->isBlend(), + &mMeshVertices[0].position[0], &mMeshVertices[0].texture[0], + GL_TRIANGLE_STRIP, gMeshCount, swap, swap || simpleTransform); resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); } else { @@ -690,9 +714,9 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { size_t count; const android::Rect* rects = layer->region.getArray(&count); - const float alpha = layer->alpha / 255.0f; - const float texX = 1.0f / float(layer->width); - const float texY = 1.0f / float(layer->height); + const float alpha = layer->getAlpha() / 255.0f; + const float texX = 1.0f / float(layer->getWidth()); + const float texY = 1.0f / float(layer->getHeight()); const float height = rect.getHeight(); TextureVertex* mesh = mCaches.getRegionMesh(); @@ -702,13 +726,22 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { setupDrawWithTexture(); setupDrawColor(alpha, alpha, alpha, alpha); setupDrawColorFilter(); - setupDrawBlending(layer->blend || layer->alpha < 255, layer->mode, false); + setupDrawBlending(layer->isBlend() || alpha < 1.0f, layer->getMode(), false); setupDrawProgram(); setupDrawDirtyRegionsDisabled(); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); - setupDrawTexture(layer->texture); - setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom); + setupDrawTexture(layer->getTexture()); + if (mSnapshot->transform->isPureTranslate()) { + const float x = (int) floorf(rect.left + mSnapshot->transform->getTranslateX() + 0.5f); + const float y = (int) floorf(rect.top + mSnapshot->transform->getTranslateY() + 0.5f); + + layer->setFilter(GL_NEAREST, GL_NEAREST); + setupDrawModelViewTranslate(x, y, x + rect.getWidth(), y + rect.getHeight(), true); + } else { + layer->setFilter(GL_LINEAR, GL_LINEAR); + setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom); + } setupDrawMesh(&mesh[0].position[0], &mesh[0].texture[0]); for (size_t i = 0; i < count; i++) { @@ -2154,8 +2187,7 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) { SkXfermode::Mode mode; getAlphaAndMode(paint, &alpha, &mode); - layer->alpha = alpha; - layer->mode = mode; + layer->setAlpha(alpha, mode); #if RENDER_LAYERS_AS_REGIONS if (!layer->region.isEmpty()) { @@ -2169,13 +2201,23 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) { setupDrawWithTexture(); setupDrawColor(a, a, a, a); setupDrawColorFilter(); - setupDrawBlending(layer->blend || layer->alpha < 255, layer->mode, false); + setupDrawBlending(layer->isBlend() || a < 1.0f, layer->getMode(), false); setupDrawProgram(); - setupDrawModelViewTranslate(x, y, - x + layer->layer.getWidth(), y + layer->layer.getHeight()); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); - setupDrawTexture(layer->texture); + setupDrawTexture(layer->getTexture()); + if (mSnapshot->transform->isPureTranslate()) { + x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f); + y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f); + + layer->setFilter(GL_NEAREST, GL_NEAREST); + setupDrawModelViewTranslate(x, y, + x + layer->layer.getWidth(), y + layer->layer.getHeight(), true); + } else { + layer->setFilter(GL_LINEAR, GL_LINEAR); + setupDrawModelViewTranslate(x, y, + x + layer->layer.getWidth(), y + layer->layer.getHeight()); + } setupDrawMesh(&layer->mesh[0].position[0], &layer->mesh[0].texture[0]); glDrawElements(GL_TRIANGLES, layer->meshElementCount, diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index 4922bb3..c6ae326 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -29,8 +29,22 @@ struct Texture { Texture() { cleanup = false; bitmapSize = 0; + wrapS = GL_CLAMP_TO_EDGE; wrapT = GL_CLAMP_TO_EDGE; + + minFilter = GL_NEAREST; + magFilter = GL_NEAREST; + } + + void setWrap(GLenum wrapS, GLenum wrapT) { + this->wrapS = wrapS; + this->wrapT = wrapT; + } + + void setFilter(GLenum min, GLenum mag) { + minFilter = min; + magFilter = mag; } /** @@ -67,6 +81,12 @@ struct Texture { */ GLenum wrapS; GLenum wrapT; + + /** + * Last filters set on this texture. Defaults to GL_NEAREST. + */ + GLenum minFilter; + GLenum magFilter; }; // struct Texture class AutoTexture { -- cgit v1.1