summaryrefslogtreecommitdiffstats
path: root/libs/hwui
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui')
-rw-r--r--libs/hwui/Android.mk21
-rw-r--r--libs/hwui/AssetAtlas.cpp137
-rw-r--r--libs/hwui/AssetAtlas.h173
-rw-r--r--libs/hwui/Caches.cpp128
-rw-r--r--libs/hwui/Caches.h52
-rw-r--r--libs/hwui/Debug.h2
-rw-r--r--libs/hwui/DeferredDisplayList.cpp13
-rw-r--r--libs/hwui/DisplayList.cpp4
-rw-r--r--libs/hwui/DisplayList.h7
-rw-r--r--libs/hwui/DisplayListOp.h118
-rw-r--r--libs/hwui/DisplayListRenderer.cpp24
-rw-r--r--libs/hwui/DisplayListRenderer.h6
-rw-r--r--libs/hwui/Dither.cpp13
-rw-r--r--libs/hwui/Dither.h7
-rw-r--r--libs/hwui/Extensions.cpp80
-rw-r--r--libs/hwui/Extensions.h26
-rw-r--r--libs/hwui/FontRenderer.cpp72
-rw-r--r--libs/hwui/FontRenderer.h5
-rw-r--r--libs/hwui/GradientCache.cpp6
-rw-r--r--libs/hwui/Image.cpp63
-rw-r--r--libs/hwui/Image.h67
-rw-r--r--libs/hwui/Layer.cpp59
-rw-r--r--libs/hwui/Layer.h46
-rw-r--r--libs/hwui/LayerRenderer.cpp6
-rw-r--r--libs/hwui/Matrix.h25
-rw-r--r--libs/hwui/OpenGLRenderer.cpp338
-rw-r--r--libs/hwui/OpenGLRenderer.h66
-rw-r--r--libs/hwui/Patch.cpp181
-rw-r--r--libs/hwui/Patch.h60
-rw-r--r--libs/hwui/PatchCache.cpp164
-rw-r--r--libs/hwui/PatchCache.h78
-rw-r--r--libs/hwui/PathCache.cpp6
-rw-r--r--libs/hwui/PathCache.h2
-rw-r--r--libs/hwui/PathTessellator.cpp145
-rw-r--r--libs/hwui/PathTessellator.h35
-rw-r--r--libs/hwui/PixelBuffer.cpp10
-rw-r--r--libs/hwui/Program.cpp35
-rw-r--r--libs/hwui/Program.h31
-rw-r--r--libs/hwui/ProgramCache.cpp53
-rw-r--r--libs/hwui/Properties.h13
-rw-r--r--libs/hwui/Rect.h4
-rw-r--r--libs/hwui/SkiaShader.cpp22
-rw-r--r--libs/hwui/SkiaShader.h19
-rw-r--r--libs/hwui/TextDropShadowCache.cpp9
-rw-r--r--libs/hwui/TextDropShadowCache.h4
-rw-r--r--libs/hwui/Texture.cpp84
-rw-r--r--libs/hwui/Texture.h93
-rw-r--r--libs/hwui/TextureCache.cpp8
-rw-r--r--libs/hwui/UvMapper.h133
-rw-r--r--libs/hwui/Vertex.h17
-rw-r--r--libs/hwui/font/CacheTexture.cpp10
-rw-r--r--libs/hwui/font/CacheTexture.h4
-rw-r--r--libs/hwui/thread/TaskManager.h2
53 files changed, 1883 insertions, 903 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index a630ea1..5a30472 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 \
@@ -39,6 +41,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
SkiaShader.cpp \
Snapshot.cpp \
Stencil.cpp \
+ Texture.cpp \
TextureCache.cpp \
TextDropShadowCache.cpp
@@ -52,17 +55,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..782c052
--- /dev/null
+++ b/libs/hwui/AssetAtlas.cpp
@@ -0,0 +1,137 @@
+/*
+ * 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 "Caches.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);
+
+ if (mImage->getTexture()) {
+ Caches& caches = Caches::getInstance();
+
+ mTexture = new Texture(caches);
+ mTexture->id = mImage->getTexture();
+ mTexture->width = buffer->getWidth();
+ mTexture->height = buffer->getHeight();
+
+ createEntries(caches, map, count);
+ } else {
+ ALOGW("Could not create atlas image");
+
+ delete mImage;
+ mImage = NULL;
+ mTexture = NULL;
+ }
+}
+
+void AssetAtlas::terminate() {
+ if (mImage) {
+ delete mImage;
+ mImage = NULL;
+
+ delete mTexture;
+ mTexture = NULL;
+
+ for (size_t i = 0; i < mEntries.size(); i++) {
+ delete mEntries.valueAt(i);
+ }
+ mEntries.clear();
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// 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;
+}
+
+/**
+ * Delegates changes to wrapping and filtering to the base atlas texture
+ * instead of applying the changes to the virtual textures.
+ */
+struct DelegateTexture: public Texture {
+ DelegateTexture(Caches& caches, Texture* delegate): Texture(caches), mDelegate(delegate) { }
+
+ virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false,
+ bool force = false, GLenum renderTarget = GL_TEXTURE_2D) {
+ mDelegate->setWrapST(wrapS, wrapT, bindTexture, force, renderTarget);
+ }
+
+ virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false,
+ bool force = false, GLenum renderTarget = GL_TEXTURE_2D) {
+ mDelegate->setFilterMinMag(min, mag, bindTexture, force, renderTarget);
+ }
+private:
+ Texture* const mDelegate;
+}; // struct DelegateTexture
+
+/**
+ * TODO: This method does not take the rotation flag into account
+ */
+void AssetAtlas::createEntries(Caches& caches, int* map, int count) {
+ const float width = float(mTexture->width);
+ const float height = float(mTexture->height);
+
+ 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 / width, (x + bitmap->width()) / width,
+ y / height, (y + bitmap->height()) / height);
+
+ Texture* texture = new DelegateTexture(caches, mTexture);
+ Entry* entry = new Entry(bitmap, x, y, rotated, texture, mapper, *this);
+
+ texture->id = mTexture->id;
+ texture->blend = !bitmap->isOpaque();
+ texture->width = bitmap->width();
+ texture->height = bitmap->height();
+ 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..2624907
--- /dev/null
+++ b/libs/hwui/AssetAtlas.h
@@ -0,0 +1,173 @@
+/*
+ * 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 {
+
+class Caches;
+
+/**
+ * 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;
+
+ /*
+ * A "virtual texture" object that represents the texture
+ * this entry belongs to. This texture should never be
+ * modified.
+ */
+ Texture* texture;
+
+ /**
+ * 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;
+
+ private:
+ Entry(SkBitmap* bitmap, int x, int y, bool rotated,
+ Texture* texture, const UvMapper& mapper, const AssetAtlas& atlas):
+ bitmap(bitmap), x(x), y(y), rotated(rotated),
+ texture(texture), uvMapper(mapper), atlas(atlas) { }
+
+ ~Entry() {
+ delete texture;
+ }
+
+ friend class AssetAtlas;
+ };
+
+ AssetAtlas(): mTexture(NULL), 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 mTexture ? mTexture->width : 0;
+ }
+
+ /**
+ * Returns the height of this atlas in pixels.
+ * Can return 0 if the atlas is not initialized.
+ */
+ uint32_t getHeight() const {
+ return mTexture ? mTexture->height : 0;
+ }
+
+ /**
+ * Returns the OpenGL name of the texture backing this atlas.
+ * Can return 0 if the atlas is not initialized.
+ */
+ GLuint getTexture() const {
+ return mTexture ? mTexture->id : 0;
+ }
+
+ /**
+ * 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(Caches& caches, int* map, int count);
+
+ Texture* 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..74aeddb 100644
--- a/libs/hwui/Caches.cpp
+++ b/libs/hwui/Caches.cpp
@@ -47,19 +47,21 @@ namespace uirenderer {
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-Caches::Caches(): Singleton<Caches>(), mExtensions(Extensions::getInstance()), mInitialized(false) {
+Caches::Caches(): Singleton<Caches>(),
+ mExtensions(Extensions::getInstance()), mInitialized(false) {
init();
initFont();
initConstraints();
initProperties();
+ initStaticProperties();
initExtensions();
mDebugLevel = readDebugLevel();
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 +84,7 @@ void Caches::init() {
mTextureUnit = 0;
mRegionMesh = NULL;
+ mMeshIndices = 0;
blend = false;
lastSrcMode = GL_ZERO;
@@ -94,7 +97,13 @@ void Caches::init() {
debugOverdraw = false;
debugStencilClip = kStencilHide;
+ patchCache.init(*this);
+
mInitialized = true;
+
+ resetBoundTextures();
+
+ return true;
}
void Caches::initFont() {
@@ -132,6 +141,18 @@ void Caches::initConstraints() {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
}
+void Caches::initStaticProperties() {
+ gpuPixelBuffersEnabled = false;
+
+ // OpenGL ES 3.0+ specific features
+ if (mExtensions.getMajorGlVersion() >= 3) {
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, property, "true") > 0) {
+ gpuPixelBuffersEnabled = !strcmp(property, "true");
+ }
+ }
+}
+
bool Caches::initProperties() {
bool prevDebugLayersUpdates = debugLayersUpdates;
bool prevDebugOverdraw = debugOverdraw;
@@ -147,7 +168,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 +212,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 +222,10 @@ void Caches::terminate() {
programCache.clear();
currentProgram = NULL;
+ assetAtlas.terminate();
+
+ patchCache.clear();
+
mInitialized = false;
}
@@ -227,6 +253,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 +262,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 +270,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 +384,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);
@@ -441,6 +494,46 @@ void Caches::activeTexture(GLuint textureUnit) {
}
}
+void Caches::bindTexture(GLuint texture) {
+ if (mBoundTextures[mTextureUnit] != texture) {
+ glBindTexture(GL_TEXTURE_2D, texture);
+ mBoundTextures[mTextureUnit] = texture;
+ }
+}
+
+void Caches::bindTexture(GLenum target, GLuint texture) {
+ if (mBoundTextures[mTextureUnit] != texture) {
+ glBindTexture(target, texture);
+ mBoundTextures[mTextureUnit] = texture;
+ }
+}
+
+void Caches::deleteTexture(GLuint texture) {
+ // When glDeleteTextures() is called on a currently bound texture,
+ // OpenGL ES specifies that the texture is then considered unbound
+ // Consider the following series of calls:
+ //
+ // glGenTextures -> creates texture name 2
+ // glBindTexture(2)
+ // glDeleteTextures(2) -> 2 is now unbound
+ // glGenTextures -> can return 2 again
+ //
+ // If we don't call glBindTexture(2) after the second glGenTextures
+ // call, any texture operation will be performed on the default
+ // texture (name=0)
+
+ for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) {
+ if (mBoundTextures[i] == texture) {
+ mBoundTextures[i] = 0;
+ }
+ }
+ glDeleteTextures(1, &texture);
+}
+
+void Caches::resetBoundTextures() {
+ memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint));
+}
+
///////////////////////////////////////////////////////////////////////////////
// Scissor
///////////////////////////////////////////////////////////////////////////////
@@ -546,27 +639,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..bdde8fb 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"
@@ -50,6 +55,7 @@ namespace uirenderer {
// Globals
///////////////////////////////////////////////////////////////////////////////
+// GL ES 2.0 defines that at least 16 texture units must be supported
#define REQUIRED_TEXTURE_UNITS_COUNT 3
#define REGION_MESH_QUAD_COUNT 512
@@ -74,6 +80,7 @@ static const GLsizei gVertexAAWidthOffset = 2 * sizeof(float);
static const GLsizei gVertexAALengthOffset = 3 * sizeof(float);
static const GLsizei gMeshCount = 4;
+// Must define as many texture units as specified by REQUIRED_TEXTURE_UNITS_COUNT
static const GLenum gTextureUnits[] = {
GL_TEXTURE0,
GL_TEXTURE1,
@@ -113,7 +120,7 @@ public:
/**
* Initialize caches.
*/
- void init();
+ bool init();
/**
* Initialize global system properties.
@@ -172,6 +179,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();
@@ -213,6 +225,33 @@ public:
void activeTexture(GLuint textureUnit);
/**
+ * Binds the specified texture as a GL_TEXTURE_2D texture.
+ * All texture bindings must be performed with this method or
+ * bindTexture(GLenum, GLuint).
+ */
+ void bindTexture(GLuint texture);
+
+ /**
+ * Binds the specified texture with the specified render target.
+ * All texture bindings must be performed with this method or
+ * bindTexture(GLuint).
+ */
+ void bindTexture(GLenum target, GLuint texture);
+
+ /**
+ * Deletes the specified texture and clears it from the cache
+ * of bound textures.
+ * All textures must be deleted using this method.
+ */
+ void deleteTexture(GLuint texture);
+
+ /**
+ * Signals that the cache of bound textures should be cleared.
+ * Other users of the context may have altered which textures are bound.
+ */
+ void resetBoundTextures();
+
+ /**
* Sets the scissor for the current surface.
*/
bool setScissor(GLint x, GLint y, GLint width, GLint height);
@@ -290,6 +329,10 @@ public:
Dither dither;
Stencil stencil;
+ AssetAtlas assetAtlas;
+
+ bool gpuPixelBuffersEnabled;
+
// Debug methods
PFNGLINSERTEVENTMARKEREXTPROC eventMark;
PFNGLPUSHGROUPMARKEREXTPROC startMark;
@@ -302,6 +345,7 @@ private:
void initFont();
void initExtensions();
void initConstraints();
+ void initStaticProperties();
static void eventMarkNull(GLsizei length, const GLchar* marker) { }
static void startMarkNull(GLsizei length, const GLchar* marker) { }
@@ -336,7 +380,9 @@ private:
// Used to render layers
TextureVertex* mRegionMesh;
- GLuint mRegionMeshIndices;
+
+ // Global index buffer
+ GLuint mMeshIndices;
mutable Mutex mGarbageLock;
Vector<Layer*> mLayerGarbage;
@@ -346,6 +392,8 @@ private:
bool mInitialized;
uint32_t mFunctorsCount;
+
+ GLuint mBoundTextures[REQUIRED_TEXTURE_UNITS_COUNT];
}; // class Caches
}; // namespace uirenderer
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/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 9323a3a..512d3b1 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -85,8 +85,8 @@ public:
}
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
- DEFER_LOGD("%d replaying DrawingBatch %p, with %d ops (batch id %x, merge id %p)",
- index, this, mOps.size(), mOps[0]->getBatchId(), mOps[0]->getMergeId());
+ DEFER_LOGD("%d replaying DrawBatch %p, with %d ops (batch id %x, merge id %p)",
+ index, this, mOps.size(), getBatchId(), getMergeId());
status_t status = DrawGlInfo::kStatusDone;
DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
@@ -145,7 +145,10 @@ public:
* dropped, so we make simplifying qualifications on the ops that can merge, per op type.
*/
bool canMergeWith(DrawOp* op) {
- if (!op->state.mMatrix.isPureTranslate()) return false;
+ if (getBatchId() == DeferredDisplayList::kOpBatch_Bitmap) {
+ // Bitmap batches can handle translate and scaling
+ if (!op->state.mMatrix.isSimple()) return false;
+ } else if (!op->state.mMatrix.isPureTranslate()) return false;
bool isTextBatch = getBatchId() == DeferredDisplayList::kOpBatch_Text ||
getBatchId() == DeferredDisplayList::kOpBatch_ColorText;
@@ -193,10 +196,10 @@ public:
}
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
- DEFER_LOGD("%d replaying DrawingBatch %p, with %d ops (batch id %x, merge id %p)",
+ DEFER_LOGD("%d replaying MergingDrawBatch %p, with %d ops (batch id %x, merge id %p)",
index, this, mOps.size(), getBatchId(), getMergeId());
if (mOps.size() == 1) {
- return DrawBatch::replay(renderer, dirty, false);
+ return DrawBatch::replay(renderer, dirty, 0);
}
DrawOp* op = mOps[0];
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 1cbd531..b2d9915 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -514,6 +514,10 @@ void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level)
for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
DisplayListOp *op = mDisplayListData->displayListOps[i];
+#if DEBUG_DISPLAY_LIST
+ op->output(level + 1);
+#endif
+
logBuffer.writeCommand(level, op->name());
handler(op, saveCount, mClipToBounds);
}
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 5f84329..97c34dc 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -129,7 +129,12 @@ public:
void setName(const char* name) {
if (name) {
- mName.setTo(name);
+ char* lastPeriod = strrchr(name, '.');
+ if (lastPeriod) {
+ mName.setTo(lastPeriod + 1);
+ } else {
+ mName.setTo(name);
+ }
}
}
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index a0290e3..028decd 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 { \
@@ -218,6 +220,9 @@ public:
DrawBoundedOp(float left, float top, float right, float bottom, SkPaint* paint)
: DrawOp(paint), mLocalBounds(left, top, right, bottom) {}
+ DrawBoundedOp(const Rect& localBounds, SkPaint* paint)
+ : DrawOp(paint), mLocalBounds(localBounds) {}
+
// Calculates bounds as smallest rect encompassing all points
// NOTE: requires at least 1 vertex, and doesn't account for stroke size (should be handled in
// subclass' constructor)
@@ -721,7 +726,6 @@ private:
int mSetBits;
};
-
///////////////////////////////////////////////////////////////////////////////
// DRAW OPERATIONS - these are operations that can draw to the canvas's device
///////////////////////////////////////////////////////////////////////////////
@@ -729,9 +733,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 +760,20 @@ 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);
+ bool transformed = false;
- 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;
+ // When we reach multiDraw(), the matrix can be either
+ // pureTranslate or simple (translate and/or scale).
+ // If the matrix is not pureTranslate, then we have a scale
+ if (!ops[i]->state.mMatrix.isPureTranslate()) transformed = true;
+
+ 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);
@@ -766,7 +783,8 @@ public:
SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, bottom);
}
- return renderer.drawBitmaps(mBitmap, ops.size(), &vertices[0], bounds, mPaint);
+ return renderer.drawBitmaps(mBitmap, ops.size(), &vertices[0],
+ transformed, bounds, mPaint);
}
virtual void output(int level, uint32_t logFlags) {
@@ -775,18 +793,25 @@ public:
virtual const char* name() { return "DrawBitmap"; }
+ bool bitmapMergeAllowed() {
+ return state.mMatrix.isSimple() && !state.mClipped &&
+ OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+ }
+
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
- return mergeAllowed() && (mBitmap->getConfig() != SkBitmap::kA8_Config);
+ return bitmapMergeAllowed() && (mBitmap->getConfig() != SkBitmap::kA8_Config);
}
const SkBitmap* bitmap() { return mBitmap; }
protected:
SkBitmap* mBitmap;
+ const AssetAtlas::Entry* mAtlasEntry;
+ UvMapper mUvMapper;
};
class DrawBitmapMatrixOp : public DrawBoundedOp {
@@ -904,20 +929,25 @@ 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),
+ mGenerationId(0), mMesh(NULL) {
+ 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,
+ if (!mMesh || renderer.getCaches().patchCache.getGenerationId() != mGenerationId) {
+ PatchCache& cache = renderer.getCaches().patchCache;
+ mMesh = cache.get(mEntry, mBitmap->width(), mBitmap->height(),
+ mLocalBounds.right - mLocalBounds.left, mLocalBounds.bottom - mLocalBounds.top,
+ mPatch);
+ mGenerationId = cache.getGenerationId();
+ }
+ // We're not calling the public variant of drawPatch() here
+ // This method won't perform the quickReject() since we've already done it at this point
+ return renderer.drawPatch(mBitmap, mMesh, mEntry, mLocalBounds.left, mLocalBounds.top,
mLocalBounds.right, mLocalBounds.bottom, mAlpha, mMode);
}
@@ -929,20 +959,20 @@ 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;
+
+ uint32_t mGenerationId;
+ const Patch* mMesh;
+ AssetAtlas::Entry* mEntry;
};
class DrawColorOp : public DrawOp {
@@ -1270,23 +1300,10 @@ private:
class DrawTextOp : public DrawBoundedOp {
public:
DrawTextOp(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float length)
- : DrawBoundedOp(paint), mText(text), mBytesCount(bytesCount), mCount(count),
- mX(x), mY(y), mPositions(positions), mLength(length) {
- // duplicates bounds calculation from OpenGLRenderer::drawText, but doesn't alter mX
- SkPaint::FontMetrics metrics;
- paint->getFontMetrics(&metrics, 0.0f);
- switch (paint->getTextAlign()) {
- case SkPaint::kCenter_Align:
- x -= length / 2.0f;
- break;
- case SkPaint::kRight_Align:
- x -= length;
- break;
- default:
- break;
- }
- mLocalBounds.set(x, mY + metrics.fTop, x + length, mY + metrics.fBottom);
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds)
+ : DrawBoundedOp(bounds, paint), mText(text), mBytesCount(bytesCount), mCount(count),
+ mX(x), mY(y), mPositions(positions), mTotalAdvance(totalAdvance) {
+ mLocalBounds.translate(x,y);
memset(&mPrecacheTransform.data[0], 0xff, 16 * sizeof(float));
}
@@ -1312,7 +1329,7 @@ public:
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
- mPositions, getPaint(renderer), mLength);
+ mPositions, getPaint(renderer), mTotalAdvance, mLocalBounds);
}
virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
@@ -1325,7 +1342,8 @@ public:
DrawTextOp& op = *((DrawTextOp*)ops[i]);
status |= renderer.drawText(op.mText, op.mBytesCount, op.mCount, op.mX, op.mY,
- op.mPositions, op.getPaint(renderer), op.mLength, drawOpMode);
+ op.mPositions, op.getPaint(renderer), op.mTotalAdvance, op.mLocalBounds,
+ drawOpMode);
}
return status;
}
@@ -1343,7 +1361,7 @@ private:
float mX;
float mY;
const float* mPositions;
- float mLength;
+ float mTotalAdvance;
mat4 mPrecacheTransform;
};
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 876c38a..6d85a16 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;
}
@@ -423,17 +420,16 @@ status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int
status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int count,
float x, float y, const float* positions, SkPaint* paint,
- float length, DrawOpMode drawOpMode) {
+ float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode) {
if (!text || count <= 0) return DrawGlInfo::kStatusDone;
- if (length < 0.0f) length = paint->measureText(text, bytesCount);
-
text = refText(text, bytesCount);
positions = refBuffer<float>(positions, count * 2);
paint = refPaint(paint);
- DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count, x, y, positions, paint, length);
+ DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count,
+ x, y, positions, paint, totalAdvance, bounds);
addDrawOp(op);
return DrawGlInfo::kStatusDone;
}
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 75abad6..85d6107 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);
@@ -122,7 +121,8 @@ public:
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float length, DrawOpMode drawOpMode);
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
+ DrawOpMode drawOpMode);
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
index 19b3849..649a7bc 100644
--- a/libs/hwui/Dither.cpp
+++ b/libs/hwui/Dither.cpp
@@ -24,12 +24,15 @@ namespace uirenderer {
// Lifecycle
///////////////////////////////////////////////////////////////////////////////
+Dither::Dither(): mCaches(NULL), mInitialized(false), mDitherTexture(0) {
+}
+
void Dither::bindDitherTexture() {
if (!mInitialized) {
bool useFloatTexture = Extensions::getInstance().getMajorGlVersion() >= 3;
glGenTextures(1, &mDitherTexture);
- glBindTexture(GL_TEXTURE_2D, mDitherTexture);
+ mCaches->bindTexture(mDitherTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
@@ -68,13 +71,13 @@ void Dither::bindDitherTexture() {
mInitialized = true;
} else {
- glBindTexture(GL_TEXTURE_2D, mDitherTexture);
+ mCaches->bindTexture(mDitherTexture);
}
}
void Dither::clear() {
if (mInitialized) {
- glDeleteTextures(1, &mDitherTexture);
+ mCaches->deleteTexture(mDitherTexture);
mInitialized = false;
}
}
@@ -84,8 +87,10 @@ void Dither::clear() {
///////////////////////////////////////////////////////////////////////////////
void Dither::setupProgram(Program* program, GLuint* textureUnit) {
+ if (!mCaches) mCaches = &Caches::getInstance();
+
GLuint textureSlot = (*textureUnit)++;
- Caches::getInstance().activeTexture(textureSlot);
+ mCaches->activeTexture(textureSlot);
bindDitherTexture();
diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h
index 4d1f921..546236b 100644
--- a/libs/hwui/Dither.h
+++ b/libs/hwui/Dither.h
@@ -24,9 +24,7 @@
namespace android {
namespace uirenderer {
-///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
+class Caches;
// Must be a power of two
#define DITHER_KERNEL_SIZE 4
@@ -39,7 +37,7 @@ namespace uirenderer {
*/
class Dither {
public:
- Dither(): mInitialized(false), mDitherTexture(0) { }
+ Dither();
void clear();
void setupProgram(Program* program, GLuint* textureUnit);
@@ -47,6 +45,7 @@ public:
private:
void bindDitherTexture();
+ Caches* mCaches;
bool mInitialized;
GLuint mDitherTexture;
};
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index 51aec8d..eefdb84 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -14,6 +14,16 @@
* limitations under the License.
*/
+#define LOG_TAG "OpenGLRenderer"
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include <utils/Log.h>
+
#include "Debug.h"
#include "Extensions.h"
@@ -40,33 +50,22 @@ namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
Extensions::Extensions(): Singleton<Extensions>() {
- const char* buffer = (const char*) glGetString(GL_EXTENSIONS);
- const char* current = buffer;
- const char* head = current;
- EXT_LOGD("Available GL extensions:");
- do {
- head = strchr(current, ' ');
- String8 s(current, head ? head - current : strlen(current));
- if (s.length()) {
- mExtensionList.add(s);
- EXT_LOGD(" %s", s.string());
- }
- current = head + 1;
- } while (head);
-
- mHasNPot = hasExtension("GL_OES_texture_npot");
- mHasFramebufferFetch = hasExtension("GL_NV_shader_framebuffer_fetch");
- mHasDiscardFramebuffer = hasExtension("GL_EXT_discard_framebuffer");
- mHasDebugMarker = hasExtension("GL_EXT_debug_marker");
- mHasDebugLabel = hasExtension("GL_EXT_debug_label");
- mHasTiledRendering = hasExtension("GL_QCOM_tiled_rendering");
- mHas1BitStencil = hasExtension("GL_OES_stencil1");
- mHas4BitStencil = hasExtension("GL_OES_stencil4");
-
- mExtensions = strdup(buffer);
+ // Query GL extensions
+ findExtensions((const char*) glGetString(GL_EXTENSIONS), mGlExtensionList);
+ mHasNPot = hasGlExtension("GL_OES_texture_npot");
+ mHasFramebufferFetch = hasGlExtension("GL_NV_shader_framebuffer_fetch");
+ mHasDiscardFramebuffer = hasGlExtension("GL_EXT_discard_framebuffer");
+ mHasDebugMarker = hasGlExtension("GL_EXT_debug_marker");
+ mHasDebugLabel = hasGlExtension("GL_EXT_debug_label");
+ mHasTiledRendering = hasGlExtension("GL_QCOM_tiled_rendering");
+ mHas1BitStencil = hasGlExtension("GL_OES_stencil1");
+ mHas4BitStencil = hasGlExtension("GL_OES_stencil4");
+
+ // Query EGL extensions
+ findExtensions(eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS), mEglExtensionList);
+ mHasNvSystemTime = hasEglExtension("EGL_NV_system_time");
const char* version = (const char*) glGetString(GL_VERSION);
- mVersion = strdup(version);
// Section 6.1.5 of the OpenGL ES specification indicates the GL version
// string strictly follows this format:
@@ -88,22 +87,41 @@ Extensions::Extensions(): Singleton<Extensions>() {
}
Extensions::~Extensions() {
- free(mExtensions);
- free(mVersion);
}
///////////////////////////////////////////////////////////////////////////////
// Methods
///////////////////////////////////////////////////////////////////////////////
-bool Extensions::hasExtension(const char* extension) const {
+bool Extensions::hasGlExtension(const char* extension) const {
const String8 s(extension);
- return mExtensionList.indexOf(s) >= 0;
+ return mGlExtensionList.indexOf(s) >= 0;
+}
+
+bool Extensions::hasEglExtension(const char* extension) const {
+ const String8 s(extension);
+ return mEglExtensionList.indexOf(s) >= 0;
+}
+
+void Extensions::findExtensions(const char* extensions, SortedVector<String8>& list) const {
+ const char* current = extensions;
+ const char* head = current;
+ EXT_LOGD("Available extensions:");
+ do {
+ head = strchr(current, ' ');
+ String8 s(current, head ? head - current : strlen(current));
+ if (s.length()) {
+ list.add(s);
+ EXT_LOGD(" %s", s.string());
+ }
+ current = head + 1;
+ } while (head);
}
void Extensions::dump() const {
- ALOGD("%s", mVersion);
- ALOGD("Supported extensions:\n%s", mExtensions);
+ ALOGD("%s", (const char*) glGetString(GL_VERSION));
+ ALOGD("Supported GL extensions:\n%s", (const char*) glGetString(GL_EXTENSIONS));
+ ALOGD("Supported EGL extensions:\n%s", eglQueryString(eglGetCurrentDisplay(), EGL_EXTENSIONS));
}
}; // namespace uirenderer
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index 54a3987..3112244 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -17,13 +17,12 @@
#ifndef ANDROID_HWUI_EXTENSIONS_H
#define ANDROID_HWUI_EXTENSIONS_H
+#include <cutils/compiler.h>
+
#include <utils/Singleton.h>
#include <utils/SortedVector.h>
#include <utils/String8.h>
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
namespace android {
namespace uirenderer {
@@ -31,11 +30,8 @@ namespace uirenderer {
// Classes
///////////////////////////////////////////////////////////////////////////////
-class Extensions: public Singleton<Extensions> {
+class ANDROID_API 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; }
@@ -44,21 +40,26 @@ public:
inline bool hasTiledRendering() const { return mHasTiledRendering; }
inline bool has1BitStencil() const { return mHas1BitStencil; }
inline bool has4BitStencil() const { return mHas4BitStencil; }
+ inline bool hasNvSystemTime() const { return mHasNvSystemTime; }
inline int getMajorGlVersion() const { return mVersionMajor; }
inline int getMinorGlVersion() const { return mVersionMinor; }
- bool hasExtension(const char* extension) const;
+ bool hasGlExtension(const char* extension) const;
+ bool hasEglExtension(const char* extension) const;
void dump() const;
private:
- friend class Singleton<Extensions>;
+ Extensions();
+ ~Extensions();
- SortedVector<String8> mExtensionList;
+ void findExtensions(const char* extensions, SortedVector<String8>& list) const;
+
+ friend class Singleton<Extensions>;
- char* mExtensions;
- char* mVersion;
+ SortedVector<String8> mGlExtensionList;
+ SortedVector<String8> mEglExtensionList;
bool mHasNPot;
bool mHasFramebufferFetch;
@@ -68,6 +69,7 @@ private:
bool mHasTiledRendering;
bool mHas1BitStencil;
bool mHas4BitStencil;
+ bool mHasNvSystemTime;
int mVersionMajor;
int mVersionMinor;
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 543cfa2..3e3d882 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"
@@ -374,7 +376,7 @@ void FontRenderer::checkTextureUpdate() {
if (cacheTexture->getTextureId() != lastTextureId) {
lastTextureId = cacheTexture->getTextureId();
caches.activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, lastTextureId);
+ caches.bindTexture(lastTextureId);
}
if (cacheTexture->upload()) {
@@ -427,7 +429,7 @@ void FontRenderer::issueDrawCommand() {
first = false;
}
- glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
+ caches.bindTexture(texture->getTextureId());
texture->setLinearFiltering(mLinearFiltering, false);
TextureVertex* mesh = texture->mesh();
@@ -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/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 507ed95..1815bff 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -120,7 +120,7 @@ void GradientCache::operator()(GradientCacheEntry& shader, Texture*& texture) {
const uint32_t size = texture->width * texture->height * bytesPerPixel();
mSize -= size;
- glDeleteTextures(1, &texture->id);
+ texture->deleteTexture();
delete texture;
}
}
@@ -173,7 +173,7 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient,
GradientInfo info;
getGradientInfo(colors, count, info);
- Texture* texture = new Texture;
+ Texture* texture = new Texture();
texture->width = info.width;
texture->height = 2;
texture->blend = info.hasAlpha;
@@ -286,7 +286,7 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions,
memcpy(pixels + rowBytes, pixels, rowBytes);
glGenTextures(1, &texture->id);
- glBindTexture(GL_TEXTURE_2D, texture->id);
+ Caches::getInstance().bindTexture(texture->id);
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
if (mUseFloatTexture) {
diff --git a/libs/hwui/Image.cpp b/libs/hwui/Image.cpp
new file mode 100644
index 0000000..edf3930
--- /dev/null
+++ b/libs/hwui/Image.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 "Caches.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);
+ Caches::getInstance().bindTexture(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;
+
+ Caches::getInstance().deleteTexture(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/Layer.cpp b/libs/hwui/Layer.cpp
index 4adad05..134f452 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -28,7 +28,8 @@
namespace android {
namespace uirenderer {
-Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight) {
+Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight):
+ caches(Caches::getInstance()), texture(caches) {
mesh = NULL;
meshIndices = NULL;
meshElementCount = 0;
@@ -47,11 +48,11 @@ Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight) {
debugDrawUpdate = false;
hasDrawnSinceUpdate = false;
deferredList = NULL;
- Caches::getInstance().resourceCache.incrementRefcount(this);
+ caches.resourceCache.incrementRefcount(this);
}
Layer::~Layer() {
- if (colorFilter) Caches::getInstance().resourceCache.decrementRefcount(colorFilter);
+ if (colorFilter) caches.resourceCache.decrementRefcount(colorFilter);
removeFbo();
deleteTexture();
@@ -76,7 +77,7 @@ bool Layer::resize(const uint32_t width, const uint32_t height) {
return true;
}
- const uint32_t maxTextureSize = Caches::getInstance().maxTextureSize;
+ const uint32_t maxTextureSize = caches.maxTextureSize;
if (desiredWidth > maxTextureSize || desiredHeight > maxTextureSize) {
ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)",
desiredWidth, desiredHeight, maxTextureSize, maxTextureSize);
@@ -89,7 +90,7 @@ bool Layer::resize(const uint32_t width, const uint32_t height) {
setSize(desiredWidth, desiredHeight);
if (fbo) {
- Caches::getInstance().activeTexture(0);
+ caches.activeTexture(0);
bindTexture();
allocateTexture();
@@ -120,14 +121,14 @@ void Layer::removeFbo(bool flush) {
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
- Caches::getInstance().renderBufferCache.put(stencil);
+ caches.renderBufferCache.put(stencil);
stencil = NULL;
}
if (fbo) {
if (flush) LayerRenderer::flushLayer(this);
// If put fails the cache will delete the FBO
- Caches::getInstance().fboCache.put(fbo);
+ caches.fboCache.put(fbo);
fbo = 0;
}
}
@@ -138,11 +139,51 @@ void Layer::setPaint(SkPaint* paint) {
void Layer::setColorFilter(SkiaColorFilter* filter) {
if (colorFilter) {
- Caches::getInstance().resourceCache.decrementRefcount(colorFilter);
+ caches.resourceCache.decrementRefcount(colorFilter);
}
colorFilter = filter;
if (colorFilter) {
- Caches::getInstance().resourceCache.incrementRefcount(colorFilter);
+ caches.resourceCache.incrementRefcount(colorFilter);
+ }
+}
+
+void Layer::bindTexture() const {
+ if (texture.id) {
+ caches.bindTexture(renderTarget, texture.id);
+ }
+}
+
+void Layer::bindStencilRenderBuffer() const {
+ if (stencil) {
+ stencil->bind();
+ }
+}
+
+void Layer::generateTexture() {
+ if (!texture.id) {
+ glGenTextures(1, &texture.id);
+ }
+}
+
+void Layer::deleteTexture() {
+ if (texture.id) {
+ texture.deleteTexture();
+ texture.id = 0;
+ }
+}
+
+void Layer::clearTexture() {
+ texture.id = 0;
+}
+
+void Layer::allocateTexture() {
+#if DEBUG_LAYERS
+ ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight());
+#endif
+ if (texture.id) {
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
+ glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, NULL);
}
}
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 7186603..326b25a 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -40,6 +40,7 @@ namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
// Forward declarations
+class Caches;
class OpenGLRenderer;
class DisplayList;
class DeferredDisplayList;
@@ -221,50 +222,19 @@ struct Layer {
ANDROID_API void setColorFilter(SkiaColorFilter* filter);
- inline void bindTexture() const {
- if (texture.id) {
- glBindTexture(renderTarget, texture.id);
- }
- }
-
- inline void bindStencilRenderBuffer() const {
- if (stencil) {
- stencil->bind();
- }
- }
+ void bindStencilRenderBuffer() const;
- inline void generateTexture() {
- if (!texture.id) {
- glGenTextures(1, &texture.id);
- }
- }
-
- inline void deleteTexture() {
- if (texture.id) {
- glDeleteTextures(1, &texture.id);
- texture.id = 0;
- }
- }
+ void bindTexture() const;
+ void generateTexture();
+ void allocateTexture();
+ void deleteTexture();
/**
* When the caller frees the texture itself, the caller
* must call this method to tell this layer that it lost
* the texture.
*/
- void clearTexture() {
- texture.id = 0;
- }
-
- inline void allocateTexture() {
-#if DEBUG_LAYERS
- ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight());
-#endif
- if (texture.id) {
- glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
- glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0,
- GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- }
- }
+ ANDROID_API void clearTexture();
inline mat4& getTexTransform() {
return texTransform;
@@ -320,6 +290,8 @@ struct Layer {
bool hasDrawnSinceUpdate;
private:
+ Caches& caches;
+
/**
* Name of the FBO used to render the layer. If the name is 0
* this layer is not backed by an FBO, but a simple texture.
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 3e55fff..cfb1e97 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -436,7 +436,7 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
if ((error = glGetError()) != GL_NO_ERROR) goto error;
caches.activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, texture);
+ caches.bindTexture(texture);
glPixelStorei(GL_PACK_ALIGNMENT, bitmap->bytesPerPixel());
@@ -467,7 +467,7 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
mat4 texTransform(layer->getTexTransform());
mat4 invert;
- invert.translate(0.0f, 1.0f, 0.0f);
+ invert.translate(0.0f, 1.0f);
invert.scale(1.0f, -1.0f, 1.0f);
layer->getTexTransform().multiply(invert);
@@ -498,7 +498,7 @@ error:
glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
layer->setAlpha(alpha, mode);
layer->setFbo(previousLayerFbo);
- glDeleteTextures(1, &texture);
+ caches.deleteTexture(texture);
caches.fboCache.put(fbo);
glViewport(previousViewport[0], previousViewport[1],
previousViewport[2], previousViewport[3]);
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 75e280c..df744be 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -128,10 +128,27 @@ public:
void multiply(float v);
- void translate(float x, float y, float z) {
- Matrix4 u;
- u.loadTranslate(x, y, z);
- multiply(u);
+ void translate(float x, float y) {
+ if ((getType() & sGeometryMask) == kTypeTranslate) {
+ data[kTranslateX] += x;
+ data[kTranslateY] += y;
+ } else {
+ // Doing a translation will only affect the translate bit of the type
+ // Save the type
+ uint32_t type = mType;
+
+ Matrix4 u;
+ u.loadTranslate(x, y, 0.0f);
+ multiply(u);
+
+ // Restore the type and fix the translate bit
+ mType = type;
+ if (data[kTranslateX] != 0.0f || data[kTranslateY] != 0.0f) {
+ mType |= kTypeTranslate;
+ } else {
+ mType &= ~kTypeTranslate;
+ }
+ }
}
void scale(float sx, float sy, float sz) {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index ddb190e..d95a62c 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;
}
@@ -366,6 +371,7 @@ void OpenGLRenderer::resume() {
dirtyClip();
mCaches.activeTexture(0);
+ mCaches.resetBoundTextures();
mCaches.blend = true;
glEnable(GL_BLEND);
@@ -459,7 +465,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 +477,7 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
}
resume();
- return result;
+ return result | DrawGlInfo::kStatusDrew;
}
///////////////////////////////////////////////////////////////////////////////
@@ -524,6 +530,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
///////////////////////////////////////////////////////////////////////////////
@@ -1339,11 +1360,15 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
// state has bounds initialized in local coordinates
if (!state.mBounds.isEmpty()) {
currentMatrix.mapRect(state.mBounds);
+ state.mClipped = !currentClip.contains(state.mBounds);
if (!state.mBounds.intersect(currentClip)) {
// quick rejected
return true;
}
} else {
+ // If we don't have bounds, let's assume we're clipped
+ // to prevent merging
+ state.mClipped = true;
state.mBounds.set(currentClip);
}
}
@@ -1382,7 +1407,7 @@ void OpenGLRenderer::setFullScreenClip() {
///////////////////////////////////////////////////////////////////////////////
void OpenGLRenderer::translate(float dx, float dy) {
- currentTransform().translate(dx, dy, 0.0f);
+ currentTransform().translate(dx, dy);
}
void OpenGLRenderer::rotate(float degrees) {
@@ -1656,6 +1681,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) {
@@ -1681,11 +1708,6 @@ void OpenGLRenderer::setupDrawAA() {
mDescription.isAA = true;
}
-void OpenGLRenderer::setupDrawPoint(float pointSize) {
- mDescription.isPoint = true;
- mDescription.pointSize = pointSize;
-}
-
void OpenGLRenderer::setupDrawColor(int color, int alpha) {
mColorA = alpha / 255.0f;
mColorR = mColorA * ((color >> 16) & 0xFF) / 255.0f;
@@ -1800,11 +1822,6 @@ void OpenGLRenderer::setupDrawModelView(float left, float top, float right, floa
}
}
-void OpenGLRenderer::setupDrawPointUniforms() {
- int slot = mCaches.currentProgram->getUniform("pointSize");
- glUniform1f(slot, mDescription.pointSize);
-}
-
void OpenGLRenderer::setupDrawColorUniforms() {
if ((mColorSet && !mDrawModifiers.mShader) || (mDrawModifiers.mShader && mSetShaderColor)) {
mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA);
@@ -1873,7 +1890,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 +1921,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,20 +2007,23 @@ 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,
- const Rect& bounds, SkPaint* paint) {
+ bool transformed, const Rect& bounds, SkPaint* paint) {
// merged draw operations don't need scissor, but clip should still be valid
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;
@@ -2001,7 +2031,7 @@ status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureV
getAlphaAndMode(paint, &alpha, &mode);
texture->setWrap(GL_CLAMP_TO_EDGE, true);
- texture->setFilter(GL_NEAREST, true); // merged ops are always pure-translation for now
+ texture->setFilter(transformed ? FILTER(paint) : GL_NEAREST, true);
const float x = (int) floorf(bounds.left + 0.5f);
const float y = (int) floorf(bounds.top + 0.5f);
@@ -2030,7 +2060,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 +2083,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 +2146,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 +2159,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 +2190,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 +2248,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 +2331,37 @@ 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,
- 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) {
if (quickReject(left, top, right, bottom)) {
return DrawGlInfo::kStatusDone;
}
- alpha *= mSnapshot->alpha;
+ AssetAtlas::Entry* entry = mCaches.assetAtlas.getEntry(bitmap);
+ const Patch* mesh = mCaches.patchCache.get(entry, bitmap->width(), bitmap->height(),
+ right - left, bottom - top, patch);
- const Patch* mesh = mCaches.patchCache.get(bitmap->width(), bitmap->height(),
- right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors);
+ return drawPatch(bitmap, mesh, entry, left, top, right, bottom, alpha, mode);
+}
+
+status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh,
+ 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;
+ }
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);
}
}
@@ -2363,7 +2409,7 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const
status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint,
bool useOffset) {
- if (!vertexBuffer.getSize()) {
+ if (!vertexBuffer.getVertexCount()) {
// no vertices to draw
return DrawGlInfo::kStatusDone;
}
@@ -2401,7 +2447,7 @@ status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPa
glVertexAttribPointer(alphaSlot, 1, GL_FLOAT, GL_FALSE, gAlphaVertexStride, alphaCoords);
}
- glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getSize());
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexBuffer.getVertexCount());
if (isAA) {
glDisableVertexAttribArray(alphaSlot);
@@ -2464,65 +2510,22 @@ status_t OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) {
}
status_t OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) {
- if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
-
- // TODO: The paint's cap style defines whether the points are square or circular
- // TODO: Handle AA for round points
+ if (mSnapshot->isIgnored() || count < 2) return DrawGlInfo::kStatusDone;
- // A stroke width of 0 has a special meaning in Skia:
- // it draws an unscaled 1px point
- float strokeWidth = paint->getStrokeWidth();
- const bool isHairLine = paint->getStrokeWidth() == 0.0f;
- if (isHairLine) {
- // Now that we know it's hairline, we can set the effective width, to be used later
- strokeWidth = 1.0f;
- }
- const float halfWidth = strokeWidth / 2;
+ count &= ~0x1; // round down to nearest two
- int alpha;
- SkXfermode::Mode mode;
- getAlphaAndMode(paint, &alpha, &mode);
-
- int verticesCount = count >> 1;
- int generatedVerticesCount = 0;
-
- TextureVertex pointsData[verticesCount];
- TextureVertex* vertex = &pointsData[0];
-
- // TODO: We should optimize this method to not generate vertices for points
- // that lie outside of the clip.
- mCaches.enableScissor();
-
- setupDraw();
- setupDrawNoTexture();
- setupDrawPoint(strokeWidth);
- setupDrawColor(paint->getColor(), alpha);
- setupDrawColorFilter();
- setupDrawShader();
- setupDrawBlending(mode);
- setupDrawProgram();
- setupDrawModelViewIdentity(true);
- setupDrawColorUniforms();
- setupDrawColorFilterUniforms();
- setupDrawPointUniforms();
- setupDrawShaderIdentityUniforms();
- setupDrawMesh(vertex);
-
- for (int i = 0; i < count; i += 2) {
- TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f);
- generatedVerticesCount++;
-
- float left = points[i] - halfWidth;
- float right = points[i] + halfWidth;
- float top = points[i + 1] - halfWidth;
- float bottom = points [i + 1] + halfWidth;
+ VertexBuffer buffer;
+ SkRect bounds;
+ PathTessellator::tessellatePoints(points, count, paint, mSnapshot->transform, bounds, buffer);
- dirtyLayer(left, top, right, bottom, currentTransform());
+ if (quickReject(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom)) {
+ return DrawGlInfo::kStatusDone;
}
- glDrawArrays(GL_POINTS, 0, generatedVerticesCount);
+ dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform());
- return DrawGlInfo::kStatusDrew;
+ bool useOffset = !paint->isAntiAlias();
+ return drawVertexBuffer(buffer, paint, useOffset);
}
status_t OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
@@ -2853,8 +2856,8 @@ mat4 OpenGLRenderer::findBestFontTransform(const mat4& transform) const {
return fontTransform;
}
-status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
- float x, float y, const float* positions, SkPaint* paint, float length,
+status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y,
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode) {
if (drawOpMode == kDrawOpMode_Immediate &&
@@ -2862,24 +2865,8 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
return DrawGlInfo::kStatusDone;
}
- if (length < 0.0f) length = paint->measureText(text, bytesCount);
- switch (paint->getTextAlign()) {
- case SkPaint::kCenter_Align:
- x -= length / 2.0f;
- break;
- case SkPaint::kRight_Align:
- x -= length;
- break;
- default:
- break;
- }
-
- SkPaint::FontMetrics metrics;
- paint->getFontMetrics(&metrics, 0.0f);
if (drawOpMode == kDrawOpMode_Immediate) {
- if (quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom)) {
- return DrawGlInfo::kStatusDone;
- }
+ if (quickReject(bounds)) return DrawGlInfo::kStatusDone;
} else {
// merged draw operations don't need scissor, but clip should still be valid
mCaches.setScissorEnabled(mScissorOptimizationDisabled);
@@ -2930,7 +2917,7 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
// TODO: Implement better clipping for scaled/rotated text
const Rect* clip = !pureTranslate ? NULL : mSnapshot->clipRect;
- Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
+ Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
bool status;
TextSetupFunctor functor(*this, x, y, pureTranslate, alpha, mode, paint);
@@ -2941,20 +2928,20 @@ status_t OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
SkPaint paintCopy(*paint);
paintCopy.setTextAlign(SkPaint::kLeft_Align);
status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y,
- positions, hasActiveLayer ? &bounds : NULL, &functor, forceFinish);
+ positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish);
} else {
status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
- positions, hasActiveLayer ? &bounds : NULL, &functor, forceFinish);
+ positions, hasActiveLayer ? &layerBounds : NULL, &functor, forceFinish);
}
if ((status || drawOpMode != kDrawOpMode_Immediate) && hasActiveLayer) {
if (!pureTranslate) {
- transform.mapRect(bounds);
+ transform.mapRect(layerBounds);
}
- dirtyLayerUnchecked(bounds, getRegion());
+ dirtyLayerUnchecked(layerBounds, getRegion());
}
- drawTextDecorations(text, bytesCount, length, oldX, oldY, paint);
+ drawTextDecorations(text, bytesCount, totalAdvance, oldX, oldY, paint);
return DrawGlInfo::kStatusDrew;
}
@@ -3128,7 +3115,7 @@ void OpenGLRenderer::resetShader() {
void OpenGLRenderer::setupShader(SkiaShader* shader) {
mDrawModifiers.mShader = shader;
if (mDrawModifiers.mShader) {
- mDrawModifiers.mShader->set(&mCaches.textureCache, &mCaches.gradientCache);
+ mDrawModifiers.mShader->setCaches(mCaches);
}
}
@@ -3196,6 +3183,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)) {
@@ -3230,17 +3225,12 @@ void OpenGLRenderer::drawPathTexture(const PathTexture* texture,
#define kStdUnderline_Offset (1.0f / 9.0f)
#define kStdUnderline_Thickness (1.0f / 18.0f)
-void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float length,
+void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float underlineWidth,
float x, float y, SkPaint* paint) {
// Handle underline and strike-through
uint32_t flags = paint->getFlags();
if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
SkPaint paintCopy(*paint);
- float underlineWidth = length;
- // If length is > 0.0f, we already measured the text for the text alignment
- if (length <= 0.0f) {
- underlineWidth = paintCopy.measureText(text, bytesCount);
- }
if (CC_LIKELY(underlineWidth > 0.0f)) {
const float textSize = paintCopy.getTextSize();
@@ -3389,19 +3379,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 +3444,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 +3504,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..ce4ce42 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,11 +81,13 @@ 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;
Rect mClip;
+ bool mClipped;
mat4 mMatrix;
DrawModifiers mDrawModifiers;
float mAlpha;
@@ -188,6 +193,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);
@@ -229,6 +242,9 @@ public:
ANDROID_API const Rect& getClipBounds();
ANDROID_API bool quickReject(float left, float top, float right, float bottom);
+ bool quickReject(const Rect& bounds) {
+ return quickReject(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ }
bool quickRejectNoScissor(float left, float top, float right, float bottom);
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
virtual bool clipPath(SkPath* path, SkRegion::Op op);
@@ -240,7 +256,7 @@ public:
virtual status_t drawLayer(Layer* layer, float x, float y);
virtual status_t drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint);
status_t drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureVertex* vertices,
- const Rect& bounds, SkPaint* paint);
+ bool transformed, const Rect& bounds, SkPaint* paint);
virtual status_t drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint);
virtual status_t drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop,
float srcRight, float srcBottom, float dstLeft, float dstTop,
@@ -248,11 +264,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, const Patch* mesh, 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);
@@ -270,7 +284,7 @@ public:
virtual status_t drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint);
virtual status_t drawText(const char* text, int bytesCount, int count, float x, float y,
- const float* positions, SkPaint* paint, float length = -1.0f,
+ const float* positions, SkPaint* paint, float totalAdvance, const Rect& bounds,
DrawOpMode drawOpMode = kDrawOpMode_Immediate);
virtual status_t drawRects(const float* rects, int count, SkPaint* paint);
@@ -798,6 +812,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,
@@ -808,12 +828,12 @@ private:
*
* @param text The text to decor
* @param bytesCount The number of bytes in the text
- * @param length The length in pixels of the text, can be <= 0.0f to force a measurement
+ * @param totalAdvance The total advance in pixels, defines underline/strikethrough length
* @param x The x coordinate where the text will be drawn
* @param y The y coordinate where the text will be drawn
* @param paint The paint to draw the text with
*/
- void drawTextDecorations(const char* text, int bytesCount, float length,
+ void drawTextDecorations(const char* text, int bytesCount, float totalAdvance,
float x, float y, SkPaint* paint);
/**
@@ -868,7 +888,7 @@ private:
* prior to calling this method.
*/
inline void bindTexture(GLuint texture) {
- glBindTexture(GL_TEXTURE_2D, texture);
+ mCaches.bindTexture(texture);
}
/**
@@ -876,7 +896,7 @@ private:
* prior to calling this method.
*/
inline void bindExternalTexture(GLuint texture) {
- glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
+ mCaches.bindTexture(GL_TEXTURE_EXTERNAL_OES, texture);
}
/**
@@ -911,7 +931,6 @@ private:
void setupDrawWithExternalTexture();
void setupDrawNoTexture();
void setupDrawAA();
- void setupDrawPoint(float pointSize);
void setupDrawColor(int color, int alpha);
void setupDrawColor(float r, float g, float b, float a);
void setupDrawAlpha8Color(int color, int alpha);
@@ -929,7 +948,6 @@ private:
bool ignoreTransform = false, bool ignoreModelView = false);
void setupDrawModelViewTranslate(float left, float top, float right, float bottom,
bool ignoreTransform = false);
- void setupDrawPointUniforms();
void setupDrawColorUniforms();
void setupDrawPureColorUniforms();
void setupDrawShaderIdentityUniforms();
@@ -943,7 +961,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 +991,7 @@ private:
void debugOverdraw(bool enable, bool clear);
void renderOverdraw();
+ void countOverdraw();
/**
* Should be invoked every time the glScissor is modified.
@@ -985,6 +1004,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 +1040,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 +1083,11 @@ 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..c23e991 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,123 @@ namespace uirenderer {
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
-PatchCache::PatchCache(): mMaxEntries(DEFAULT_PATCH_CACHE_SIZE) {
-}
-
-PatchCache::PatchCache(uint32_t maxEntries): mMaxEntries(maxEntries) {
+PatchCache::PatchCache():
+ mSize(0), mCache(LruCache<PatchDescription, Patch*>::kUnlimitedCapacity),
+ mMeshBuffer(0), mGenerationId(0) {
+ 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);
+ }
}
PatchCache::~PatchCache() {
clear();
}
-///////////////////////////////////////////////////////////////////////////////
-// 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;
+void PatchCache::init(Caches& caches) {
+ bool created = false;
+ if (!mMeshBuffer) {
+ glGenBuffers(1, &mMeshBuffer);
+ created = true;
+ }
- if (lhs.pixelWidth < rhs.pixelWidth) return -1;
- if (lhs.pixelWidth > rhs.pixelWidth) return +1;
+ caches.bindMeshBuffer(mMeshBuffer);
+ caches.resetVertexPointers();
- if (lhs.pixelHeight < rhs.pixelHeight) return -1;
- if (lhs.pixelHeight > rhs.pixelHeight) return +1;
+ if (created) {
+ createVertexBuffer();
+ }
+}
- deltaInt = lhs.xCount - rhs.xCount;
- if (deltaInt != 0) return deltaInt;
+///////////////////////////////////////////////////////////////////////////////
+// Caching
+///////////////////////////////////////////////////////////////////////////////
- deltaInt = lhs.yCount - rhs.yCount;
- 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);
+}
- deltaInt = lhs.emptyCount - rhs.emptyCount;
- if (deltaInt != 0) return deltaInt;
+int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
+ const PatchCache::PatchDescription& rhs) {
+ return memcmp(&lhs, &rhs, sizeof(PatchDescription));
+}
- deltaInt = lhs.colorKey - rhs.colorKey;
- if (deltaInt != 0) return deltaInt;
+void PatchCache::clear() {
+ clearCache();
- return 0;
+ if (mMeshBuffer) {
+ Caches::getInstance().unbindMeshBuffer();
+ glDeleteBuffers(1, &mMeshBuffer);
+ mMeshBuffer = 0;
+ mSize = 0;
+ }
}
-void PatchCache::clear() {
- size_t count = mCache.size();
- for (size_t i = 0; i < count; i++) {
- delete mCache.valueAt(i);
+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) {
+void PatchCache::createVertexBuffer() {
+ glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW);
+ mSize = 0;
+ mGenerationId++;
+}
- int8_t transparentQuads = 0;
- uint32_t colorKey = 0;
+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) {
- if (uint8_t(numColors) < sizeof(uint32_t) * 4) {
- for (int8_t i = 0; i < numColors; i++) {
- if (colors[i] == 0x0) {
- transparentQuads++;
- colorKey |= 0x1 << i;
- }
+ const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
+ const Patch* mesh = mCache.get(description);
+
+ 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) {
+ // This call ensures the VBO exists and that it is bound
+ init(Caches::getInstance());
- const PatchDescription description(bitmapWidth, bitmapHeight,
- pixelWidth, pixelHeight, width, height, transparentQuads, colorKey);
+ // 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();
+ createVertexBuffer();
+ }
- ssize_t index = mCache.indexOfKey(description);
- Patch* mesh = NULL;
- if (index >= 0) {
- mesh = mCache.valueAt(index);
- }
+ newMesh->offset = (GLintptr) mSize;
+ newMesh->textureOffset = newMesh->offset + gMeshTextureOffset;
+ mSize += size;
- 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);
+ glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices);
+
+ 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..1829b89 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,52 @@ 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;
+ }
+
+ uint32_t getGenerationId() const {
+ return mGenerationId;
}
private:
- /**
- * Description of a patch.
- */
+ void clearCache();
+ void createVertexBuffer();
+
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 +111,27 @@ 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;
+ uint32_t mGenerationId;
}; // class PatchCache
}; // namespace uirenderer
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index fdb10e2..3ab40da 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -139,7 +139,7 @@ static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap,
static PathTexture* createTexture(float left, float top, float offset,
uint32_t width, uint32_t height, uint32_t id) {
- PathTexture* texture = new PathTexture();
+ PathTexture* texture = new PathTexture(Caches::getInstance());
texture->left = left;
texture->top = top;
texture->offset = offset;
@@ -223,7 +223,7 @@ void PathCache::removeTexture(PathTexture* texture) {
}
if (texture->id) {
- glDeleteTextures(1, &texture->id);
+ Caches::getInstance().deleteTexture(texture->id);
}
delete texture;
}
@@ -300,7 +300,7 @@ void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) {
glGenTextures(1, &texture->id);
- glBindTexture(GL_TEXTURE_2D, texture->id);
+ Caches::getInstance().bindTexture(texture->id);
// Textures are Alpha8
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index dd1f996..a191f0e 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -58,7 +58,7 @@ class Caches;
* Alpha texture used to represent a path.
*/
struct PathTexture: public Texture {
- PathTexture(): Texture() {
+ PathTexture(Caches& caches): Texture(caches) {
}
~PathTexture() {
diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp
index 0879b1b..3970913 100644
--- a/libs/hwui/PathTessellator.cpp
+++ b/libs/hwui/PathTessellator.cpp
@@ -66,11 +66,11 @@ void PathTessellator::expandBoundsForStroke(SkRect& bounds, const SkPaint* paint
}
}
-inline void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
+inline static void copyVertex(Vertex* destPtr, const Vertex* srcPtr) {
Vertex::set(destPtr, srcPtr->position[0], srcPtr->position[1]);
}
-inline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
+inline static void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
AlphaVertex::set(destPtr, srcPtr->position[0], srcPtr->position[1], srcPtr->alpha);
}
@@ -84,7 +84,7 @@ inline void copyAlphaVertex(AlphaVertex* destPtr, const AlphaVertex* srcPtr) {
*
* NOTE: assumes angles between normals 90 degrees or less
*/
-inline vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
+inline static vec2 totalOffsetFromNormals(const vec2& normalA, const vec2& normalB) {
return (normalA + normalB) / (1 + fabs(normalA.dot(normalB)));
}
@@ -224,6 +224,20 @@ void getStrokeVerticesFromPerimeter(const PaintInfo& paintInfo, const Vector<Ver
DEBUG_DUMP_BUFFER();
}
+static inline void storeBeginEnd(const PaintInfo& paintInfo, const Vertex& center,
+ const vec2& normal, Vertex* buffer, int& currentIndex, bool begin) {
+ vec2 strokeOffset = normal;
+ paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
+
+ vec2 referencePoint(center.position[0], center.position[1]);
+ if (paintInfo.cap == SkPaint::kSquare_Cap) {
+ referencePoint += vec2(-strokeOffset.y, strokeOffset.x) * (begin ? -1 : 1);
+ }
+
+ Vertex::set(&buffer[currentIndex++], referencePoint + strokeOffset);
+ Vertex::set(&buffer[currentIndex++], referencePoint - strokeOffset);
+}
+
/**
* Fills a vertexBuffer with non-alpha vertices similar to getStrokeVerticesFromPerimeter, except:
*
@@ -235,19 +249,17 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
const Vector<Vertex>& vertices, VertexBuffer& vertexBuffer) {
const int extra = paintInfo.capExtraDivisions();
const int allocSize = (vertices.size() + extra) * 2;
-
Vertex* buffer = vertexBuffer.alloc<Vertex>(allocSize);
+ const int lastIndex = vertices.size() - 1;
if (extra > 0) {
// tessellate both round caps
- const int last = vertices.size() - 1;
float beginTheta = atan2(
- - (vertices[0].position[0] - vertices[1].position[0]),
- vertices[0].position[1] - vertices[1].position[1]);
+ - (vertices[0].position[0] - vertices[1].position[0]),
+ vertices[0].position[1] - vertices[1].position[1]);
float endTheta = atan2(
- - (vertices[last].position[0] - vertices[last - 1].position[0]),
- vertices[last].position[1] - vertices[last - 1].position[1]);
-
+ - (vertices[lastIndex].position[0] - vertices[lastIndex - 1].position[0]),
+ vertices[lastIndex].position[1] - vertices[lastIndex - 1].position[1]);
const float dTheta = PI / (extra + 1);
const float radialScale = 2.0f / (1 + cos(dTheta));
@@ -270,56 +282,45 @@ void getStrokeVerticesFromUnclosedVertices(const PaintInfo& paintInfo,
vec2 endRadialOffset(cos(endTheta), sin(endTheta));
paintInfo.scaleOffsetForStrokeWidth(endRadialOffset);
Vertex::set(&buffer[allocSize - 1 - capOffset],
- vertices[last].position[0] + endRadialOffset.x,
- vertices[last].position[1] + endRadialOffset.y);
+ vertices[lastIndex].position[0] + endRadialOffset.x,
+ vertices[lastIndex].position[1] + endRadialOffset.y);
}
}
int currentIndex = extra;
- const Vertex* current = &(vertices[0]);
- vec2 lastNormal;
- for (unsigned int i = 0; i < vertices.size() - 1; i++) {
+ const Vertex* last = &(vertices[0]);
+ const Vertex* current = &(vertices[1]);
+ vec2 lastNormal(current->position[1] - last->position[1],
+ last->position[0] - current->position[0]);
+ lastNormal.normalize();
+
+ storeBeginEnd(paintInfo, vertices[0], lastNormal, buffer, currentIndex, true);
+
+ for (unsigned int i = 1; i < vertices.size() - 1; i++) {
const Vertex* next = &(vertices[i + 1]);
vec2 nextNormal(next->position[1] - current->position[1],
current->position[0] - next->position[0]);
nextNormal.normalize();
- vec2 totalOffset;
- if (i == 0) {
- totalOffset = nextNormal;
- } else {
- totalOffset = totalOffsetFromNormals(lastNormal, nextNormal);
- }
- paintInfo.scaleOffsetForStrokeWidth(totalOffset);
+ vec2 strokeOffset = totalOffsetFromNormals(lastNormal, nextNormal);
+ paintInfo.scaleOffsetForStrokeWidth(strokeOffset);
- Vertex::set(&buffer[currentIndex++],
- current->position[0] + totalOffset.x,
- current->position[1] + totalOffset.y);
-
- Vertex::set(&buffer[currentIndex++],
- current->position[0] - totalOffset.x,
- current->position[1] - totalOffset.y);
+ vec2 center(current->position[0], current->position[1]);
+ Vertex::set(&buffer[currentIndex++], center + strokeOffset);
+ Vertex::set(&buffer[currentIndex++], center - strokeOffset);
current = next;
lastNormal = nextNormal;
}
- vec2 totalOffset = lastNormal;
- paintInfo.scaleOffsetForStrokeWidth(totalOffset);
-
- Vertex::set(&buffer[currentIndex++],
- current->position[0] + totalOffset.x,
- current->position[1] + totalOffset.y);
- Vertex::set(&buffer[currentIndex++],
- current->position[0] - totalOffset.x,
- current->position[1] - totalOffset.y);
+ storeBeginEnd(paintInfo, vertices[lastIndex], lastNormal, buffer, currentIndex, false);
DEBUG_DUMP_BUFFER();
}
/**
* Populates a vertexBuffer with AlphaVertices to create an anti-aliased fill shape tessellation
- *
+ *
* 1 - create the AA perimeter of unit width, by zig-zagging at each point around the perimeter of
* the shape (using 2 * perimeter.size() vertices)
*
@@ -389,7 +390,7 @@ void getFillVerticesFromPerimeterAA(const PaintInfo& paintInfo, const Vector<Ver
* For explanation of constants and general methodoloyg, see comments for
* getStrokeVerticesFromUnclosedVerticesAA() below.
*/
-inline void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
+inline static void storeCapAA(const PaintInfo& paintInfo, const Vector<Vertex>& vertices,
AlphaVertex* buffer, bool isFirst, vec2 normal, int offset) {
const int extra = paintInfo.capExtraDivisions();
const int extraOffset = (extra + 1) / 2;
@@ -772,11 +773,67 @@ void PathTessellator::tessellatePath(const SkPath &path, const SkPaint* paint,
}
}
+static void expandRectToCoverVertex(SkRect& rect, float x, float y) {
+ rect.fLeft = fminf(rect.fLeft, x);
+ rect.fTop = fminf(rect.fTop, y);
+ rect.fRight = fmaxf(rect.fRight, x);
+ rect.fBottom = fmaxf(rect.fBottom, y);
+}
static void expandRectToCoverVertex(SkRect& rect, const Vertex& vertex) {
- rect.fLeft = fminf(rect.fLeft, vertex.position[0]);
- rect.fTop = fminf(rect.fTop, vertex.position[1]);
- rect.fRight = fmaxf(rect.fRight, vertex.position[0]);
- rect.fBottom = fmaxf(rect.fBottom, vertex.position[1]);
+ expandRectToCoverVertex(rect, vertex.position[0], vertex.position[1]);
+}
+
+template <class TYPE>
+static void instanceVertices(VertexBuffer& srcBuffer, VertexBuffer& dstBuffer,
+ const float* points, int count, SkRect& bounds) {
+ bounds.set(points[0], points[1], points[0], points[1]);
+
+ int numPoints = count / 2;
+ int verticesPerPoint = srcBuffer.getVertexCount();
+ dstBuffer.alloc<TYPE>(numPoints * verticesPerPoint + (numPoints - 1) * 2);
+
+ for (int i = 0; i < count; i += 2) {
+ expandRectToCoverVertex(bounds, points[i + 0], points[i + 1]);
+ dstBuffer.copyInto<TYPE>(srcBuffer, points[i + 0], points[i + 1]);
+ }
+ dstBuffer.createDegenerateSeparators<TYPE>(verticesPerPoint);
+}
+
+void PathTessellator::tessellatePoints(const float* points, int count, SkPaint* paint,
+ const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer) {
+ const PaintInfo paintInfo(paint, transform);
+
+ // determine point shape
+ SkPath path;
+ float radius = paintInfo.halfStrokeWidth;
+ if (radius == 0.0f) radius = 0.25f;
+
+ if (paintInfo.cap == SkPaint::kRound_Cap) {
+ path.addCircle(0, 0, radius);
+ } else {
+ path.addRect(-radius, -radius, radius, radius);
+ }
+
+ // calculate outline
+ Vector<Vertex> outlineVertices;
+ approximatePathOutlineVertices(path, true,
+ paintInfo.inverseScaleX * paintInfo.inverseScaleX,
+ paintInfo.inverseScaleY * paintInfo.inverseScaleY, outlineVertices);
+
+ if (!outlineVertices.size()) return;
+
+ // tessellate, then duplicate outline across points
+ int numPoints = count / 2;
+ VertexBuffer tempBuffer;
+ if (!paintInfo.isAA) {
+ getFillVerticesFromPerimeter(outlineVertices, tempBuffer);
+ instanceVertices<Vertex>(tempBuffer, vertexBuffer, points, count, bounds);
+ } else {
+ getFillVerticesFromPerimeterAA(paintInfo, outlineVertices, tempBuffer);
+ instanceVertices<AlphaVertex>(tempBuffer, vertexBuffer, points, count, bounds);
+ }
+
+ expandBoundsForStroke(bounds, paint, true); // force-expand bounds to incorporate stroke
}
void PathTessellator::tessellateLines(const float* points, int count, SkPaint* paint,
diff --git a/libs/hwui/PathTessellator.h b/libs/hwui/PathTessellator.h
index 596d49d..85797fc 100644
--- a/libs/hwui/PathTessellator.h
+++ b/libs/hwui/PathTessellator.h
@@ -30,7 +30,7 @@ class VertexBuffer {
public:
VertexBuffer():
mBuffer(0),
- mSize(0),
+ mVertexCount(0),
mCleanupMethod(NULL)
{}
@@ -44,30 +44,42 @@ public:
multiple regions within a single VertexBuffer, such as with PathTessellator::tesselateLines()
*/
template <class TYPE>
- TYPE* alloc(int size) {
- if (mSize) {
+ TYPE* alloc(int vertexCount) {
+ if (mVertexCount) {
TYPE* reallocBuffer = (TYPE*)mReallocBuffer;
// already have allocated the buffer, re-allocate space within
if (mReallocBuffer != mBuffer) {
// not first re-allocation, leave space for degenerate triangles to separate strips
reallocBuffer += 2;
}
- mReallocBuffer = reallocBuffer + size;
+ mReallocBuffer = reallocBuffer + vertexCount;
return reallocBuffer;
}
- mSize = size;
- mReallocBuffer = mBuffer = (void*)new TYPE[size];
+ mVertexCount = vertexCount;
+ mReallocBuffer = mBuffer = (void*)new TYPE[vertexCount];
mCleanupMethod = &(cleanup<TYPE>);
return (TYPE*)mBuffer;
}
- void* getBuffer() const { return mBuffer; }
- unsigned int getSize() const { return mSize; }
+ template <class TYPE>
+ void copyInto(const VertexBuffer& srcBuffer, float xOffset, float yOffset) {
+ int verticesToCopy = srcBuffer.getVertexCount();
+
+ TYPE* dst = alloc<TYPE>(verticesToCopy);
+ TYPE* src = (TYPE*)srcBuffer.getBuffer();
+
+ for (int i = 0; i < verticesToCopy; i++) {
+ TYPE::copyWithOffset(&dst[i], src[i], xOffset, yOffset);
+ }
+ }
+
+ void* getBuffer() const { return mBuffer; } // shouldn't be const, since not a const ptr?
+ unsigned int getVertexCount() const { return mVertexCount; }
template <class TYPE>
void createDegenerateSeparators(int allocSize) {
- TYPE* end = (TYPE*)mBuffer + mSize;
+ TYPE* end = (TYPE*)mBuffer + mVertexCount;
for (TYPE* degen = (TYPE*)mBuffer + allocSize; degen < end; degen += 2 + allocSize) {
memcpy(degen, degen - 1, sizeof(TYPE));
memcpy(degen + 1, degen + 2, sizeof(TYPE));
@@ -81,7 +93,7 @@ private:
}
void* mBuffer;
- unsigned int mSize;
+ unsigned int mVertexCount;
void* mReallocBuffer; // used for multi-allocation
@@ -95,6 +107,9 @@ public:
static void tessellatePath(const SkPath& path, const SkPaint* paint,
const mat4 *transform, VertexBuffer& vertexBuffer);
+ static void tessellatePoints(const float* points, int count, SkPaint* paint,
+ const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer);
+
static void tessellateLines(const float* points, int count, SkPaint* paint,
const mat4* transform, SkRect& bounds, VertexBuffer& vertexBuffer);
diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp
index 8280370..29f8756 100644
--- a/libs/hwui/PixelBuffer.cpp
+++ b/libs/hwui/PixelBuffer.cpp
@@ -147,14 +147,8 @@ void GpuPixelBuffer::upload(uint32_t x, uint32_t y, uint32_t width, uint32_t hei
///////////////////////////////////////////////////////////////////////////////
PixelBuffer* PixelBuffer::create(GLenum format, uint32_t width, uint32_t height, BufferType type) {
- bool gpuBuffer = type == kBufferType_Auto && Extensions::getInstance().getMajorGlVersion() >= 3;
- if (gpuBuffer) {
- char property[PROPERTY_VALUE_MAX];
- if (property_get(PROPERTY_ENABLE_GPU_PIXEL_BUFFERS, property, "false") > 0) {
- if (!strcmp(property, "true")) {
- return new GpuPixelBuffer(format, width, height);
- }
- }
+ if (type == kBufferType_Auto && Caches::getInstance().gpuPixelBuffersEnabled) {
+ return new GpuPixelBuffer(format, width, height);
}
return new CpuPixelBuffer(format, width, height);
}
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 14a2376..9e4670e 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);
+ 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..4f94afc 100644
--- a/libs/hwui/Program.h
+++ b/libs/hwui/Program.h
@@ -68,23 +68,22 @@ namespace uirenderer {
#define PROGRAM_BITMAP_WRAPS_SHIFT 9
#define PROGRAM_BITMAP_WRAPT_SHIFT 11
-#define PROGRAM_GRADIENT_TYPE_SHIFT 33
+#define PROGRAM_GRADIENT_TYPE_SHIFT 33 // 2 bits for gradient type
#define PROGRAM_MODULATE_SHIFT 35
-#define PROGRAM_IS_POINT_SHIFT 36
+#define PROGRAM_HAS_AA_SHIFT 36
-#define PROGRAM_HAS_AA_SHIFT 37
+#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 37
+#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 38
-#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38
-#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39
+#define PROGRAM_HAS_GAMMA_CORRECTION 39
-#define PROGRAM_HAS_GAMMA_CORRECTION 40
+#define PROGRAM_IS_SIMPLE_GRADIENT 40
-#define PROGRAM_IS_SIMPLE_GRADIENT 41
+#define PROGRAM_HAS_COLORS 41
-#define PROGRAM_HAS_COLORS 42
-
-#define PROGRAM_HAS_DEBUG_HIGHLIGHT 43
+#define PROGRAM_HAS_DEBUG_HIGHLIGHT 42
+#define PROGRAM_EMULATE_STENCIL 43
///////////////////////////////////////////////////////////////////////////////
// Types
@@ -156,13 +155,11 @@ struct ProgramDescription {
SkXfermode::Mode framebufferMode;
bool swapSrcDst;
- bool isPoint;
- float pointSize;
-
bool hasGammaCorrection;
float gamma;
bool hasDebugHighlight;
+ bool emulateStencil;
/**
* Resets this description. All fields are reset back to the default
@@ -199,9 +196,6 @@ struct ProgramDescription {
framebufferMode = SkXfermode::kClear_Mode;
swapSrcDst = false;
- isPoint = false;
- pointSize = 0.0f;
-
hasGammaCorrection = false;
gamma = 2.2f;
@@ -267,7 +261,6 @@ struct ProgramDescription {
key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT;
if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST;
if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT;
- if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT;
if (isAA) key |= programid(0x1) << PROGRAM_HAS_AA_SHIFT;
if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT;
if (hasTextureTransform) key |= programid(0x1) << PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT;
@@ -275,6 +268,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 +424,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..a5ce6f6 100644
--- a/libs/hwui/ProgramCache.cpp
+++ b/libs/hwui/ProgramCache.cpp
@@ -53,8 +53,6 @@ const char* gVS_Header_Uniforms_TextureTransform =
const char* gVS_Header_Uniforms =
"uniform mat4 projection;\n" \
"uniform mat4 transform;\n";
-const char* gVS_Header_Uniforms_IsPoint =
- "uniform mediump float pointSize;\n";
const char* gVS_Header_Uniforms_HasGradient =
"uniform mat4 screenSpace;\n";
const char* gVS_Header_Uniforms_HasBitmap =
@@ -68,8 +66,6 @@ const char* gVS_Header_Varyings_IsAAVertexShape =
"varying float alpha;\n";
const char* gVS_Header_Varyings_HasBitmap =
"varying highp vec2 outBitmapTexCoords;\n";
-const char* gVS_Header_Varyings_PointHasBitmap =
- "varying highp vec2 outPointBitmapTexCoords;\n";
const char* gVS_Header_Varyings_HasGradient[6] = {
// Linear
"varying highp vec2 linear;\n"
@@ -118,12 +114,8 @@ const char* gVS_Main_OutGradient[6] = {
};
const char* gVS_Main_OutBitmapTexCoords =
" outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
-const char* gVS_Main_OutPointBitmapTexCoords =
- " outPointBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n";
const char* gVS_Main_Position =
" gl_Position = projection * transform * position;\n";
-const char* gVS_Main_PointSize =
- " gl_PointSize = pointSize;\n";
const char* gVS_Main_AAVertexShape =
" alpha = vtxAlpha;\n";
const char* gVS_Footer =
@@ -141,9 +133,6 @@ const char* gFS_Header =
"precision mediump float;\n\n";
const char* gFS_Uniforms_Color =
"uniform vec4 color;\n";
-const char* gFS_Header_Uniforms_PointHasBitmap =
- "uniform vec2 textureDimension;\n"
- "uniform float pointSize;\n";
const char* gFS_Uniforms_TextureSampler =
"uniform sampler2D baseSampler;\n";
const char* gFS_Uniforms_ExternalTextureSampler =
@@ -178,10 +167,6 @@ const char* gFS_Main =
"\nvoid main(void) {\n"
" lowp vec4 fragColor;\n";
-const char* gFS_Main_PointBitmapTexCoords =
- " highp vec2 outBitmapTexCoords = outPointBitmapTexCoords + "
- "((gl_PointCoord - vec2(0.5, 0.5)) * textureDimension * vec2(pointSize, pointSize));\n";
-
const char* gFS_Main_Dither[2] = {
// ES 2.0
"texture2D(ditherSampler, ditherTexCoords).a * " STR(DITHER_KERNEL_SIZE_INV_SQUARE),
@@ -342,6 +327,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";
@@ -478,9 +469,6 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
if (description.hasBitmap) {
shader.append(gVS_Header_Uniforms_HasBitmap);
}
- if (description.isPoint) {
- shader.append(gVS_Header_Uniforms_IsPoint);
- }
// Varyings
if (description.hasTexture || description.hasExternalTexture) {
shader.append(gVS_Header_Varyings_HasTexture);
@@ -495,9 +483,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
}
if (description.hasBitmap) {
- shader.append(description.isPoint ?
- gVS_Header_Varyings_PointHasBitmap :
- gVS_Header_Varyings_HasBitmap);
+ shader.append(gVS_Header_Varyings_HasBitmap);
}
// Begin the shader
@@ -514,12 +500,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description
shader.append(gVS_Main_OutColors);
}
if (description.hasBitmap) {
- shader.append(description.isPoint ?
- gVS_Main_OutPointBitmapTexCoords :
- gVS_Main_OutBitmapTexCoords);
- }
- if (description.isPoint) {
- shader.append(gVS_Main_PointSize);
+ shader.append(gVS_Main_OutBitmapTexCoords);
}
// Output transformed position
shader.append(gVS_Main_Position);
@@ -570,9 +551,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.append(gVS_Header_Varyings_HasGradient[gradientIndex(description)]);
}
if (description.hasBitmap) {
- shader.append(description.isPoint ?
- gVS_Header_Varyings_PointHasBitmap :
- gVS_Header_Varyings_HasBitmap);
+ shader.append(gVS_Header_Varyings_HasBitmap);
}
// Uniforms
@@ -593,9 +572,6 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.appendFormat(gFS_Uniforms_GradientSampler[description.isSimpleGradient],
gFS_Uniforms_Dither);
}
- if (description.hasBitmap && description.isPoint) {
- shader.append(gFS_Header_Uniforms_PointHasBitmap);
- }
if (description.hasGammaCorrection) {
shader.append(gFS_Uniforms_Gamma);
}
@@ -603,7 +579,7 @@ 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.hasDebugHighlight && !description.emulateStencil) {
bool fast = false;
const bool noShader = !description.hasGradient && !description.hasBitmap;
@@ -683,6 +659,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) {
@@ -703,9 +682,6 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti
shader.appendFormat(gFS_Main_AddDitherToGradient, gFS_Main_Dither[mHasES3]);
}
if (description.hasBitmap) {
- if (description.isPoint) {
- shader.append(gFS_Main_PointBitmapTexCoords);
- }
if (!description.isBitmapNpot) {
shader.append(gFS_Main_FetchBitmap);
} else {
@@ -757,6 +733,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..dbbb956 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.
@@ -123,9 +123,9 @@ enum DebugLevel {
/**
* Indicates whether PBOs can be used to back pixel buffers.
- * Accepted values are "true" and "false".
+ * Accepted values are "true" and "false". Default is true.
*/
-#define PROPERTY_ENABLE_GPU_PIXEL_BUFFERS "hwui.use_gpu_pixel_buffers"
+#define PROPERTY_ENABLE_GPU_PIXEL_BUFFERS "ro.hwui.use_gpu_pixel_buffers"
// These properties are defined in mega-bytes
#define PROPERTY_TEXTURE_CACHE_SIZE "ro.hwui.texture_cache_size"
@@ -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/Rect.h b/libs/hwui/Rect.h
index f50ac3c..689fe6c0 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -125,11 +125,11 @@ public:
return intersect(r.left, r.top, r.right, r.bottom);
}
- inline bool contains(float l, float t, float r, float b) {
+ inline bool contains(float l, float t, float r, float b) const {
return l >= left && t >= top && r <= right && b <= bottom;
}
- inline bool contains(const Rect& r) {
+ inline bool contains(const Rect& r) const {
return contains(r.left, r.top, r.right, r.bottom);
}
diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp
index c38eedb..797ed10 100644
--- a/libs/hwui/SkiaShader.cpp
+++ b/libs/hwui/SkiaShader.cpp
@@ -69,9 +69,13 @@ void SkiaShader::copyFrom(const SkiaShader& shader) {
mGenerationId = shader.mGenerationId;
}
+SkiaShader::SkiaShader(): mCaches(NULL) {
+}
+
SkiaShader::SkiaShader(Type type, SkShader* key, SkShader::TileMode tileX,
SkShader::TileMode tileY, SkMatrix* matrix, bool blend):
- mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend) {
+ mType(type), mKey(key), mTileX(tileX), mTileY(tileY), mBlend(blend),
+ mCaches(NULL) {
setMatrix(matrix);
mGenerationId = 0;
}
@@ -87,7 +91,7 @@ void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Sna
}
void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT) {
- glBindTexture(GL_TEXTURE_2D, texture->id);
+ mCaches->bindTexture(texture->id);
texture->setWrapST(wrapS, wrapT);
}
@@ -114,7 +118,7 @@ SkiaShader* SkiaBitmapShader::copy() {
}
void SkiaBitmapShader::describe(ProgramDescription& description, const Extensions& extensions) {
- Texture* texture = mTextureCache->get(mBitmap);
+ Texture* texture = mCaches->textureCache.get(mBitmap);
if (!texture) return;
mTexture = texture;
@@ -229,7 +233,7 @@ void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelV
GLuint textureSlot = (*textureUnit)++;
Caches::getInstance().activeTexture(textureSlot);
- Texture* texture = mGradientCache->get(mColors, mPositions, mCount);
+ Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
// Uniforms
bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
@@ -349,7 +353,7 @@ void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelVi
GLuint textureSlot = (*textureUnit)++;
Caches::getInstance().activeTexture(textureSlot);
- Texture* texture = mGradientCache->get(mColors, mPositions, mCount);
+ Texture* texture = mCaches->gradientCache.get(mColors, mPositions, mCount);
// Uniforms
bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]);
@@ -359,7 +363,7 @@ void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelVi
bindUniformColor(program->getUniform("endColor"), mColors[1]);
}
- Caches::getInstance().dither.setupProgram(program, textureUnit);
+ mCaches->dither.setupProgram(program, textureUnit);
mat4 screenSpace;
computeScreenSpaceMatrix(screenSpace, modelView);
@@ -394,12 +398,6 @@ SkiaShader* SkiaComposeShader::copy() {
return copy;
}
-void SkiaComposeShader::set(TextureCache* textureCache, GradientCache* gradientCache) {
- SkiaShader::set(textureCache, gradientCache);
- mFirst->set(textureCache, gradientCache);
- mSecond->set(textureCache, gradientCache);
-}
-
void SkiaComposeShader::describe(ProgramDescription& description, const Extensions& extensions) {
mFirst->describe(description, extensions);
mSecond->describe(description, extensions);
diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h
index bc12b0d..a63431c 100644
--- a/libs/hwui/SkiaShader.h
+++ b/libs/hwui/SkiaShader.h
@@ -33,6 +33,8 @@
namespace android {
namespace uirenderer {
+class Caches;
+
///////////////////////////////////////////////////////////////////////////////
// Base shader
///////////////////////////////////////////////////////////////////////////////
@@ -77,9 +79,8 @@ struct SkiaShader {
return mType;
}
- virtual void set(TextureCache* textureCache, GradientCache* gradientCache) {
- mTextureCache = textureCache;
- mGradientCache = gradientCache;
+ virtual void setCaches(Caches& caches) {
+ mCaches = &caches;
}
uint32_t getGenerationId() {
@@ -103,8 +104,7 @@ struct SkiaShader {
void computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView);
protected:
- SkiaShader() {
- }
+ SkiaShader();
/**
* The appropriate texture unit must have been activated prior to invoking
@@ -118,8 +118,7 @@ protected:
SkShader::TileMode mTileY;
bool mBlend;
- TextureCache* mTextureCache;
- GradientCache* mGradientCache;
+ Caches* mCaches;
mat4 mUnitMatrix;
mat4 mShaderMatrix;
@@ -229,7 +228,11 @@ struct SkiaComposeShader: public SkiaShader {
~SkiaComposeShader();
SkiaShader* copy();
- void set(TextureCache* textureCache, GradientCache* gradientCache);
+ void setCaches(Caches& caches) {
+ SkiaShader::setCaches(caches);
+ mFirst->setCaches(caches);
+ mSecond->setCaches(caches);
+ }
void describe(ProgramDescription& description, const Extensions& extensions);
void setupProgram(Program* program, const mat4& modelView, const Snapshot& snapshot,
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index 6976eaa..0b2c130 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -18,6 +18,7 @@
#include <utils/JenkinsHash.h>
+#include "Caches.h"
#include "Debug.h"
#include "TextDropShadowCache.h"
#include "Properties.h"
@@ -154,7 +155,7 @@ void TextDropShadowCache::operator()(ShadowText& text, ShadowTexture*& texture)
ALOGD("Shadow texture deleted, size = %d", texture->bitmapSize);
}
- glDeleteTextures(1, &texture->id);
+ texture->deleteTexture();
delete texture;
}
}
@@ -182,7 +183,9 @@ ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32
return NULL;
}
- texture = new ShadowTexture;
+ Caches& caches = Caches::getInstance();
+
+ texture = new ShadowTexture(caches);
texture->left = shadow.penX;
texture->top = shadow.penY;
texture->width = shadow.width;
@@ -202,7 +205,7 @@ ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32
glGenTextures(1, &texture->id);
- glBindTexture(GL_TEXTURE_2D, texture->id);
+ caches.bindTexture(texture->id);
// Textures are Alpha8
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index 0bed72b6..04d7357 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -30,6 +30,8 @@
namespace android {
namespace uirenderer {
+class Caches;
+
struct ShadowText {
ShadowText(): len(0), radius(0.0f), textSize(0.0f), typeface(NULL),
flags(0), italicStyle(0.0f), scaleX(0), text(NULL), positions(NULL) {
@@ -114,7 +116,7 @@ inline hash_t hash_type(const ShadowText& entry) {
* Alpha texture used to represent a shadow.
*/
struct ShadowTexture: public Texture {
- ShadowTexture(): Texture() {
+ ShadowTexture(Caches& caches): Texture(caches) {
}
float left;
diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp
new file mode 100644
index 0000000..7923ce7
--- /dev/null
+++ b/libs/hwui/Texture.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "Caches.h"
+#include "Texture.h"
+
+namespace android {
+namespace uirenderer {
+
+Texture::Texture(): id(0), generation(0), blend(false), width(0), height(0),
+ cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL),
+ mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
+ mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
+ mFirstFilter(true), mFirstWrap(true), mCaches(Caches::getInstance()) {
+}
+
+Texture::Texture(Caches& caches): id(0), generation(0), blend(false), width(0), height(0),
+ cleanup(false), bitmapSize(0), mipMap(false), uvMapper(NULL),
+ mWrapS(GL_CLAMP_TO_EDGE), mWrapT(GL_CLAMP_TO_EDGE),
+ mMinFilter(GL_NEAREST), mMagFilter(GL_NEAREST),
+ mFirstFilter(true), mFirstWrap(true), mCaches(caches) {
+}
+
+void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force,
+ GLenum renderTarget) {
+
+ if (mFirstWrap || force || wrapS != mWrapS || wrapT != mWrapT) {
+ mFirstWrap = false;
+
+ mWrapS = wrapS;
+ mWrapT = wrapT;
+
+ if (bindTexture) {
+ mCaches.bindTexture(renderTarget, id);
+ }
+
+ glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
+ glTexParameteri(renderTarget, GL_TEXTURE_WRAP_T, wrapT);
+ }
+}
+
+void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool force,
+ GLenum renderTarget) {
+
+ if (mFirstFilter || force || min != mMinFilter || mag != mMagFilter) {
+ mFirstFilter = false;
+
+ mMinFilter = min;
+ mMagFilter = mag;
+
+ if (bindTexture) {
+ mCaches.bindTexture(renderTarget, id);
+ }
+
+ if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
+
+ glTexParameteri(renderTarget, GL_TEXTURE_MIN_FILTER, min);
+ glTexParameteri(renderTarget, GL_TEXTURE_MAG_FILTER, mag);
+ }
+}
+
+void Texture::deleteTexture() const {
+ mCaches.deleteTexture(id);
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h
index 8d88bdc..d48ec59 100644
--- a/libs/hwui/Texture.h
+++ b/libs/hwui/Texture.h
@@ -22,75 +22,39 @@
namespace android {
namespace uirenderer {
+class Caches;
+class UvMapper;
+
/**
* Represents an OpenGL texture.
*/
-struct Texture {
- Texture() {
- cleanup = false;
- bitmapSize = 0;
-
- wrapS = GL_CLAMP_TO_EDGE;
- wrapT = GL_CLAMP_TO_EDGE;
-
- minFilter = GL_NEAREST;
- magFilter = GL_NEAREST;
-
- mipMap = false;
-
- firstFilter = true;
- firstWrap = true;
+class Texture {
+public:
+ Texture();
+ Texture(Caches& caches);
- id = 0;
- }
+ virtual ~Texture() { }
- void setWrap(GLenum wrap, bool bindTexture = false, bool force = false,
+ inline void setWrap(GLenum wrap, bool bindTexture = false, bool force = false,
GLenum renderTarget = GL_TEXTURE_2D) {
setWrapST(wrap, wrap, bindTexture, force, renderTarget);
}
- void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, bool force = false,
- GLenum renderTarget = GL_TEXTURE_2D) {
-
- if (firstWrap || force || wrapS != this->wrapS || wrapT != this->wrapT) {
- firstWrap = false;
-
- this->wrapS = wrapS;
- this->wrapT = wrapT;
-
- if (bindTexture) {
- glBindTexture(renderTarget, id);
- }
-
- glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS);
- glTexParameteri(renderTarget, GL_TEXTURE_WRAP_T, wrapT);
- }
- }
+ virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false,
+ bool force = false, GLenum renderTarget = GL_TEXTURE_2D);
- void setFilter(GLenum filter, bool bindTexture = false, bool force = false,
+ inline void setFilter(GLenum filter, bool bindTexture = false, bool force = false,
GLenum renderTarget = GL_TEXTURE_2D) {
setFilterMinMag(filter, filter, bindTexture, force, renderTarget);
}
- void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false, bool force = false,
- GLenum renderTarget = GL_TEXTURE_2D) {
+ virtual void setFilterMinMag(GLenum min, GLenum mag, bool bindTexture = false,
+ bool force = false, GLenum renderTarget = GL_TEXTURE_2D);
- if (firstFilter || force || min != minFilter || mag != magFilter) {
- firstFilter = false;
-
- minFilter = min;
- magFilter = mag;
-
- if (bindTexture) {
- glBindTexture(renderTarget, id);
- }
-
- if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR;
-
- glTexParameteri(renderTarget, GL_TEXTURE_MIN_FILTER, min);
- glTexParameteri(renderTarget, GL_TEXTURE_MAG_FILTER, mag);
- }
- }
+ /**
+ * Convenience method to call glDeleteTextures() on this texture's id.
+ */
+ void deleteTexture() const;
/**
* Name of the texture.
@@ -125,21 +89,28 @@ 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.
*/
- GLenum wrapS;
- GLenum wrapT;
+ GLenum mWrapS;
+ GLenum mWrapT;
/**
* Last filters set on this texture. Defaults to GL_NEAREST.
*/
- GLenum minFilter;
- GLenum magFilter;
+ GLenum mMinFilter;
+ GLenum mMagFilter;
+
+ bool mFirstFilter;
+ bool mFirstWrap;
- bool firstFilter;
- bool firstWrap;
+ Caches& mCaches;
}; // struct Texture
class AutoTexture {
@@ -147,7 +118,7 @@ public:
AutoTexture(const Texture* texture): mTexture(texture) { }
~AutoTexture() {
if (mTexture && mTexture->cleanup) {
- glDeleteTextures(1, &mTexture->id);
+ mTexture->deleteTexture();
delete mTexture;
}
}
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 2378eb5..a63cac6 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -112,7 +112,7 @@ void TextureCache::operator()(SkBitmap*& bitmap, Texture*& texture) {
if (mDebugEnabled) {
ALOGD("Texture deleted, size = %d", texture->bitmapSize);
}
- glDeleteTextures(1, &texture->id);
+ texture->deleteTexture();
delete texture;
}
}
@@ -139,7 +139,7 @@ Texture* TextureCache::get(SkBitmap* bitmap) {
}
}
- texture = new Texture;
+ texture = new Texture();
texture->bitmapSize = size;
generateTexture(bitmap, texture, false);
@@ -162,7 +162,7 @@ Texture* TextureCache::get(SkBitmap* bitmap) {
}
Texture* TextureCache::getTransient(SkBitmap* bitmap) {
- Texture* texture = new Texture;
+ Texture* texture = new Texture();
texture->bitmapSize = bitmap->rowBytes() * bitmap->height();
texture->cleanup = true;
@@ -235,7 +235,7 @@ void TextureCache::generateTexture(SkBitmap* bitmap, Texture* texture, bool rege
texture->width = bitmap->width();
texture->height = bitmap->height();
- glBindTexture(GL_TEXTURE_2D, texture->id);
+ Caches::getInstance().bindTexture(texture->id);
switch (bitmap->getConfig()) {
case SkBitmap::kA8_Config:
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/Vertex.h b/libs/hwui/Vertex.h
index 523120e..c06762f 100644
--- a/libs/hwui/Vertex.h
+++ b/libs/hwui/Vertex.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_HWUI_VERTEX_H
#define ANDROID_HWUI_VERTEX_H
+#include "Vector.h"
+
namespace android {
namespace uirenderer {
@@ -30,6 +32,15 @@ struct Vertex {
vertex[0].position[0] = x;
vertex[0].position[1] = y;
}
+
+ static inline void set(Vertex* vertex, vec2 val) {
+ set(vertex, val.x, val.y);
+ }
+
+ static inline void copyWithOffset(Vertex* vertex, const Vertex& src, float x, float y) {
+ set(vertex, src.position[0] + x, src.position[1] + y);
+ }
+
}; // struct Vertex
/**
@@ -81,6 +92,12 @@ struct AlphaVertex : Vertex {
vertex[0].alpha = alpha;
}
+ static inline void copyWithOffset(AlphaVertex* vertex, const AlphaVertex& src,
+ float x, float y) {
+ Vertex::set(vertex, src.position[0] + x, src.position[1] + y);
+ vertex[0].alpha = src.alpha;
+ }
+
static inline void setColor(AlphaVertex* vertex, float alpha) {
vertex[0].alpha = alpha;
}
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
index 6c5267d..5f15724 100644
--- a/libs/hwui/font/CacheTexture.cpp
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -17,6 +17,7 @@
#include <SkGlyph.h>
#include "CacheTexture.h"
+#include "../Caches.h"
#include "../Debug.h"
#include "../Extensions.h"
#include "../PixelBuffer.h"
@@ -110,7 +111,8 @@ CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock* blockToRemove)
CacheTexture::CacheTexture(uint16_t width, uint16_t height, uint32_t maxQuadCount) :
mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
mLinearFiltering(false), mDirty(false), mNumGlyphs(0),
- mMesh(NULL), mCurrentQuad(0), mMaxQuadCount(maxQuadCount) {
+ mMesh(NULL), mCurrentQuad(0), mMaxQuadCount(maxQuadCount),
+ mCaches(Caches::getInstance()) {
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
@@ -154,7 +156,7 @@ void CacheTexture::releaseTexture() {
mTexture = NULL;
}
if (mTextureId) {
- glDeleteTextures(1, &mTextureId);
+ mCaches.deleteTexture(mTextureId);
mTextureId = 0;
}
mDirty = false;
@@ -166,7 +168,7 @@ void CacheTexture::setLinearFiltering(bool linearFiltering, bool bind) {
mLinearFiltering = linearFiltering;
const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
- if (bind) glBindTexture(GL_TEXTURE_2D, getTextureId());
+ if (bind) mCaches.bindTexture(getTextureId());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
}
@@ -186,7 +188,7 @@ void CacheTexture::allocateTexture() {
if (!mTextureId) {
glGenTextures(1, &mTextureId);
- glBindTexture(GL_TEXTURE_2D, mTextureId);
+ mCaches.bindTexture(mTextureId);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Initialize texture dimensions
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mWidth, mHeight, 0,
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index ddcc836..8c3ea0b 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -30,6 +30,7 @@
namespace android {
namespace uirenderer {
+class Caches;
class PixelBuffer;
/**
@@ -178,9 +179,10 @@ private:
TextureVertex* mMesh;
uint32_t mCurrentQuad;
uint32_t mMaxQuadCount;
+ Caches& mCaches;
CacheBlock* mCacheBlocks;
- Rect mDirtyRect;
bool mHasES3;
+ Rect mDirtyRect;
};
}; // namespace uirenderer
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;