diff options
Diffstat (limited to 'libs/hwui')
| -rw-r--r-- | libs/hwui/Android.mk | 20 | ||||
| -rw-r--r-- | libs/hwui/AssetAtlas.cpp | 106 | ||||
| -rw-r--r-- | libs/hwui/AssetAtlas.h | 169 | ||||
| -rw-r--r-- | libs/hwui/Caches.cpp | 70 | ||||
| -rw-r--r-- | libs/hwui/Caches.h | 18 | ||||
| -rw-r--r-- | libs/hwui/Debug.h | 2 | ||||
| -rw-r--r-- | libs/hwui/DisplayListOp.h | 58 | ||||
| -rw-r--r-- | libs/hwui/DisplayListRenderer.cpp | 17 | ||||
| -rw-r--r-- | libs/hwui/DisplayListRenderer.h | 3 | ||||
| -rw-r--r-- | libs/hwui/Extensions.h | 6 | ||||
| -rw-r--r-- | libs/hwui/FontRenderer.cpp | 68 | ||||
| -rw-r--r-- | libs/hwui/FontRenderer.h | 5 | ||||
| -rw-r--r-- | libs/hwui/Image.cpp | 62 | ||||
| -rw-r--r-- | libs/hwui/Image.h | 67 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 198 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.h | 49 | ||||
| -rw-r--r-- | libs/hwui/Patch.cpp | 181 | ||||
| -rw-r--r-- | libs/hwui/Patch.h | 60 | ||||
| -rw-r--r-- | libs/hwui/PatchCache.cpp | 149 | ||||
| -rw-r--r-- | libs/hwui/PatchCache.h | 70 | ||||
| -rw-r--r-- | libs/hwui/Program.cpp | 35 | ||||
| -rw-r--r-- | libs/hwui/Program.h | 6 | ||||
| -rw-r--r-- | libs/hwui/ProgramCache.cpp | 15 | ||||
| -rw-r--r-- | libs/hwui/Properties.h | 9 | ||||
| -rw-r--r-- | libs/hwui/Texture.h | 9 | ||||
| -rw-r--r-- | libs/hwui/UvMapper.h | 133 | ||||
| -rw-r--r-- | libs/hwui/thread/TaskManager.h | 2 |
27 files changed, 1150 insertions, 437 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index a630ea1..771ac45 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -10,6 +10,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) thread/TaskManager.cpp \ font/CacheTexture.cpp \ font/Font.cpp \ + AssetAtlas.cpp \ FontRenderer.cpp \ GammaFontRenderer.cpp \ Caches.cpp \ @@ -21,6 +22,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) Extensions.cpp \ FboCache.cpp \ GradientCache.cpp \ + Image.cpp \ Layer.cpp \ LayerCache.cpp \ LayerRenderer.cpp \ @@ -52,17 +54,23 @@ ifeq ($(USE_OPENGL_RENDERER),true) external/skia/include/images \ external/skia/src/core \ external/skia/src/ports \ - external/skia/include/utils \ - $(intermediates) \ - frameworks/rs/cpp \ - frameworks/rs + external/skia/include/utils - LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DGL_GLEXT_PROTOTYPES + LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES LOCAL_MODULE_CLASS := SHARED_LIBRARIES - LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libGLESv2 libskia libui libRS libRScpp + LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libEGL libGLESv2 libskia libui LOCAL_MODULE := libhwui LOCAL_MODULE_TAGS := optional + ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT)) + LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT + LOCAL_SHARED_LIBRARIES += libRS libRScpp + LOCAL_C_INCLUDES += \ + $(intermediates) \ + frameworks/rs/cpp \ + frameworks/rs + endif + ifndef HWUI_COMPILE_SYMBOLS LOCAL_CFLAGS += -fvisibility=hidden endif diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp new file mode 100644 index 0000000..4d2fc01 --- /dev/null +++ b/libs/hwui/AssetAtlas.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "AssetAtlas.h" + +#include <GLES2/gl2ext.h> + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Lifecycle +/////////////////////////////////////////////////////////////////////////////// + +void AssetAtlas::init(sp<GraphicBuffer> buffer, int* map, int count) { + if (mImage) { + return; + } + + mImage = new Image(buffer); + mTexture = mImage->getTexture(); + + if (mTexture) { + mWidth = buffer->getWidth(); + mHeight = buffer->getHeight(); + + createEntries(map, count); + } else { + ALOGW("Could not create atlas image"); + + delete mImage; + mImage = NULL; + } +} + +void AssetAtlas::terminate() { + if (mImage) { + delete mImage; + mImage = NULL; + + for (size_t i = 0; i < mEntries.size(); i++) { + delete mEntries.valueAt(i); + } + mEntries.clear(); + + mWidth = mHeight = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Entries +/////////////////////////////////////////////////////////////////////////////// + +AssetAtlas::Entry* AssetAtlas::getEntry(SkBitmap* const bitmap) const { + ssize_t index = mEntries.indexOfKey(bitmap); + return index >= 0 ? mEntries.valueAt(index) : NULL; +} + +Texture* AssetAtlas::getEntryTexture(SkBitmap* const bitmap) const { + ssize_t index = mEntries.indexOfKey(bitmap); + return index >= 0 ? &mEntries.valueAt(index)->texture : NULL; +} + +/** + * TODO: This method does not take the rotation flag into account + */ +void AssetAtlas::createEntries(int* map, int count) { + for (int i = 0; i < count; ) { + SkBitmap* bitmap = (SkBitmap*) map[i++]; + int x = map[i++]; + int y = map[i++]; + bool rotated = map[i++] > 0; + + // Bitmaps should never be null, we're just extra paranoid + if (!bitmap) continue; + + const UvMapper mapper( + x / (float) mWidth, (x + bitmap->width()) / (float) mWidth, + y / (float) mHeight, (y + bitmap->height()) / (float) mHeight); + + Entry* entry = new Entry(bitmap, x, y, rotated, mapper, *this); + entry->texture.id = mTexture; + entry->texture.blend = !bitmap->isOpaque(); + entry->texture.width = bitmap->width(); + entry->texture.height = bitmap->height(); + entry->texture.uvMapper = &entry->uvMapper; + + mEntries.add(entry->bitmap, entry); + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h new file mode 100644 index 0000000..0bbd2a7 --- /dev/null +++ b/libs/hwui/AssetAtlas.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HWUI_ASSET_ATLAS_H +#define ANDROID_HWUI_ASSET_ATLAS_H + +#include <GLES2/gl2.h> + +#include <ui/GraphicBuffer.h> + +#include <utils/KeyedVector.h> + +#include <cutils/compiler.h> + +#include <SkBitmap.h> + +#include "Image.h" +#include "Texture.h" +#include "UvMapper.h" + +namespace android { +namespace uirenderer { + +/** + * An asset atlas holds a collection of framework bitmaps in a single OpenGL + * texture. Each bitmap is associated with a location, defined in pixels, + * inside the atlas. The atlas is generated by the framework and bound as + * an external texture using the EGLImageKHR extension. + */ +class AssetAtlas { +public: + /** + * Entry representing the position and rotation of a + * bitmap inside the atlas. + */ + struct Entry { + /** + * The bitmap that generated this atlas entry. + */ + SkBitmap* bitmap; + + /** + * Location of the bitmap inside the atlas, in pixels. + */ + int x; + int y; + + /** + * If set, the bitmap is rotated 90 degrees (clockwise) + * inside the atlas. + */ + bool rotated; + + /** + * Maps texture coordinates in the [0..1] range into the + * correct range to sample this entry from the atlas. + */ + const UvMapper uvMapper; + + /** + * Atlas this entry belongs to. + */ + const AssetAtlas& atlas; + + /* + * A "virtual texture" object that represents the texture + * this entry belongs to. This texture should never be + * modified. + */ + Texture texture; + + private: + Entry(SkBitmap* bitmap, int x, int y, bool rotated, + const UvMapper& mapper, const AssetAtlas& atlas): + bitmap(bitmap), x(x), y(y), rotated(rotated), uvMapper(mapper), atlas(atlas) { } + + friend class AssetAtlas; + }; + + AssetAtlas(): mWidth(0), mHeight(0), mTexture(0), mImage(NULL) { } + ~AssetAtlas() { terminate(); } + + /** + * Initializes the atlas with the specified buffer and + * map. The buffer is a gralloc'd texture that will be + * used as an EGLImage. The map is a list of SkBitmap* + * and their (x, y) positions as well as their rotation + * flags. + * + * This method returns immediately if the atlas is already + * initialized. To re-initialize the atlas, you must + * first call terminate(). + */ + ANDROID_API void init(sp<GraphicBuffer> buffer, int* map, int count); + + /** + * Destroys the atlas texture. This object can be + * re-initialized after calling this method. + * + * After calling this method, the width, height + * and texture are set to 0. + */ + ANDROID_API void terminate(); + + /** + * Returns the width of this atlas in pixels. + * Can return 0 if the atlas is not initialized. + */ + uint32_t getWidth() const { + return mWidth; + } + + /** + * Returns the height of this atlas in pixels. + * Can return 0 if the atlas is not initialized. + */ + uint32_t getHeight() const { + return mHeight; + } + + /** + * Returns the OpenGL name of the texture backing this atlas. + * Can return 0 if the atlas is not initialized. + */ + GLuint getTexture() const { + return mTexture; + } + + /** + * Returns the entry in the atlas associated with the specified + * bitmap. If the bitmap is not in the atlas, return NULL. + */ + Entry* getEntry(SkBitmap* const bitmap) const; + + /** + * Returns the texture for the atlas entry associated with the + * specified bitmap. If the bitmap is not in the atlas, return NULL. + */ + Texture* getEntryTexture(SkBitmap* const bitmap) const; + +private: + void createEntries(int* map, int count); + + uint32_t mWidth; + uint32_t mHeight; + + GLuint mTexture; + Image* mImage; + + KeyedVector<SkBitmap*, Entry*> mEntries; +}; // class AssetAtlas + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_ASSET_ATLAS_H diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index a381a68..f08b5ca 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -58,8 +58,8 @@ Caches::Caches(): Singleton<Caches>(), mExtensions(Extensions::getInstance()), m ALOGD("Enabling debug mode %d", mDebugLevel); } -void Caches::init() { - if (mInitialized) return; +bool Caches::init() { + if (mInitialized) return false; glGenBuffers(1, &meshBuffer); glBindBuffer(GL_ARRAY_BUFFER, meshBuffer); @@ -82,6 +82,7 @@ void Caches::init() { mTextureUnit = 0; mRegionMesh = NULL; + mMeshIndices = 0; blend = false; lastSrcMode = GL_ZERO; @@ -94,7 +95,11 @@ void Caches::init() { debugOverdraw = false; debugStencilClip = kStencilHide; + patchCache.init(*this); + mInitialized = true; + + return true; } void Caches::initFont() { @@ -147,7 +152,7 @@ bool Caches::initProperties() { if (property_get(PROPERTY_DEBUG_OVERDRAW, property, NULL) > 0) { INIT_LOGD(" Overdraw debug enabled: %s", property); - debugOverdraw = !strcmp(property, "true"); + debugOverdraw = !strcmp(property, "show"); } else { debugOverdraw = false; } @@ -191,8 +196,9 @@ void Caches::terminate() { glDeleteBuffers(1, &meshBuffer); mCurrentBuffer = 0; - glDeleteBuffers(1, &mRegionMeshIndices); + glDeleteBuffers(1, &mMeshIndices); delete[] mRegionMesh; + mMeshIndices = 0; mRegionMesh = NULL; fboCache.clear(); @@ -200,6 +206,10 @@ void Caches::terminate() { programCache.clear(); currentProgram = NULL; + assetAtlas.terminate(); + + patchCache.clear(); + mInitialized = false; } @@ -227,6 +237,8 @@ void Caches::dumpMemoryUsage(String8 &log) { pathCache.getSize(), pathCache.getMaxSize()); log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(), dropShadowCache.getMaxSize()); + log.appendFormat(" PatchCache %8d / %8d\n", + patchCache.getSize(), patchCache.getMaxSize()); for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) { const uint32_t size = fontRenderer->getFontRendererSize(i); log.appendFormat(" FontRenderer %d %8d / %8d\n", i, size, size); @@ -234,8 +246,6 @@ void Caches::dumpMemoryUsage(String8 &log) { log.appendFormat("Other:\n"); log.appendFormat(" FboCache %8d / %8d\n", fboCache.getSize(), fboCache.getMaxSize()); - log.appendFormat(" PatchCache %8d / %8d\n", - patchCache.getSize(), patchCache.getMaxSize()); uint32_t total = 0; total += textureCache.getSize(); @@ -244,6 +254,7 @@ void Caches::dumpMemoryUsage(String8 &log) { total += gradientCache.getSize(); total += pathCache.getSize(); total += dropShadowCache.getSize(); + total += patchCache.getSize(); for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) { total += fontRenderer->getFontRendererSize(i); } @@ -357,6 +368,32 @@ bool Caches::bindIndicesBuffer(const GLuint buffer) { return false; } +bool Caches::bindIndicesBuffer() { + if (!mMeshIndices) { + uint16_t* regionIndices = new uint16_t[REGION_MESH_QUAD_COUNT * 6]; + for (int i = 0; i < REGION_MESH_QUAD_COUNT; i++) { + uint16_t quad = i * 4; + int index = i * 6; + regionIndices[index ] = quad; // top-left + regionIndices[index + 1] = quad + 1; // top-right + regionIndices[index + 2] = quad + 2; // bottom-left + regionIndices[index + 3] = quad + 2; // bottom-left + regionIndices[index + 4] = quad + 1; // top-right + regionIndices[index + 5] = quad + 3; // bottom-right + } + + glGenBuffers(1, &mMeshIndices); + bool force = bindIndicesBuffer(mMeshIndices); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, REGION_MESH_QUAD_COUNT * 6 * sizeof(uint16_t), + regionIndices, GL_STATIC_DRAW); + + delete[] regionIndices; + return force; + } + + return bindIndicesBuffer(mMeshIndices); +} + bool Caches::unbindIndicesBuffer() { if (mCurrentIndicesBuffer) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); @@ -546,27 +583,6 @@ TextureVertex* Caches::getRegionMesh() { // Create the mesh, 2 triangles and 4 vertices per rectangle in the region if (!mRegionMesh) { mRegionMesh = new TextureVertex[REGION_MESH_QUAD_COUNT * 4]; - - uint16_t* regionIndices = new uint16_t[REGION_MESH_QUAD_COUNT * 6]; - for (int i = 0; i < REGION_MESH_QUAD_COUNT; i++) { - uint16_t quad = i * 4; - int index = i * 6; - regionIndices[index ] = quad; // top-left - regionIndices[index + 1] = quad + 1; // top-right - regionIndices[index + 2] = quad + 2; // bottom-left - regionIndices[index + 3] = quad + 2; // bottom-left - regionIndices[index + 4] = quad + 1; // top-right - regionIndices[index + 5] = quad + 3; // bottom-right - } - - glGenBuffers(1, &mRegionMeshIndices); - bindIndicesBuffer(mRegionMeshIndices); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, REGION_MESH_QUAD_COUNT * 6 * sizeof(uint16_t), - regionIndices, GL_STATIC_DRAW); - - delete[] regionIndices; - } else { - bindIndicesBuffer(mRegionMeshIndices); } return mRegionMesh; diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 91b938b..18aeeab 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -21,13 +21,18 @@ #define LOG_TAG "OpenGLRenderer" #endif +#include <GLES3/gl3.h> + +#include <utils/KeyedVector.h> #include <utils/Singleton.h> +#include <utils/Vector.h> #include <cutils/compiler.h> #include "thread/TaskProcessor.h" #include "thread/TaskManager.h" +#include "AssetAtlas.h" #include "FontRenderer.h" #include "GammaFontRenderer.h" #include "TextureCache.h" @@ -113,7 +118,7 @@ public: /** * Initialize caches. */ - void init(); + bool init(); /** * Initialize global system properties. @@ -172,6 +177,11 @@ public: */ bool unbindMeshBuffer(); + /** + * Binds a global indices buffer that can draw up to + * REGION_MESH_QUAD_COUNT quads. + */ + bool bindIndicesBuffer(); bool bindIndicesBuffer(const GLuint buffer); bool unbindIndicesBuffer(); @@ -290,6 +300,8 @@ public: Dither dither; Stencil stencil; + AssetAtlas assetAtlas; + // Debug methods PFNGLINSERTEVENTMARKEREXTPROC eventMark; PFNGLPUSHGROUPMARKEREXTPROC startMark; @@ -336,7 +348,9 @@ private: // Used to render layers TextureVertex* mRegionMesh; - GLuint mRegionMeshIndices; + + // Global index buffer + GLuint mMeshIndices; mutable Mutex mGarbageLock; Vector<Layer*> mLayerGarbage; diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h index 790c4f4..786f12a 100644 --- a/libs/hwui/Debug.h +++ b/libs/hwui/Debug.h @@ -53,8 +53,6 @@ // Turn on to display debug info about 9patch objects #define DEBUG_PATCHES 0 -// Turn on to "explode" 9patch objects -#define DEBUG_EXPLODE_PATCHES 0 // Turn on to display vertex and tex coords data about 9patch objects // This flag requires DEBUG_PATCHES to be turned on #define DEBUG_PATCHES_VERTICES 0 diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index a0290e3..990372e 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -26,8 +26,10 @@ #include <private/hwui/DrawGlInfo.h> #include "OpenGLRenderer.h" +#include "AssetAtlas.h" #include "DeferredDisplayList.h" #include "DisplayListRenderer.h" +#include "UvMapper.h" #include "utils/LinearAllocator.h" #define CRASH() do { \ @@ -721,7 +723,6 @@ private: int mSetBits; }; - /////////////////////////////////////////////////////////////////////////////// // DRAW OPERATIONS - these are operations that can draw to the canvas's device /////////////////////////////////////////////////////////////////////////////// @@ -729,9 +730,16 @@ private: class DrawBitmapOp : public DrawBoundedOp { public: DrawBitmapOp(SkBitmap* bitmap, float left, float top, SkPaint* paint) - : DrawBoundedOp(left, top, left + bitmap->width(), top + bitmap->height(), - paint), - mBitmap(bitmap) {} + : DrawBoundedOp(left, top, left + bitmap->width(), top + bitmap->height(), paint), + mBitmap(bitmap), mAtlasEntry(NULL) { + } + + DrawBitmapOp(SkBitmap* bitmap, float left, float top, SkPaint* paint, + const AssetAtlas::Entry* entry) + : DrawBoundedOp(left, top, left + bitmap->width(), top + bitmap->height(), paint), + mBitmap(bitmap), mAtlasEntry(entry) { + if (entry) mUvMapper = entry->uvMapper; + } virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { return renderer.drawBitmap(mBitmap, mLocalBounds.left, mLocalBounds.top, @@ -749,14 +757,14 @@ public: TextureVertex vertices[6 * ops.size()]; TextureVertex* vertex = &vertices[0]; - // TODO: manually handle rect clip for bitmaps by adjusting texCoords per op, and allowing - // them to be merged in getBatchId() - const Rect texCoords(0, 0, 1, 1); - - const float width = mBitmap->width(); - const float height = mBitmap->height(); + // TODO: manually handle rect clip for bitmaps by adjusting texCoords per op, + // and allowing them to be merged in getBatchId() for (unsigned int i = 0; i < ops.size(); i++) { const Rect& opBounds = ops[i]->state.mBounds; + + Rect texCoords(0, 0, 1, 1); + ((DrawBitmapOp*) ops[i])->mUvMapper.map(texCoords); + SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, top); SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top); SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom); @@ -777,7 +785,7 @@ public: virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) { *batchId = DeferredDisplayList::kOpBatch_Bitmap; - *mergeId = (mergeid_t)mBitmap; + *mergeId = mAtlasEntry ? (mergeid_t) &mAtlasEntry->atlas : (mergeid_t) mBitmap; // don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in // MergingDrawBatch::canMergeWith @@ -787,6 +795,8 @@ public: const SkBitmap* bitmap() { return mBitmap; } protected: SkBitmap* mBitmap; + const AssetAtlas::Entry* mAtlasEntry; + UvMapper mUvMapper; }; class DrawBitmapMatrixOp : public DrawBoundedOp { @@ -904,20 +914,16 @@ private: class DrawPatchOp : public DrawBoundedOp { public: - DrawPatchOp(SkBitmap* bitmap, const int32_t* xDivs, - const int32_t* yDivs, const uint32_t* colors, uint32_t width, uint32_t height, - int8_t numColors, float left, float top, float right, float bottom, - int alpha, SkXfermode::Mode mode) + DrawPatchOp(SkBitmap* bitmap, Res_png_9patch* patch, + float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode) : DrawBoundedOp(left, top, right, bottom, 0), - mBitmap(bitmap), mxDivs(xDivs), myDivs(yDivs), - mColors(colors), mxDivsCount(width), myDivsCount(height), - mNumColors(numColors), mAlpha(alpha), mMode(mode) {}; + mBitmap(bitmap), mPatch(patch), mAlpha(alpha), mMode(mode) { + mEntry = Caches::getInstance().assetAtlas.getEntry(bitmap); + }; virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { // NOTE: not calling the virtual method, which takes a paint - return renderer.drawPatch(mBitmap, mxDivs, myDivs, mColors, - mxDivsCount, myDivsCount, mNumColors, - mLocalBounds.left, mLocalBounds.top, + return renderer.drawPatch(mBitmap, mPatch, mEntry, mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom, mAlpha, mMode); } @@ -929,20 +935,16 @@ public: virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) { *batchId = DeferredDisplayList::kOpBatch_Patch; - *mergeId = (mergeid_t)mBitmap; + *mergeId = (mergeid_t) mBitmap; return true; } private: SkBitmap* mBitmap; - const int32_t* mxDivs; - const int32_t* myDivs; - const uint32_t* mColors; - uint32_t mxDivsCount; - uint32_t myDivsCount; - int8_t mNumColors; + Res_png_9patch* mPatch; int mAlpha; SkXfermode::Mode mMode; + AssetAtlas::Entry* mEntry; }; class DrawColorOp : public DrawOp { diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 876c38a..bfd4086 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -257,7 +257,8 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top bitmap = refBitmap(bitmap); paint = refPaint(paint); - addDrawOp(new (alloc()) DrawBitmapOp(bitmap, left, top, paint)); + const AssetAtlas::Entry* entry = mCaches.assetAtlas.getEntry(bitmap); + addDrawOp(new (alloc()) DrawBitmapOp(bitmap, left, top, paint, entry)); return DrawGlInfo::kStatusDone; } @@ -281,7 +282,8 @@ status_t DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float (srcBottom - srcTop == dstBottom - dstTop) && (srcRight - srcLeft == dstRight - dstLeft)) { // transform simple rect to rect drawing case into position bitmap ops, since they merge - addDrawOp(new (alloc()) DrawBitmapOp(bitmap, dstLeft, dstTop, paint)); + const AssetAtlas::Entry* entry = mCaches.assetAtlas.getEntry(bitmap); + addDrawOp(new (alloc()) DrawBitmapOp(bitmap, dstLeft, dstTop, paint, entry)); return DrawGlInfo::kStatusDone; } @@ -313,20 +315,15 @@ status_t DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, in return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, - const int32_t* yDivs, const uint32_t* colors, uint32_t width, uint32_t height, - int8_t numColors, float left, float top, float right, float bottom, SkPaint* paint) { +status_t DisplayListRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, + float left, float top, float right, float bottom, SkPaint* paint) { int alpha; SkXfermode::Mode mode; OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode); bitmap = refBitmap(bitmap); - xDivs = refBuffer<int>(xDivs, width); - yDivs = refBuffer<int>(yDivs, height); - colors = refBuffer<uint32_t>(colors, numColors); - addDrawOp(new (alloc()) DrawPatchOp(bitmap, xDivs, yDivs, colors, width, height, numColors, - left, top, right, bottom, alpha, mode)); + addDrawOp(new (alloc()) DrawPatchOp(bitmap, patch, left, top, right, bottom, alpha, mode)); return DrawGlInfo::kStatusDone; } diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index 75abad6..db08921 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -103,8 +103,7 @@ public: virtual status_t drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint); virtual status_t drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight, float* vertices, int* colors, SkPaint* paint); - virtual status_t drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, - const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, + virtual status_t drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, float left, float top, float right, float bottom, SkPaint* paint); virtual status_t drawColor(int color, SkXfermode::Mode mode); virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint); diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h index 54a3987..a3f7c44 100644 --- a/libs/hwui/Extensions.h +++ b/libs/hwui/Extensions.h @@ -33,9 +33,6 @@ namespace uirenderer { class Extensions: public Singleton<Extensions> { public: - Extensions(); - ~Extensions(); - inline bool hasNPot() const { return mHasNPot; } inline bool hasFramebufferFetch() const { return mHasFramebufferFetch; } inline bool hasDiscardFramebuffer() const { return mHasDiscardFramebuffer; } @@ -53,6 +50,9 @@ public: void dump() const; private: + Extensions(); + ~Extensions(); + friend class Singleton<Extensions>; SortedVector<String8> mExtensionList; diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 543cfa2..a9bf13e 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -24,7 +24,9 @@ #include <utils/Functor.h> #include <utils/Log.h> +#ifdef ANDROID_ENABLE_RENDERSCRIPT #include <RenderScript.h> +#endif #include "utils/Blur.h" #include "utils/Timing.h" @@ -532,13 +534,18 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch return image; } +#ifdef ANDROID_ENABLE_RENDERSCRIPT // Align buffers for renderscript usage if (paddedWidth & (RS_CPU_ALLOCATION_ALIGNMENT - 1)) { paddedWidth += RS_CPU_ALLOCATION_ALIGNMENT - paddedWidth % RS_CPU_ALLOCATION_ALIGNMENT; } - int size = paddedWidth * paddedHeight; uint8_t* dataBuffer = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, size); +#else + int size = paddedWidth * paddedHeight; + uint8_t* dataBuffer = (uint8_t*) malloc(size); +#endif + memset(dataBuffer, 0, size); int penX = radius - bounds.left; @@ -633,43 +640,46 @@ void FontRenderer::removeFont(const Font* font) { } void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) { - if (width * height * radius < RS_MIN_INPUT_CUTOFF) { - float *gaussian = new float[2 * radius + 1]; - Blur::generateGaussianWeights(gaussian, radius); +#ifdef ANDROID_ENABLE_RENDERSCRIPT + if (width * height * radius >= RS_MIN_INPUT_CUTOFF) { + uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); + + if (mRs.get() == 0) { + mRs = new RSC::RS(); + if (!mRs->init(true, true)) { + ALOGE("blur RS failed to init"); + } - uint8_t* scratch = new uint8_t[width * height]; - Blur::horizontal(gaussian, radius, *image, scratch, width, height); - Blur::vertical(gaussian, radius, scratch, *image, width, height); + mRsElement = RSC::Element::A_8(mRs); + mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement); + } - delete[] gaussian; - delete[] scratch; - return; - } + sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); + sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE, + RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image); + sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE, + RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage); - uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); + mRsScript->setRadius(radius); + mRsScript->blur(ain, aout); - if (mRs.get() == 0) { - mRs = new RSC::RS(); - if (!mRs->init(true, true)) { - ALOGE("blur RS failed to init"); - } + // replace the original image's pointer, avoiding a copy back to the original buffer + free(*image); + *image = outImage; - mRsElement = RSC::Element::A_8(mRs); - mRsScript = new RSC::ScriptIntrinsicBlur(mRs, mRsElement); + return; } +#endif - sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0); - sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE, - RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, *image); - sp<RSC::Allocation> aout = RSC::Allocation::createTyped(mRs, t, RS_ALLOCATION_MIPMAP_NONE, - RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED, outImage); + float *gaussian = new float[2 * radius + 1]; + Blur::generateGaussianWeights(gaussian, radius); - mRsScript->setRadius(radius); - mRsScript->blur(ain, aout); + uint8_t* scratch = new uint8_t[width * height]; + Blur::horizontal(gaussian, radius, *image, scratch, width, height); + Blur::vertical(gaussian, radius, scratch, *image, width, height); - // replace the original image's pointer, avoiding a copy back to the original buffer - free(*image); - *image = outImage; + delete[] gaussian; + delete[] scratch; } uint32_t FontRenderer::getCacheSize() const { diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index 307a1d9..cbbd871 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -19,6 +19,7 @@ #include <utils/LruCache.h> #include <utils/Vector.h> +#include <utils/StrongPointer.h> #include <SkPaint.h> @@ -32,11 +33,13 @@ #include "Matrix.h" #include "Properties.h" +#ifdef ANDROID_ENABLE_RENDERSCRIPT namespace RSC { class Element; class RS; class ScriptIntrinsicBlur; } +#endif class Functor; @@ -168,10 +171,12 @@ private: bool mLinearFiltering; +#ifdef ANDROID_ENABLE_RENDERSCRIPT // RS constructs sp<RSC::RS> mRs; sp<const RSC::Element> mRsElement; sp<RSC::ScriptIntrinsicBlur> mRsScript; +#endif static void computeGaussianWeights(float* weights, int32_t radius); static void horizontalBlur(float* weights, int32_t radius, const uint8_t *source, uint8_t *dest, diff --git a/libs/hwui/Image.cpp b/libs/hwui/Image.cpp new file mode 100644 index 0000000..35ca40d --- /dev/null +++ b/libs/hwui/Image.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "OpenGLRenderer" + +#include <utils/Log.h> + +#include "Image.h" + +namespace android { +namespace uirenderer { + +Image::Image(sp<GraphicBuffer> buffer) { + // Create the EGLImage object that maps the GraphicBuffer + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + EGLClientBuffer clientBuffer = (EGLClientBuffer) buffer->getNativeBuffer(); + EGLint attrs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; + + mImage = eglCreateImageKHR(display, EGL_NO_CONTEXT, + EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs); + + if (mImage == EGL_NO_IMAGE_KHR) { + ALOGW("Error creating image (%#x)", eglGetError()); + mTexture = 0; + } else { + // Create a 2D texture to sample from the EGLImage + glGenTextures(1, &mTexture); + glBindTexture(GL_TEXTURE_2D, mTexture); + glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, mImage); + + GLenum status = GL_NO_ERROR; + while ((status = glGetError()) != GL_NO_ERROR) { + ALOGW("Error creating image (%#x)", status); + } + } +} + +Image::~Image() { + if (mImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(eglGetDisplay(EGL_DEFAULT_DISPLAY), mImage); + mImage = EGL_NO_IMAGE_KHR; + + glDeleteTextures(1, &mTexture); + mTexture = 0; + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/Image.h b/libs/hwui/Image.h new file mode 100644 index 0000000..2514535 --- /dev/null +++ b/libs/hwui/Image.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HWUI_IMAGE_H +#define ANDROID_HWUI_IMAGE_H + +#include <EGL/egl.h> +#include <EGL/eglext.h> + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <ui/GraphicBuffer.h> + +namespace android { +namespace uirenderer { + +/** + * A simple wrapper that creates an EGLImage and a texture for a GraphicBuffer. + */ +class Image { +public: + /** + * Creates a new image from the specified graphic buffer. If the image + * cannot be created, getTexture() will return 0 and getImage() will + * return EGL_NO_IMAGE_KHR. + */ + Image(sp<GraphicBuffer> buffer); + ~Image(); + + /** + * Returns the name of the GL texture that can be used to sample + * from this image. + */ + GLuint getTexture() const { + return mTexture; + } + + /** + * Returns the name of the EGL image represented by this object. + */ + EGLImageKHR getImage() const { + return mImage; + } + +private: + GLuint mTexture; + EGLImageKHR mImage; +}; // class Image + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_IMAGE_H diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index ddb190e..f220e4f 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -21,7 +21,6 @@ #include <sys/types.h> #include <SkCanvas.h> -#include <SkPathMeasure.h> #include <SkTypeface.h> #include <utils/Log.h> @@ -120,6 +119,7 @@ OpenGLRenderer::OpenGLRenderer(): mFirstSnapshot = new Snapshot; mFrameStarted = false; + mCountOverdraw = false; mScissorOptimizationDisabled = false; } @@ -222,6 +222,7 @@ status_t OpenGLRenderer::prepare(bool opaque) { status_t OpenGLRenderer::prepareDirty(float left, float top, float right, float bottom, bool opaque) { + setupFrameState(left, top, right, bottom, opaque); // Layer renderers will start the frame immediately @@ -253,7 +254,7 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa } status_t OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) { - if (!opaque) { + if (!opaque || mCountOverdraw) { mCaches.enableScissor(); mCaches.setScissor(left, mSnapshot->height - bottom, right - left, bottom - top); glClear(GL_COLOR_BUFFER_BIT); @@ -335,6 +336,10 @@ void OpenGLRenderer::finish() { #endif } + if (mCountOverdraw) { + countOverdraw(); + } + mFrameStarted = false; } @@ -459,7 +464,7 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { info.height = getSnapshot()->height; getSnapshot()->transform->copyTo(&info.transform[0]); - status_t result = (*functor)(DrawGlInfo::kModeDraw, &info) | DrawGlInfo::kStatusDrew; + status_t result = (*functor)(DrawGlInfo::kModeDraw, &info); if (result != DrawGlInfo::kStatusDone) { Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom); @@ -471,7 +476,7 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { } resume(); - return result; + return result | DrawGlInfo::kStatusDrew; } /////////////////////////////////////////////////////////////////////////////// @@ -524,6 +529,21 @@ void OpenGLRenderer::renderOverdraw() { } } +void OpenGLRenderer::countOverdraw() { + size_t count = mWidth * mHeight; + uint32_t* buffer = new uint32_t[count]; + glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]); + + size_t total = 0; + for (size_t i = 0; i < count; i++) { + total += buffer[i] & 0xff; + } + + mOverdraw = total / float(count); + + delete[] buffer; +} + /////////////////////////////////////////////////////////////////////////////// // Layers /////////////////////////////////////////////////////////////////////////////// @@ -1656,6 +1676,8 @@ void OpenGLRenderer::setupDraw(bool clear) { mDescription.hasDebugHighlight = !mCaches.debugOverdraw && mCaches.debugStencilClip == Caches::kStencilShowHighlight && mCaches.stencil.isTestEnabled(); + + mDescription.emulateStencil = mCountOverdraw; } void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) { @@ -1873,7 +1895,7 @@ void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) { void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) { bool force = false; - if (!vertices) { + if (!vertices || vbo) { force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo); } else { force = mCaches.unbindMeshBuffer(); @@ -1904,8 +1926,18 @@ void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid* mCaches.unbindIndicesBuffer(); } -void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords) { - bool force = mCaches.unbindMeshBuffer(); +void OpenGLRenderer::setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) { + bool force = false; + // If vbo is != 0 we want to treat the vertices parameter as an offset inside + // a VBO. However, if vertices is set to NULL and vbo == 0 then we want to + // use the default VBO found in Caches + if (!vertices || vbo) { + force = mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo); + } else { + force = mCaches.unbindMeshBuffer(); + } + mCaches.bindIndicesBuffer(); + mCaches.bindPositionVertexPointer(force, vertices); if (mCaches.currentProgram->texCoords >= 0) { mCaches.bindTexCoordsVertexPointer(force, texCoords); @@ -1980,9 +2012,11 @@ void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, Sk texture->setFilter(FILTER(paint), true); } + // No need to check for a UV mapper on the texture object, only ARGB_8888 + // bitmaps get packed in the atlas drawAlpha8TextureMesh(x, y, x + texture->width, y + texture->height, texture->id, - paint != NULL, color, alpha, mode, (GLvoid*) NULL, - (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform); + paint != NULL, color, alpha, mode, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset, + GL_TRIANGLE_STRIP, gMeshCount, ignoreTransform); } status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices, @@ -1992,8 +2026,9 @@ status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureV mCaches.setScissorEnabled(mScissorOptimizationDisabled); mCaches.activeTexture(0); - Texture* texture = mCaches.textureCache.get(bitmap); + Texture* texture = getTexture(bitmap); if (!texture) return DrawGlInfo::kStatusDone; + const AutoTexture autoCleanup(texture); int alpha; @@ -2030,7 +2065,7 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkP } mCaches.activeTexture(0); - Texture* texture = mCaches.textureCache.get(bitmap); + Texture* texture = getTexture(bitmap); if (!texture) return DrawGlInfo::kStatusDone; const AutoTexture autoCleanup(texture); @@ -2053,7 +2088,7 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* } mCaches.activeTexture(0); - Texture* texture = mCaches.textureCache.get(bitmap); + Texture* texture = getTexture(bitmap); if (!texture) return DrawGlInfo::kStatusDone; const AutoTexture autoCleanup(texture); @@ -2116,6 +2151,10 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes cleanupColors = true; } + mCaches.activeTexture(0); + Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap); + const UvMapper& mapper(getMapper(texture)); + for (int32_t y = 0; y < meshHeight; y++) { for (int32_t x = 0; x < meshWidth; x++) { uint32_t i = (y * (meshWidth + 1) + x) * 2; @@ -2125,6 +2164,8 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes float v1 = float(y) / meshHeight; float v2 = float(y + 1) / meshHeight; + mapper.map(u1, v1, u2, v2); + int ax = i + (meshWidth + 1) * 2; int ay = ax + 1; int bx = i; @@ -2154,11 +2195,12 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes return DrawGlInfo::kStatusDone; } - mCaches.activeTexture(0); - Texture* texture = mCaches.textureCache.get(bitmap); if (!texture) { - if (cleanupColors) delete[] colors; - return DrawGlInfo::kStatusDone; + texture = mCaches.textureCache.get(bitmap); + if (!texture) { + if (cleanupColors) delete[] colors; + return DrawGlInfo::kStatusDone; + } } const AutoTexture autoCleanup(texture); @@ -2211,17 +2253,19 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, } mCaches.activeTexture(0); - Texture* texture = mCaches.textureCache.get(bitmap); + Texture* texture = getTexture(bitmap); if (!texture) return DrawGlInfo::kStatusDone; const AutoTexture autoCleanup(texture); const float width = texture->width; const float height = texture->height; - const float u1 = fmax(0.0f, srcLeft / width); - const float v1 = fmax(0.0f, srcTop / height); - const float u2 = fmin(1.0f, srcRight / width); - const float v2 = fmin(1.0f, srcBottom / height); + float u1 = fmax(0.0f, srcLeft / width); + float v1 = fmax(0.0f, srcTop / height); + float u2 = fmin(1.0f, srcRight / width); + float v2 = fmin(1.0f, srcBottom / height); + + getMapper(texture).map(u1, v1, u2, v2); mCaches.unbindMeshBuffer(); resetDrawTextureTexCoords(u1, v1, u2, v2); @@ -2292,34 +2336,32 @@ status_t OpenGLRenderer::drawBitmap(SkBitmap* bitmap, return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, - const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, +status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, float left, float top, float right, float bottom, SkPaint* paint) { int alpha; SkXfermode::Mode mode; getAlphaAndMode(paint, &alpha, &mode); - return drawPatch(bitmap, xDivs, yDivs, colors, width, height, numColors, + return drawPatch(bitmap, patch, mCaches.assetAtlas.getEntry(bitmap), left, top, right, bottom, alpha, mode); } -status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, - const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, - float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode) { +status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, + AssetAtlas::Entry* entry, float left, float top, float right, float bottom, + int alpha, SkXfermode::Mode mode) { if (quickReject(left, top, right, bottom)) { return DrawGlInfo::kStatusDone; } - alpha *= mSnapshot->alpha; - - const Patch* mesh = mCaches.patchCache.get(bitmap->width(), bitmap->height(), - right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors); + const Patch* mesh = mCaches.patchCache.get(entry, bitmap->width(), bitmap->height(), + right - left, bottom - top, patch); if (CC_LIKELY(mesh && mesh->verticesCount > 0)) { mCaches.activeTexture(0); - Texture* texture = mCaches.textureCache.get(bitmap); + Texture* texture = entry ? &entry->texture : mCaches.textureCache.get(bitmap); if (!texture) return DrawGlInfo::kStatusDone; const AutoTexture autoCleanup(texture); + texture->setWrap(GL_CLAMP_TO_EDGE, true); texture->setFilter(GL_LINEAR, true); @@ -2342,19 +2384,23 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const } } + alpha *= mSnapshot->alpha; + if (CC_LIKELY(pureTranslate)) { const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f); const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f); - drawTextureMesh(x, y, x + right - left, y + bottom - top, texture->id, alpha / 255.0f, - mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset, - GL_TRIANGLES, mesh->verticesCount, false, true, mesh->meshBuffer, - true, !mesh->hasEmptyQuads); + right = x + right - left; + bottom = y + bottom - top; + drawIndexedTextureMesh(x, y, right, bottom, texture->id, alpha / 255.0f, + mode, texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset, + GL_TRIANGLES, mesh->indexCount, false, true, + mCaches.patchCache.getMeshBuffer(), true, !mesh->hasEmptyQuads); } else { - drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, - mode, texture->blend, (GLvoid*) 0, (GLvoid*) gMeshTextureOffset, - GL_TRIANGLES, mesh->verticesCount, false, false, mesh->meshBuffer, - true, !mesh->hasEmptyQuads); + drawIndexedTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, + mode, texture->blend, (GLvoid*) mesh->offset, (GLvoid*) mesh->textureOffset, + GL_TRIANGLES, mesh->indexCount, false, false, + mCaches.patchCache.getMeshBuffer(), true, !mesh->hasEmptyQuads); } } @@ -3196,6 +3242,14 @@ SkPaint* OpenGLRenderer::filterPaint(SkPaint* paint) { // Drawing implementation /////////////////////////////////////////////////////////////////////////////// +Texture* OpenGLRenderer::getTexture(SkBitmap* bitmap) { + Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap); + if (!texture) { + return mCaches.textureCache.get(bitmap); + } + return texture; +} + void OpenGLRenderer::drawPathTexture(const PathTexture* texture, float x, float y, SkPaint* paint) { if (quickReject(x, y, x + texture->width, y + texture->height)) { @@ -3389,19 +3443,35 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b texture->setWrap(GL_CLAMP_TO_EDGE, true); + GLvoid* vertices = (GLvoid*) NULL; + GLvoid* texCoords = (GLvoid*) gMeshTextureOffset; + + if (texture->uvMapper) { + vertices = &mMeshVertices[0].position[0]; + texCoords = &mMeshVertices[0].texture[0]; + + Rect uvs(0.0f, 0.0f, 1.0f, 1.0f); + texture->uvMapper->map(uvs); + + resetDrawTextureTexCoords(uvs.left, uvs.top, uvs.right, uvs.bottom); + } + if (CC_LIKELY(currentTransform().isPureTranslate())) { const float x = (int) floorf(left + currentTransform().getTranslateX() + 0.5f); const float y = (int) floorf(top + currentTransform().getTranslateY() + 0.5f); texture->setFilter(GL_NEAREST, true); drawTextureMesh(x, y, x + texture->width, y + texture->height, texture->id, - alpha / 255.0f, mode, texture->blend, (GLvoid*) NULL, - (GLvoid*) gMeshTextureOffset, GL_TRIANGLE_STRIP, gMeshCount, false, true); + alpha / 255.0f, mode, texture->blend, vertices, texCoords, + GL_TRIANGLE_STRIP, gMeshCount, false, true); } else { texture->setFilter(FILTER(paint), true); drawTextureMesh(left, top, right, bottom, texture->id, alpha / 255.0f, mode, - texture->blend, (GLvoid*) NULL, (GLvoid*) gMeshTextureOffset, - GL_TRIANGLE_STRIP, gMeshCount); + texture->blend, vertices, texCoords, GL_TRIANGLE_STRIP, gMeshCount); + } + + if (texture->uvMapper) { + resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); } } @@ -3438,6 +3508,33 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b finishDrawTexture(); } +void OpenGLRenderer::drawIndexedTextureMesh(float left, float top, float right, float bottom, + GLuint texture, float alpha, SkXfermode::Mode mode, bool blend, + GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, + bool swapSrcDst, bool ignoreTransform, GLuint vbo, bool ignoreScale, bool dirty) { + + setupDraw(); + setupDrawWithTexture(); + setupDrawColor(alpha, alpha, alpha, alpha); + setupDrawColorFilter(); + setupDrawBlending(blend, mode, swapSrcDst); + setupDrawProgram(); + if (!dirty) setupDrawDirtyRegionsDisabled(); + if (!ignoreScale) { + setupDrawModelView(left, top, right, bottom, ignoreTransform); + } else { + setupDrawModelViewTranslate(left, top, right, bottom, ignoreTransform); + } + setupDrawTexture(texture); + setupDrawPureColorUniforms(); + setupDrawColorFilterUniforms(); + setupDrawMeshIndices(vertices, texCoords, vbo); + + glDrawElements(drawMode, elementsCount, GL_UNSIGNED_SHORT, NULL); + + finishDrawTexture(); +} + void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, float bottom, GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode, GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, @@ -3471,6 +3568,19 @@ void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, f void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description, bool swapSrcDst) { + if (mCountOverdraw) { + if (!mCaches.blend) glEnable(GL_BLEND); + if (mCaches.lastSrcMode != GL_ONE || mCaches.lastDstMode != GL_ONE) { + glBlendFunc(GL_ONE, GL_ONE); + } + + mCaches.blend = true; + mCaches.lastSrcMode = GL_ONE; + mCaches.lastDstMode = GL_ONE; + + return; + } + blend = blend || mode != SkXfermode::kSrcOver_Mode; if (blend) { diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index a0ad888..df275d7 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -34,6 +34,8 @@ #include <cutils/compiler.h> +#include <androidfw/ResourceTypes.h> + #include "Debug.h" #include "Extensions.h" #include "Matrix.h" @@ -43,6 +45,7 @@ #include "Vertex.h" #include "SkiaShader.h" #include "SkiaColorFilter.h" +#include "UvMapper.h" #include "Caches.h" namespace android { @@ -78,7 +81,8 @@ enum DrawOpMode { }; struct DeferredDisplayState { - Rect mBounds; // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped. + // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped + Rect mBounds; // the below are set and used by the OpenGLRenderer at record and deferred playback bool mClipValid; @@ -188,6 +192,14 @@ public: */ virtual void resume(); + ANDROID_API void setCountOverdrawEnabled(bool enabled) { + mCountOverdraw = enabled; + } + + ANDROID_API float getOverdraw() { + return mCountOverdraw ? mOverdraw : 0.0f; + } + ANDROID_API status_t invokeFunctors(Rect& dirty); ANDROID_API void detachFunctor(Functor* functor); ANDROID_API void attachFunctor(Functor* functor); @@ -248,11 +260,9 @@ public: virtual status_t drawBitmapData(SkBitmap* bitmap, float left, float top, SkPaint* paint); virtual status_t drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight, float* vertices, int* colors, SkPaint* paint); - virtual status_t drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, - const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, + virtual status_t drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, float left, float top, float right, float bottom, SkPaint* paint); - status_t drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, - const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, + status_t drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, AssetAtlas::Entry* entry, float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode); virtual status_t drawColor(int color, SkXfermode::Mode mode); virtual status_t drawRect(float left, float top, float right, float bottom, SkPaint* paint); @@ -798,6 +808,12 @@ private: bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0, bool ignoreScale = false, bool dirty = true); + void drawIndexedTextureMesh(float left, float top, float right, float bottom, GLuint texture, + float alpha, SkXfermode::Mode mode, bool blend, + GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, + bool swapSrcDst = false, bool ignoreTransform = false, GLuint vbo = 0, + bool ignoreScale = false, bool dirty = true); + void drawAlpha8TextureMesh(float left, float top, float right, float bottom, GLuint texture, bool hasColor, int color, int alpha, SkXfermode::Mode mode, GLvoid* vertices, GLvoid* texCoords, GLenum drawMode, GLsizei elementsCount, @@ -943,7 +959,7 @@ private: void setupDrawTextGammaUniforms(); void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0); void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid* colors); - void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords); + void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, GLuint vbo = 0); void setupDrawVertices(GLvoid* vertices); void finishDrawTexture(); void accountForClear(SkXfermode::Mode mode); @@ -973,6 +989,7 @@ private: void debugOverdraw(bool enable, bool clear); void renderOverdraw(); + void countOverdraw(); /** * Should be invoked every time the glScissor is modified. @@ -985,6 +1002,17 @@ private: return *mSnapshot->transform; } + inline const UvMapper& getMapper(const Texture* texture) { + return texture && texture->uvMapper ? *texture->uvMapper : mUvMapper; + } + + /** + * Returns a texture object for the specified bitmap. The texture can + * come from the texture cache or an atlas. If this method returns + * NULL, the texture could not be found and/or allocated. + */ + Texture* getTexture(SkBitmap* bitmap); + // Dimensions of the drawing surface int mWidth, mHeight; @@ -1010,6 +1038,9 @@ private: // Used to draw textured quads TextureVertex mMeshVertices[4]; + // Default UV mapper + const UvMapper mUvMapper; + // shader, filters, and shadow DrawModifiers mDrawModifiers; SkPaint mFilteredPaint; @@ -1050,6 +1081,12 @@ private: // No-ops start/endTiling when set bool mSuppressTiling; + + // If true, this renderer will setup drawing to emulate + // an increment stencil buffer in the color buffer + bool mCountOverdraw; + float mOverdraw; + // Optional name of the renderer String8 mName; diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp index 45c619e..6b0734a 100644 --- a/libs/hwui/Patch.cpp +++ b/libs/hwui/Patch.cpp @@ -20,9 +20,10 @@ #include <utils/Log.h> -#include "Patch.h" #include "Caches.h" +#include "Patch.h" #include "Properties.h" +#include "UvMapper.h" namespace android { namespace uirenderer { @@ -31,90 +32,61 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -Patch::Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads): - mXCount(xCount), mYCount(yCount), mEmptyQuads(emptyQuads) { - // Initialized with the maximum number of vertices we will need - // 2 triangles per patch, 3 vertices per triangle - uint32_t maxVertices = ((xCount + 1) * (yCount + 1) - emptyQuads) * 2 * 3; - mVertices = new TextureVertex[maxVertices]; - mAllocatedVerticesCount = 0; - - verticesCount = 0; - hasEmptyQuads = emptyQuads > 0; - - mColorKey = 0; - mXDivs = new int32_t[mXCount]; - mYDivs = new int32_t[mYCount]; - - PATCH_LOGD(" patch: xCount = %d, yCount = %d, emptyQuads = %d, max vertices = %d", - xCount, yCount, emptyQuads, maxVertices); - - glGenBuffers(1, &meshBuffer); +Patch::Patch(): verticesCount(0), indexCount(0), hasEmptyQuads(false) { } Patch::~Patch() { - delete[] mVertices; - delete[] mXDivs; - delete[] mYDivs; - glDeleteBuffers(1, &meshBuffer); } /////////////////////////////////////////////////////////////////////////////// -// Patch management +// Vertices management /////////////////////////////////////////////////////////////////////////////// -void Patch::copy(const int32_t* xDivs, const int32_t* yDivs) { - memcpy(mXDivs, xDivs, mXCount * sizeof(int32_t)); - memcpy(mYDivs, yDivs, mYCount * sizeof(int32_t)); +uint32_t Patch::getSize() const { + return verticesCount * sizeof(TextureVertex); } -void Patch::updateColorKey(const uint32_t colorKey) { - mColorKey = colorKey; +TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight, + float left, float top, float right, float bottom, const Res_png_9patch* patch) { + UvMapper mapper; + return createMesh(bitmapWidth, bitmapHeight, left, top, right, bottom, mapper, patch); } -bool Patch::matches(const int32_t* xDivs, const int32_t* yDivs, - const uint32_t colorKey, const int8_t emptyQuads) { +TextureVertex* Patch::createMesh(const float bitmapWidth, const float bitmapHeight, + float left, float top, float right, float bottom, + const UvMapper& mapper, const Res_png_9patch* patch) { - bool matches = true; + const uint32_t* colors = &patch->colors[0]; + const int8_t numColors = patch->numColors; - if (mEmptyQuads != emptyQuads) { - mEmptyQuads = emptyQuads; - hasEmptyQuads = emptyQuads > 0; - matches = false; - } - - if (mColorKey != colorKey) { - updateColorKey(colorKey); - matches = false; - } - - if (memcmp(mXDivs, xDivs, mXCount * sizeof(int32_t))) { - memcpy(mXDivs, xDivs, mXCount * sizeof(int32_t)); - matches = false; + mColorKey = 0; + int8_t emptyQuads = 0; + + if (uint8_t(numColors) < sizeof(uint32_t) * 4) { + for (int8_t i = 0; i < numColors; i++) { + if (colors[i] == 0x0) { + emptyQuads++; + mColorKey |= 0x1 << i; + } + } } - if (memcmp(mYDivs, yDivs, mYCount * sizeof(int32_t))) { - memcpy(mYDivs, yDivs, mYCount * sizeof(int32_t)); - matches = false; - } + hasEmptyQuads = emptyQuads > 0; - return matches; -} + uint32_t xCount = patch->numXDivs; + uint32_t yCount = patch->numYDivs; -/////////////////////////////////////////////////////////////////////////////// -// Vertices management -/////////////////////////////////////////////////////////////////////////////// + uint32_t maxVertices = ((xCount + 1) * (yCount + 1) - emptyQuads) * 4; + if (maxVertices == 0) return NULL; -void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, - float left, float top, float right, float bottom) { - if (hasEmptyQuads) quads.clear(); + TextureVertex* vertices = new TextureVertex[maxVertices]; + TextureVertex* vertex = vertices; - // Reset the vertices count here, we will count exactly how many - // vertices we actually need when generating the quads - verticesCount = 0; + const int32_t* xDivs = patch->xDivs; + const int32_t* yDivs = patch->yDivs; - const uint32_t xStretchCount = (mXCount + 1) >> 1; - const uint32_t yStretchCount = (mYCount + 1) >> 1; + const uint32_t xStretchCount = (xCount + 1) >> 1; + const uint32_t yStretchCount = (yCount + 1) >> 1; float stretchX = 0.0f; float stretchY = 0.0f; @@ -124,8 +96,8 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, if (xStretchCount > 0) { uint32_t stretchSize = 0; - for (uint32_t i = 1; i < mXCount; i += 2) { - stretchSize += mXDivs[i] - mXDivs[i - 1]; + for (uint32_t i = 1; i < xCount; i += 2) { + stretchSize += xDivs[i] - xDivs[i - 1]; } const float xStretchTex = stretchSize; const float fixed = bitmapWidth - stretchSize; @@ -136,8 +108,8 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, if (yStretchCount > 0) { uint32_t stretchSize = 0; - for (uint32_t i = 1; i < mYCount; i += 2) { - stretchSize += mYDivs[i] - mYDivs[i - 1]; + for (uint32_t i = 1; i < yCount; i += 2) { + stretchSize += yDivs[i] - yDivs[i - 1]; } const float yStretchTex = stretchSize; const float fixed = bitmapHeight - stretchSize; @@ -146,7 +118,6 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, rescaleY = fixed == 0.0f ? 0.0f : fminf(fmaxf(bottom - top, 0.0f) / fixed, 1.0f); } - TextureVertex* vertex = mVertices; uint32_t quadCount = 0; float previousStepY = 0.0f; @@ -155,8 +126,10 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, float y2 = 0.0f; float v1 = 0.0f; - for (uint32_t i = 0; i < mYCount; i++) { - float stepY = mYDivs[i]; + mUvMapper = mapper; + + for (uint32_t i = 0; i < yCount; i++) { + float stepY = yDivs[i]; const float segment = stepY - previousStepY; if (i & 1) { @@ -170,15 +143,8 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, v1 += vOffset / bitmapHeight; if (stepY > 0.0f) { -#if DEBUG_EXPLODE_PATCHES - y1 += i * EXPLODE_GAP; - y2 += i * EXPLODE_GAP; -#endif - generateRow(vertex, y1, y2, v1, v2, stretchX, rescaleX, right - left, - bitmapWidth, quadCount); -#if DEBUG_EXPLODE_PATCHES - y2 -= i * EXPLODE_GAP; -#endif + generateRow(xDivs, xCount, vertex, y1, y2, v1, v2, stretchX, rescaleX, + right - left, bitmapWidth, quadCount); } y1 = y2; @@ -189,33 +155,16 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, if (previousStepY != bitmapHeight) { y2 = bottom - top; -#if DEBUG_EXPLODE_PATCHES - y1 += mYCount * EXPLODE_GAP; - y2 += mYCount * EXPLODE_GAP; -#endif - generateRow(vertex, y1, y2, v1, 1.0f, stretchX, rescaleX, right - left, - bitmapWidth, quadCount); - } - - if (verticesCount > 0) { - Caches& caches = Caches::getInstance(); - caches.bindMeshBuffer(meshBuffer); - if (mAllocatedVerticesCount < verticesCount) { - glBufferData(GL_ARRAY_BUFFER, sizeof(TextureVertex) * verticesCount, - mVertices, GL_DYNAMIC_DRAW); - mAllocatedVerticesCount = verticesCount; - } else { - glBufferSubData(GL_ARRAY_BUFFER, 0, - sizeof(TextureVertex) * verticesCount, mVertices); - } - caches.resetVertexPointers(); + generateRow(xDivs, xCount, vertex, y1, y2, v1, 1.0f, stretchX, rescaleX, + right - left, bitmapWidth, quadCount); } - PATCH_LOGD(" patch: new vertices count = %d", verticesCount); + return vertices; } -void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, float v2, - float stretchX, float rescaleX, float width, float bitmapWidth, uint32_t& quadCount) { +void Patch::generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex, + float y1, float y2, float v1, float v2, float stretchX, float rescaleX, + float width, float bitmapWidth, uint32_t& quadCount) { float previousStepX = 0.0f; float x1 = 0.0f; @@ -223,8 +172,8 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl float u1 = 0.0f; // Generate the row quad by quad - for (uint32_t i = 0; i < mXCount; i++) { - float stepX = mXDivs[i]; + for (uint32_t i = 0; i < xCount; i++) { + float stepX = xDivs[i]; const float segment = stepX - previousStepX; if (i & 1) { @@ -238,14 +187,7 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl u1 += uOffset / bitmapWidth; if (stepX > 0.0f) { -#if DEBUG_EXPLODE_PATCHES - x1 += i * EXPLODE_GAP; - x2 += i * EXPLODE_GAP; -#endif generateQuad(vertex, x1, y1, x2, y2, u1, v1, u2, v2, quadCount); -#if DEBUG_EXPLODE_PATCHES - x2 -= i * EXPLODE_GAP; -#endif } x1 = x2; @@ -256,10 +198,6 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl if (previousStepX != bitmapWidth) { x2 = width; -#if DEBUG_EXPLODE_PATCHES - x1 += mXCount * EXPLODE_GAP; - x2 += mXCount * EXPLODE_GAP; -#endif generateQuad(vertex, x1, y1, x2, y2, u1, v1, 1.0f, v2, quadCount); } } @@ -290,18 +228,15 @@ void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, f quads.add(bounds); } - // Left triangle + mUvMapper.map(u1, v1, u2, v2); + TextureVertex::set(vertex++, x1, y1, u1, v1); TextureVertex::set(vertex++, x2, y1, u2, v1); TextureVertex::set(vertex++, x1, y2, u1, v2); - - // Right triangle - TextureVertex::set(vertex++, x1, y2, u1, v2); - TextureVertex::set(vertex++, x2, y1, u2, v1); TextureVertex::set(vertex++, x2, y2, u2, v2); - // A quad is made of 2 triangles, 6 vertices - verticesCount += 6; + verticesCount += 4; + indexCount += 6; #if DEBUG_PATCHES_VERTICES PATCH_LOGD(" quad %d", oldQuadCount); diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h index ee7bf70..448cf60 100644 --- a/libs/hwui/Patch.h +++ b/libs/hwui/Patch.h @@ -23,62 +23,52 @@ #include <utils/Vector.h> +#include <androidfw/ResourceTypes.h> + #include "Rect.h" +#include "UvMapper.h" #include "Vertex.h" namespace android { namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// -// Defines -/////////////////////////////////////////////////////////////////////////////// - -#define EXPLODE_GAP 4 - -/////////////////////////////////////////////////////////////////////////////// // 9-patch structures /////////////////////////////////////////////////////////////////////////////// -/** - * An OpenGL patch. This contains an array of vertices and an array of - * indices to render the vertices. - */ struct Patch { - Patch(const uint32_t xCount, const uint32_t yCount, const int8_t emptyQuads); + Patch(); ~Patch(); - void updateVertices(const float bitmapWidth, const float bitmapHeight, - float left, float top, float right, float bottom); - - void updateColorKey(const uint32_t colorKey); - void copy(const int32_t* xDivs, const int32_t* yDivs); - bool matches(const int32_t* xDivs, const int32_t* yDivs, - const uint32_t colorKey, const int8_t emptyQuads); + /** + * Returns the size of this patch's mesh in bytes. + */ + uint32_t getSize() const; - GLuint meshBuffer; uint32_t verticesCount; + uint32_t indexCount; bool hasEmptyQuads; Vector<Rect> quads; -private: - TextureVertex* mVertices; - uint32_t mAllocatedVerticesCount; - - int32_t* mXDivs; - int32_t* mYDivs; - uint32_t mColorKey; + GLintptr offset; + GLintptr textureOffset; - uint32_t mXCount; - uint32_t mYCount; - int8_t mEmptyQuads; + TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight, + float left, float top, float right, float bottom, + const Res_png_9patch* patch); + TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight, + float left, float top, float right, float bottom, + const UvMapper& mapper, const Res_png_9patch* patch); - void generateRow(TextureVertex*& vertex, float y1, float y2, - float v1, float v2, float stretchX, float rescaleX, +private: + void generateRow(const int32_t* xDivs, uint32_t xCount, TextureVertex*& vertex, + float y1, float y2, float v1, float v2, float stretchX, float rescaleX, float width, float bitmapWidth, uint32_t& quadCount); - void generateQuad(TextureVertex*& vertex, - float x1, float y1, float x2, float y2, - float u1, float v1, float u2, float v2, - uint32_t& quadCount); + void generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, float y2, + float u1, float v1, float u2, float v2, uint32_t& quadCount); + + uint32_t mColorKey; + UvMapper mUvMapper; }; // struct Patch }; // namespace uirenderer diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp index 62e38d3..c2b28d1 100644 --- a/libs/hwui/PatchCache.cpp +++ b/libs/hwui/PatchCache.cpp @@ -16,8 +16,10 @@ #define LOG_TAG "OpenGLRenderer" +#include <utils/JenkinsHash.h> #include <utils/Log.h> +#include "Caches.h" #include "PatchCache.h" #include "Properties.h" @@ -28,107 +30,106 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -PatchCache::PatchCache(): mMaxEntries(DEFAULT_PATCH_CACHE_SIZE) { -} - -PatchCache::PatchCache(uint32_t maxEntries): mMaxEntries(maxEntries) { +PatchCache::PatchCache(): mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, NULL) > 0) { + INIT_LOGD(" Setting patch cache size to %skB", property); + mMaxSize = KB(atoi(property)); + } else { + INIT_LOGD(" Using default patch cache size of %.2fkB", DEFAULT_PATCH_CACHE_SIZE); + mMaxSize = KB(DEFAULT_PATCH_CACHE_SIZE); + } + mSize = 0; } PatchCache::~PatchCache() { clear(); } +void PatchCache::init(Caches& caches) { + glGenBuffers(1, &mMeshBuffer); + caches.bindMeshBuffer(mMeshBuffer); + caches.resetVertexPointers(); + + glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW); +} + /////////////////////////////////////////////////////////////////////////////// // Caching /////////////////////////////////////////////////////////////////////////////// -int PatchCache::PatchDescription::compare( - const PatchCache::PatchDescription& lhs, const PatchCache::PatchDescription& rhs) { - int deltaInt = lhs.bitmapWidth - rhs.bitmapWidth; - if (deltaInt != 0) return deltaInt; - - deltaInt = lhs.bitmapHeight - rhs.bitmapHeight; - if (deltaInt != 0) return deltaInt; - - if (lhs.pixelWidth < rhs.pixelWidth) return -1; - if (lhs.pixelWidth > rhs.pixelWidth) return +1; - - if (lhs.pixelHeight < rhs.pixelHeight) return -1; - if (lhs.pixelHeight > rhs.pixelHeight) return +1; - - deltaInt = lhs.xCount - rhs.xCount; - if (deltaInt != 0) return deltaInt; - - deltaInt = lhs.yCount - rhs.yCount; - if (deltaInt != 0) return deltaInt; - - deltaInt = lhs.emptyCount - rhs.emptyCount; - if (deltaInt != 0) return deltaInt; - - deltaInt = lhs.colorKey - rhs.colorKey; - if (deltaInt != 0) return deltaInt; +hash_t PatchCache::PatchDescription::hash() const { + uint32_t hash = JenkinsHashMix(0, android::hash_type(mPatch)); + hash = JenkinsHashMix(hash, mBitmapWidth); + hash = JenkinsHashMix(hash, mBitmapHeight); + hash = JenkinsHashMix(hash, mPixelWidth); + hash = JenkinsHashMix(hash, mPixelHeight); + return JenkinsHashWhiten(hash); +} - return 0; +int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs, + const PatchCache::PatchDescription& rhs) { + return memcmp(&lhs, &rhs, sizeof(PatchDescription)); } void PatchCache::clear() { - size_t count = mCache.size(); - for (size_t i = 0; i < count; i++) { - delete mCache.valueAt(i); + glDeleteBuffers(1, &mMeshBuffer); + clearCache(); + mSize = 0; +} + +void PatchCache::clearCache() { + LruCache<PatchDescription, Patch*>::Iterator i(mCache); + while (i.next()) { + delete i.value(); } mCache.clear(); } -Patch* PatchCache::get(const uint32_t bitmapWidth, const uint32_t bitmapHeight, - const float pixelWidth, const float pixelHeight, - const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors, - const uint32_t width, const uint32_t height, const int8_t numColors) { +const Patch* PatchCache::get(const AssetAtlas::Entry* entry, + const uint32_t bitmapWidth, const uint32_t bitmapHeight, + const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch) { - int8_t transparentQuads = 0; - uint32_t colorKey = 0; + const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch); + const Patch* mesh = mCache.get(description); - if (uint8_t(numColors) < sizeof(uint32_t) * 4) { - for (int8_t i = 0; i < numColors; i++) { - if (colors[i] == 0x0) { - transparentQuads++; - colorKey |= 0x1 << i; - } + if (!mesh) { + Patch* newMesh = new Patch(); + TextureVertex* vertices; + + if (entry) { + vertices = newMesh->createMesh(bitmapWidth, bitmapHeight, + 0.0f, 0.0f, pixelWidth, pixelHeight, entry->uvMapper, patch); + } else { + vertices = newMesh->createMesh(bitmapWidth, bitmapHeight, + 0.0f, 0.0f, pixelWidth, pixelHeight, patch); } - } - // If the 9patch is made of only transparent quads - if (transparentQuads == int8_t((width + 1) * (height + 1))) { - return NULL; - } + if (vertices) { + Caches& caches = Caches::getInstance(); + caches.bindMeshBuffer(mMeshBuffer); + caches.resetVertexPointers(); + + // TODO: Simply remove the oldest items until we have enough room + // This will require to keep a list of free blocks in the VBO + uint32_t size = newMesh->getSize(); + if (mSize + size > mMaxSize) { + clearCache(); + glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW); + mSize = 0; + } - const PatchDescription description(bitmapWidth, bitmapHeight, - pixelWidth, pixelHeight, width, height, transparentQuads, colorKey); + newMesh->offset = (GLintptr) mSize; + newMesh->textureOffset = newMesh->offset + gMeshTextureOffset; + mSize += size; - ssize_t index = mCache.indexOfKey(description); - Patch* mesh = NULL; - if (index >= 0) { - mesh = mCache.valueAt(index); - } + glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices); - if (!mesh) { - PATCH_LOGD("New patch mesh " - "xCount=%d yCount=%d, w=%.2f h=%.2f, bw=%.2f bh=%.2f", - width, height, pixelWidth, pixelHeight, bitmapWidth, bitmapHeight); - - mesh = new Patch(width, height, transparentQuads); - mesh->updateColorKey(colorKey); - mesh->copy(xDivs, yDivs); - mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f, pixelWidth, pixelHeight); - - if (mCache.size() >= mMaxEntries) { - delete mCache.valueAt(mCache.size() - 1); - mCache.removeItemsAt(mCache.size() - 1, 1); + delete[] vertices; } - mCache.add(description, mesh); - } else if (!mesh->matches(xDivs, yDivs, colorKey, transparentQuads)) { - PATCH_LOGD("Patch mesh does not match, refreshing vertices"); - mesh->updateVertices(bitmapWidth, bitmapHeight, 0.0f, 0.0f, pixelWidth, pixelHeight); + mCache.put(description, newMesh); + return newMesh; } return mesh; diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h index 0822cba..129a0dc 100644 --- a/libs/hwui/PatchCache.h +++ b/libs/hwui/PatchCache.h @@ -17,8 +17,13 @@ #ifndef ANDROID_HWUI_PATCH_CACHE_H #define ANDROID_HWUI_PATCH_CACHE_H -#include <utils/KeyedVector.h> +#include <GLES2/gl2.h> +#include <utils/LruCache.h> + +#include <androidfw/ResourceTypes.h> + +#include "AssetAtlas.h" #include "Debug.h" #include "Patch.h" @@ -40,45 +45,47 @@ namespace uirenderer { // Cache /////////////////////////////////////////////////////////////////////////////// +class Caches; + class PatchCache { public: PatchCache(); - PatchCache(uint32_t maxCapacity); ~PatchCache(); + void init(Caches& caches); - Patch* get(const uint32_t bitmapWidth, const uint32_t bitmapHeight, - const float pixelWidth, const float pixelHeight, - const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors, - const uint32_t width, const uint32_t height, const int8_t numColors); + const Patch* get(const AssetAtlas::Entry* entry, + const uint32_t bitmapWidth, const uint32_t bitmapHeight, + const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch); void clear(); uint32_t getSize() const { - return mCache.size(); + return mSize; } uint32_t getMaxSize() const { - return mMaxEntries; + return mMaxSize; + } + + GLuint getMeshBuffer() const { + return mMeshBuffer; } private: - /** - * Description of a patch. - */ + void clearCache(); + struct PatchDescription { - PatchDescription(): bitmapWidth(0), bitmapHeight(0), pixelWidth(0), pixelHeight(0), - xCount(0), yCount(0), emptyCount(0), colorKey(0) { + PatchDescription(): mPatch(NULL), mBitmapWidth(0), mBitmapHeight(0), + mPixelWidth(0), mPixelHeight(0) { } PatchDescription(const uint32_t bitmapWidth, const uint32_t bitmapHeight, - const float pixelWidth, const float pixelHeight, - const uint32_t xCount, const uint32_t yCount, - const int8_t emptyCount, const uint32_t colorKey): - bitmapWidth(bitmapWidth), bitmapHeight(bitmapHeight), - pixelWidth(pixelWidth), pixelHeight(pixelHeight), - xCount(xCount), yCount(yCount), - emptyCount(emptyCount), colorKey(colorKey) { + const float pixelWidth, const float pixelHeight, const Res_png_9patch* patch): + mPatch(patch), mBitmapWidth(bitmapWidth), mBitmapHeight(bitmapHeight), + mPixelWidth(pixelWidth), mPixelHeight(pixelHeight) { } + hash_t hash() const; + static int compare(const PatchDescription& lhs, const PatchDescription& rhs); bool operator==(const PatchDescription& other) const { @@ -99,21 +106,24 @@ private: return PatchDescription::compare(lhs, rhs); } + friend inline hash_t hash_type(const PatchDescription& entry) { + return entry.hash(); + } + private: - uint32_t bitmapWidth; - uint32_t bitmapHeight; - float pixelWidth; - float pixelHeight; - uint32_t xCount; - uint32_t yCount; - int8_t emptyCount; - uint32_t colorKey; + const Res_png_9patch* mPatch; + uint32_t mBitmapWidth; + uint32_t mBitmapHeight; + float mPixelWidth; + float mPixelHeight; }; // struct PatchDescription - uint32_t mMaxEntries; - KeyedVector<PatchDescription, Patch*> mCache; + uint32_t mMaxSize; + uint32_t mSize; + LruCache<PatchDescription, Patch*> mCache; + GLuint mMeshBuffer; }; // class PatchCache }; // namespace uirenderer diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp index 14a2376..c127d68 100644 --- a/libs/hwui/Program.cpp +++ b/libs/hwui/Program.cpp @@ -15,6 +15,9 @@ */ #define LOG_TAG "OpenGLRenderer" +#define ATRACE_TAG ATRACE_TAG_VIEW + +#include <utils/Trace.h> #include "Program.h" @@ -25,7 +28,6 @@ namespace uirenderer { // Base program /////////////////////////////////////////////////////////////////////////////// -// TODO: Program instance should be created from a factory method Program::Program(const ProgramDescription& description, const char* vertex, const char* fragment) { mInitialized = false; mHasColorUniform = false; @@ -50,7 +52,9 @@ Program::Program(const ProgramDescription& description, const char* vertex, cons texCoords = -1; } + ATRACE_BEGIN("linkProgram"); glLinkProgram(mProgramId); + ATRACE_END(); GLint status; glGetProgramiv(mProgramId, GL_LINK_STATUS, &status); @@ -87,6 +91,9 @@ Program::Program(const ProgramDescription& description, const char* vertex, cons Program::~Program() { if (mInitialized) { + // This would ideally happen after linking the program + // but Tegra drivers, especially when perfhud is enabled, + // sometimes crash if we do so glDetachShader(mProgramId, mVertexShader); glDetachShader(mProgramId, mFragmentShader); @@ -132,6 +139,8 @@ int Program::getUniform(const char* name) { } GLuint Program::buildShader(const char* source, GLenum type) { + ATRACE_CALL(); + GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &source, 0); glCompileShader(shader); @@ -153,20 +162,24 @@ GLuint Program::buildShader(const char* source, GLenum type) { void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix, const mat4& transformMatrix, bool offset) { - mat4 p(projectionMatrix); - if (offset) { - // offset screenspace xy by an amount that compensates for typical precision - // issues in GPU hardware that tends to paint hor/vert lines in pixels shifted - // up and to the left. - // This offset value is based on an assumption that some hardware may use as - // little as 12.4 precision, so we offset by slightly more than 1/16. - p.translate(.065, .065, 0); + if (projectionMatrix != mProjection) { + if (CC_LIKELY(!offset)) { + glUniformMatrix4fv(projection, 1, GL_FALSE, &projectionMatrix.data[0]); + } else { + mat4 p(projectionMatrix); + // offset screenspace xy by an amount that compensates for typical precision + // issues in GPU hardware that tends to paint hor/vert lines in pixels shifted + // up and to the left. + // This offset value is based on an assumption that some hardware may use as + // little as 12.4 precision, so we offset by slightly more than 1/16. + p.translate(.065, .065, 0); + glUniformMatrix4fv(projection, 1, GL_FALSE, &p.data[0]); + } + mProjection = projectionMatrix; } mat4 t(transformMatrix); t.multiply(modelViewMatrix); - - glUniformMatrix4fv(projection, 1, GL_FALSE, &p.data[0]); glUniformMatrix4fv(transform, 1, GL_FALSE, &t.data[0]); } diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index e8b6d47..dd1aaa2 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -85,6 +85,7 @@ namespace uirenderer { #define PROGRAM_HAS_COLORS 42 #define PROGRAM_HAS_DEBUG_HIGHLIGHT 43 +#define PROGRAM_EMULATE_STENCIL 44 /////////////////////////////////////////////////////////////////////////////// // Types @@ -163,6 +164,7 @@ struct ProgramDescription { float gamma; bool hasDebugHighlight; + bool emulateStencil; /** * Resets this description. All fields are reset back to the default @@ -275,6 +277,7 @@ struct ProgramDescription { if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT; if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS; if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT; + if (emulateStencil) key |= programid(0x1) << PROGRAM_EMULATE_STENCIL; return key; } @@ -430,10 +433,13 @@ private: bool mUse; bool mInitialized; + // Uniforms caching bool mHasColorUniform; int mColorUniform; bool mHasSampler; + + mat4 mProjection; }; // class Program }; // namespace uirenderer diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 8eb85e5..367294c 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -342,6 +342,12 @@ const char* gFS_Main_ApplyColorOp[4] = { }; const char* gFS_Main_DebugHighlight = " gl_FragColor.rgb = vec3(0.0, gl_FragColor.a, 0.0);\n"; +const char* gFS_Main_EmulateStencil = + " gl_FragColor.rgba = vec4(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 1.0);\n" + " return;\n" + " /*\n"; +const char* gFS_Footer_EmulateStencil = + " */\n"; const char* gFS_Footer = "}\n\n"; @@ -603,7 +609,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti // Optimization for common cases if (!description.isAA && !blendFramebuffer && !description.hasColors && description.colorOp == ProgramDescription::kColorNone && - !description.isPoint && !description.hasDebugHighlight) { + !description.isPoint && !description.hasDebugHighlight && + !description.emulateStencil) { bool fast = false; const bool noShader = !description.hasGradient && !description.hasBitmap; @@ -683,6 +690,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti // Begin the shader shader.append(gFS_Main); { + if (description.emulateStencil) { + shader.append(gFS_Main_EmulateStencil); + } // Stores the result in fragColor directly if (description.hasTexture || description.hasExternalTexture) { if (description.hasAlpha8Texture) { @@ -757,6 +767,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gFS_Main_DebugHighlight); } } + if (description.emulateStencil) { + shader.append(gFS_Footer_EmulateStencil); + } // End the shader shader.append(gFS_Footer); diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 6eea00c..7c68b5b 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -71,9 +71,9 @@ enum DebugLevel { /** * Used to enable/disable overdraw debugging. The accepted values are - * "true" and "false". The default value is "false". + * "show", "count" and "false". The default value is "false". */ -#define PROPERTY_DEBUG_OVERDRAW "debug.hwui.show_overdraw" +#define PROPERTY_DEBUG_OVERDRAW "debug.hwui.overdraw" /** * Used to enable/disable non-rectangular clipping debugging. @@ -133,6 +133,7 @@ enum DebugLevel { #define PROPERTY_RENDER_BUFFER_CACHE_SIZE "ro.hwui.r_buffer_cache_size" #define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size" #define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size" +#define PROPERTY_PATCH_CACHE_SIZE "ro.hwui.patch_cache_size" #define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size" #define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size" @@ -178,7 +179,7 @@ enum DebugLevel { #define DEFAULT_LAYER_CACHE_SIZE 16.0f #define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f #define DEFAULT_PATH_CACHE_SIZE 10.0f -#define DEFAULT_PATCH_CACHE_SIZE 512 +#define DEFAULT_PATCH_CACHE_SIZE 128 // in kB #define DEFAULT_GRADIENT_CACHE_SIZE 0.5f #define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f #define DEFAULT_FBO_CACHE_SIZE 16 @@ -195,6 +196,8 @@ enum DebugLevel { // Converts a number of mega-bytes into bytes #define MB(s) s * 1024 * 1024 +// Converts a number of kilo-bytes into bytes +#define KB(s) s * 1024 static DebugLevel readDebugLevel() { char property[PROPERTY_VALUE_MAX]; diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index 8d88bdc..dd39cae 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -22,6 +22,8 @@ namespace android { namespace uirenderer { +class UvMapper; + /** * Represents an OpenGL texture. */ @@ -42,6 +44,8 @@ struct Texture { firstWrap = true; id = 0; + + uvMapper = NULL; } void setWrap(GLenum wrap, bool bindTexture = false, bool force = false, @@ -125,6 +129,11 @@ struct Texture { */ bool mipMap; + /** + * Optional, pointer to a texture coordinates mapper. + */ + const UvMapper* uvMapper; + private: /** * Last wrap modes set on this texture. Defaults to GL_CLAMP_TO_EDGE. diff --git a/libs/hwui/UvMapper.h b/libs/hwui/UvMapper.h new file mode 100644 index 0000000..70428d2 --- /dev/null +++ b/libs/hwui/UvMapper.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_HWUI_UV_MAPPER_H +#define ANDROID_HWUI_UV_MAPPER_H + +#include "Rect.h" + +namespace android { +namespace uirenderer { + +/** + * This class can be used to map UV coordinates from the [0..1] + * range to other arbitrary ranges. All the methods below assume + * that the input values lie in the [0..1] range already. + */ +class UvMapper { +public: + /** + * Using this constructor is equivalent to not using any mapping at all. + * UV coordinates in the [0..1] range remain in the [0..1] range. + */ + UvMapper(): mIdentity(true), mMinU(0.0f), mMaxU(1.0f), mMinV(0.0f), mMaxV(1.0f) { + } + + /** + * Creates a new mapper with the specified ranges for U and V coordinates. + * The parameter minU must be < maxU and minV must be < maxV. + */ + UvMapper(float minU, float maxU, float minV, float maxV): + mMinU(minU), mMaxU(maxU), mMinV(minV), mMaxV(maxV) { + checkIdentity(); + } + + /** + * Returns true if calling the map*() methods has no effect (that is, + * texture coordinates remain in the 0..1 range.) + */ + bool isIdentity() const { + return mIdentity; + } + + /** + * Changes the U and V mapping ranges. + * The parameter minU must be < maxU and minV must be < maxV. + */ + void setMapping(float minU, float maxU, float minV, float maxV) { + mMinU = minU; + mMaxU = maxU; + mMinV = minV; + mMaxV = maxV; + checkIdentity(); + } + + /** + * Maps a single value in the U range. + */ + void mapU(float& u) const { + if (!mIdentity) u = lerp(mMinU, mMaxU, u); + } + + /** + * Maps a single value in the V range. + */ + void mapV(float& v) const { + if (!mIdentity) v = lerp(mMinV, mMaxV, v); + } + + /** + * Maps the specified rectangle in place. This method assumes: + * - left = min. U + * - top = min. V + * - right = max. U + * - bottom = max. V + */ + void map(Rect& texCoords) const { + if (!mIdentity) { + texCoords.left = lerp(mMinU, mMaxU, texCoords.left); + texCoords.right = lerp(mMinU, mMaxU, texCoords.right); + texCoords.top = lerp(mMinV, mMaxV, texCoords.top); + texCoords.bottom = lerp(mMinV, mMaxV, texCoords.bottom); + } + } + + /** + * Maps the specified UV coordinates in place. + */ + void map(float& u1, float& v1, float& u2, float& v2) const { + if (!mIdentity) { + u1 = lerp(mMinU, mMaxU, u1); + u2 = lerp(mMinU, mMaxU, u2); + v1 = lerp(mMinV, mMaxV, v1); + v2 = lerp(mMinV, mMaxV, v2); + } + } + + void dump() const { + ALOGD("mapper[minU=%.2f maxU=%.2f minV=%.2f maxV=%.2f]", mMinU, mMaxU, mMinV, mMaxV); + } + +private: + static float lerp(float start, float stop, float amount) { + return start + (stop - start) * amount; + } + + void checkIdentity() { + mIdentity = mMinU == 0.0f && mMaxU == 1.0f && mMinV == 0.0f && mMaxV == 1.0f; + } + + bool mIdentity; + float mMinU; + float mMaxU; + float mMinV; + float mMaxV; +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_UV_MAPPER_H diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h index 477314b..f2a216f 100644 --- a/libs/hwui/thread/TaskManager.h +++ b/libs/hwui/thread/TaskManager.h @@ -43,7 +43,7 @@ public: /** * Returns true if this task manager can run tasks, * false otherwise. This method will typically return - * true on a single CPU core device. + * false on a single CPU core device. */ bool canRunTasks() const; |
