summaryrefslogtreecommitdiffstats
path: root/libs/hwui
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui')
-rw-r--r--libs/hwui/Android.mk20
-rw-r--r--libs/hwui/AssetAtlas.cpp106
-rw-r--r--libs/hwui/AssetAtlas.h169
-rw-r--r--libs/hwui/Caches.cpp70
-rw-r--r--libs/hwui/Caches.h18
-rw-r--r--libs/hwui/Debug.h2
-rw-r--r--libs/hwui/DisplayListOp.h58
-rw-r--r--libs/hwui/DisplayListRenderer.cpp17
-rw-r--r--libs/hwui/DisplayListRenderer.h3
-rw-r--r--libs/hwui/Extensions.h6
-rw-r--r--libs/hwui/FontRenderer.cpp68
-rw-r--r--libs/hwui/FontRenderer.h5
-rw-r--r--libs/hwui/Image.cpp62
-rw-r--r--libs/hwui/Image.h67
-rw-r--r--libs/hwui/OpenGLRenderer.cpp198
-rw-r--r--libs/hwui/OpenGLRenderer.h49
-rw-r--r--libs/hwui/Patch.cpp181
-rw-r--r--libs/hwui/Patch.h60
-rw-r--r--libs/hwui/PatchCache.cpp149
-rw-r--r--libs/hwui/PatchCache.h70
-rw-r--r--libs/hwui/Program.cpp35
-rw-r--r--libs/hwui/Program.h6
-rw-r--r--libs/hwui/ProgramCache.cpp15
-rw-r--r--libs/hwui/Properties.h9
-rw-r--r--libs/hwui/Texture.h9
-rw-r--r--libs/hwui/UvMapper.h133
-rw-r--r--libs/hwui/thread/TaskManager.h2
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;