summaryrefslogtreecommitdiffstats
path: root/libs/hwui
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui')
-rw-r--r--libs/hwui/Android.mk24
-rw-r--r--libs/hwui/AssetAtlas.cpp142
-rw-r--r--libs/hwui/AssetAtlas.h195
-rw-r--r--libs/hwui/Caches.cpp174
-rw-r--r--libs/hwui/Caches.h73
-rw-r--r--libs/hwui/Debug.h2
-rw-r--r--libs/hwui/DeferredDisplayList.cpp285
-rw-r--r--libs/hwui/DeferredDisplayList.h85
-rw-r--r--libs/hwui/DisplayList.cpp45
-rw-r--r--libs/hwui/DisplayList.h22
-rw-r--r--libs/hwui/DisplayListOp.h542
-rw-r--r--libs/hwui/DisplayListRenderer.cpp34
-rw-r--r--libs/hwui/DisplayListRenderer.h30
-rw-r--r--libs/hwui/Dither.cpp15
-rw-r--r--libs/hwui/Dither.h7
-rw-r--r--libs/hwui/Extensions.cpp89
-rw-r--r--libs/hwui/Extensions.h30
-rw-r--r--libs/hwui/Fence.h113
-rw-r--r--libs/hwui/FontRenderer.cpp407
-rw-r--r--libs/hwui/FontRenderer.h68
-rw-r--r--libs/hwui/GammaFontRenderer.h14
-rw-r--r--libs/hwui/GradientCache.cpp8
-rw-r--r--libs/hwui/Image.cpp63
-rw-r--r--libs/hwui/Image.h67
-rw-r--r--libs/hwui/Layer.cpp86
-rw-r--r--libs/hwui/Layer.h48
-rw-r--r--libs/hwui/LayerCache.cpp4
-rw-r--r--libs/hwui/LayerRenderer.cpp27
-rw-r--r--libs/hwui/Matrix.cpp10
-rw-r--r--libs/hwui/Matrix.h38
-rw-r--r--libs/hwui/OpenGLRenderer.cpp732
-rw-r--r--libs/hwui/OpenGLRenderer.h158
-rw-r--r--libs/hwui/Patch.cpp202
-rw-r--r--libs/hwui/Patch.h59
-rw-r--r--libs/hwui/PatchCache.cpp266
-rw-r--r--libs/hwui/PatchCache.h125
-rw-r--r--libs/hwui/PathCache.cpp53
-rw-r--r--libs/hwui/PathCache.h4
-rw-r--r--libs/hwui/PathTessellator.cpp145
-rw-r--r--libs/hwui/PathTessellator.h35
-rw-r--r--libs/hwui/PixelBuffer.cpp24
-rw-r--r--libs/hwui/PixelBuffer.h21
-rw-r--r--libs/hwui/Program.cpp36
-rw-r--r--libs/hwui/Program.h31
-rw-r--r--libs/hwui/ProgramCache.cpp53
-rw-r--r--libs/hwui/Properties.h28
-rw-r--r--libs/hwui/Query.h152
-rw-r--r--libs/hwui/Rect.h44
-rw-r--r--libs/hwui/ResourceCache.cpp60
-rw-r--r--libs/hwui/ResourceCache.h11
-rw-r--r--libs/hwui/SkiaShader.cpp22
-rw-r--r--libs/hwui/SkiaShader.h19
-rw-r--r--libs/hwui/Stencil.cpp1
-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.cpp37
-rw-r--r--libs/hwui/TextureCache.h4
-rw-r--r--libs/hwui/UvMapper.h133
-rw-r--r--libs/hwui/Vertex.h26
-rw-r--r--libs/hwui/font/CacheTexture.cpp59
-rw-r--r--libs/hwui/font/CacheTexture.h19
-rw-r--r--libs/hwui/font/Font.cpp5
-rw-r--r--libs/hwui/font/Font.h1
-rw-r--r--libs/hwui/font/FontUtil.h3
-rw-r--r--libs/hwui/thread/TaskManager.cpp2
-rw-r--r--libs/hwui/thread/TaskManager.h2
68 files changed, 3922 insertions, 1587 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index a630ea1..411c133 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,26 @@ 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 libstlport
+ LOCAL_C_INCLUDES += \
+ $(intermediates) \
+ frameworks/rs/cpp \
+ frameworks/rs \
+ external/stlport/stlport \
+ bionic/ \
+ bionic/libstdc++/include
+ 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..eb8bb9f
--- /dev/null
+++ b/libs/hwui/AssetAtlas.cpp
@@ -0,0 +1,142 @@
+/*
+ * 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 "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;
+ }
+
+ mGenerationId++;
+}
+
+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);
+ texture->id = mTexture->id;
+ texture->blend = !bitmap->isOpaque();
+ texture->width = bitmap->width();
+ texture->height = bitmap->height();
+
+ Entry* entry = new Entry(bitmap, x, y, rotated, texture, mapper, *this);
+ 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..a28efc6
--- /dev/null
+++ b/libs/hwui/AssetAtlas.h
@@ -0,0 +1,195 @@
+/*
+ * 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;
+
+ /**
+ * Unique identifier used to merge bitmaps and 9-patches stored
+ * in the atlas.
+ */
+ const void* getMergeId() const {
+ return texture->blend ? &atlas.mBlendKey : &atlas.mOpaqueKey;
+ }
+
+ 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), mGenerationId(0),
+ mBlendKey(true), mOpaqueKey(false) { }
+ ~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;
+
+ /**
+ * Returns the current generation id of the atlas.
+ */
+ uint32_t getGenerationId() const {
+ return mGenerationId;
+ }
+
+private:
+ void createEntries(Caches& caches, int* map, int count);
+
+ Texture* mTexture;
+ Image* mImage;
+
+ uint32_t mGenerationId;
+
+ const bool mBlendKey;
+ const bool mOpaqueKey;
+
+ 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..f8d3589 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.hasPixelBufferObjects()) {
+ 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;
@@ -145,11 +166,16 @@ bool Caches::initProperties() {
debugLayersUpdates = false;
}
+ debugOverdraw = false;
if (property_get(PROPERTY_DEBUG_OVERDRAW, property, NULL) > 0) {
INIT_LOGD(" Overdraw debug enabled: %s", property);
- debugOverdraw = !strcmp(property, "true");
- } else {
- debugOverdraw = false;
+ if (!strcmp(property, "show")) {
+ debugOverdraw = true;
+ mOverdrawDebugColorSet = kColorSet_Default;
+ } else if (!strcmp(property, "show_deuteranomaly")) {
+ debugOverdraw = true;
+ mOverdrawDebugColorSet = kColorSet_Deuteranomaly;
+ }
}
// See Properties.h for valid values
@@ -191,8 +217,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 +227,12 @@ void Caches::terminate() {
programCache.clear();
currentProgram = NULL;
+ assetAtlas.terminate();
+
+ patchCache.clear();
+
+ clearGarbage();
+
mInitialized = false;
}
@@ -207,6 +240,16 @@ void Caches::terminate() {
// Debug
///////////////////////////////////////////////////////////////////////////////
+uint32_t Caches::getOverdrawColor(uint32_t amount) const {
+ static uint32_t sOverdrawColors[2][4] = {
+ { 0x2f0000ff, 0x2f00ff00, 0x3fff0000, 0x7fff0000 },
+ { 0x2f0000ff, 0x4fffff00, 0x5fff8ad8, 0x7fff0000 }
+ };
+ if (amount < 1) amount = 1;
+ if (amount > 4) amount = 4;
+ return sOverdrawColors[mOverdrawDebugColorSet][amount - 1];
+}
+
void Caches::dumpMemoryUsage() {
String8 stringLog;
dumpMemoryUsage(stringLog);
@@ -227,15 +270,19 @@ 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);
+ const uint32_t sizeA8 = fontRenderer->getFontRendererSize(i, GL_ALPHA);
+ const uint32_t sizeRGBA = fontRenderer->getFontRendererSize(i, GL_RGBA);
+ log.appendFormat(" FontRenderer %d A8 %8d / %8d\n", i, sizeA8, sizeA8);
+ log.appendFormat(" FontRenderer %d RGBA %8d / %8d\n", i, sizeRGBA, sizeRGBA);
+ log.appendFormat(" FontRenderer %d total %8d / %8d\n", i, sizeA8 + sizeRGBA,
+ sizeA8 + sizeRGBA);
}
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,8 +291,10 @@ 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);
+ total += fontRenderer->getFontRendererSize(i, GL_ALPHA);
+ total += fontRenderer->getFontRendererSize(i, GL_RGBA);
}
log.appendFormat("Total memory usage:\n");
@@ -259,6 +308,7 @@ void Caches::dumpMemoryUsage(String8 &log) {
void Caches::clearGarbage() {
textureCache.clearGarbage();
pathCache.clearGarbage();
+ patchCache.clearGarbage();
Vector<DisplayList*> displayLists;
Vector<Layer*> layers;
@@ -298,6 +348,11 @@ void Caches::deleteDisplayListDeferred(DisplayList* displayList) {
void Caches::flush(FlushMode mode) {
FLUSH_LOGD("Flushing caches (mode %d)", mode);
+ // We must stop tasks before clearing caches
+ if (mode > kFlushMode_Layers) {
+ tasks.stop();
+ }
+
switch (mode) {
case kFlushMode_Full:
textureCache.clear();
@@ -305,13 +360,13 @@ void Caches::flush(FlushMode mode) {
dropShadowCache.clear();
gradientCache.clear();
fontRenderer->clear();
+ fboCache.clear();
dither.clear();
// fall through
case kFlushMode_Moderate:
fontRenderer->flush();
textureCache.flush();
pathCache.clear();
- tasks.stop();
// fall through
case kFlushMode_Layers:
layerCache.clear();
@@ -357,6 +412,32 @@ bool Caches::bindIndicesBuffer(const GLuint buffer) {
return false;
}
+bool Caches::bindIndicesBuffer() {
+ if (!mMeshIndices) {
+ uint16_t* regionIndices = new uint16_t[gMaxNumberOfQuads * 6];
+ for (uint32_t i = 0; i < gMaxNumberOfQuads; 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, gMaxNumberOfQuads * 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 +522,50 @@ void Caches::activeTexture(GLuint textureUnit) {
}
}
+void Caches::resetActiveTexture() {
+ mTextureUnit = -1;
+}
+
+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
///////////////////////////////////////////////////////////////////////////////
@@ -545,28 +670,7 @@ void Caches::unregisterFunctors(uint32_t functorCount) {
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);
+ mRegionMesh = new TextureVertex[gMaxNumberOfQuads * 4];
}
return mRegionMesh;
diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h
index 91b938b..282aee9 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,9 +55,11 @@ 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
+// Maximum number of quads that pre-allocated meshes can draw
+static const uint32_t gMaxNumberOfQuads = 2048;
// Generates simple and textured vertices
#define FV(x, y, u, v) { { x, y }, { u, v } }
@@ -74,6 +81,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 +121,7 @@ public:
/**
* Initialize caches.
*/
- void init();
+ bool init();
/**
* Initialize global system properties.
@@ -142,6 +150,12 @@ public:
}
/**
+ * Returns a non-premultiplied ARGB color for the specified
+ * amount of overdraw (1 for 1x, 2 for 2x, etc.)
+ */
+ uint32_t getOverdrawColor(uint32_t amount) const;
+
+ /**
* Call this on each frame to ensure that garbage is deleted from
* GPU memory.
*/
@@ -172,6 +186,11 @@ public:
*/
bool unbindMeshBuffer();
+ /**
+ * Binds a global indices buffer that can draw up to
+ * gMaxNumberOfQuads quads.
+ */
+ bool bindIndicesBuffer();
bool bindIndicesBuffer(const GLuint buffer);
bool unbindIndicesBuffer();
@@ -213,6 +232,38 @@ public:
void activeTexture(GLuint textureUnit);
/**
+ * Invalidate the cached value of the active texture unit.
+ */
+ void resetActiveTexture();
+
+ /**
+ * 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 +341,10 @@ public:
Dither dither;
Stencil stencil;
+ AssetAtlas assetAtlas;
+
+ bool gpuPixelBuffersEnabled;
+
// Debug methods
PFNGLINSERTEVENTMARKEREXTPROC eventMark;
PFNGLPUSHGROUPMARKEREXTPROC startMark;
@@ -299,9 +354,15 @@ public:
PFNGLGETOBJECTLABELEXTPROC getLabel;
private:
+ enum OverdrawColorSet {
+ kColorSet_Default = 0,
+ kColorSet_Deuteranomaly
+ };
+
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 +397,9 @@ private:
// Used to render layers
TextureVertex* mRegionMesh;
- GLuint mRegionMeshIndices;
+
+ // Global index buffer
+ GLuint mMeshIndices;
mutable Mutex mGarbageLock;
Vector<Layer*> mLayerGarbage;
@@ -346,6 +409,10 @@ private:
bool mInitialized;
uint32_t mFunctorsCount;
+
+ GLuint mBoundTextures[REQUIRED_TEXTURE_UNITS_COUNT];
+
+ OverdrawColorSet mOverdrawDebugColorSet;
}; // 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..7eb7028 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -20,6 +20,8 @@
#include <SkCanvas.h>
#include <utils/Trace.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
#include "Caches.h"
#include "Debug.h"
@@ -51,32 +53,36 @@ class Batch {
public:
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) = 0;
virtual ~Batch() {}
+ virtual bool purelyDrawBatch() { return false; }
+ virtual bool coversBounds(const Rect& bounds) { return false; }
};
class DrawBatch : public Batch {
public:
- DrawBatch(int batchId, mergeid_t mergeId) : mBatchId(batchId), mMergeId(mergeId) {
+ DrawBatch(const DeferInfo& deferInfo) : mAllOpsOpaque(true),
+ mBatchId(deferInfo.batchId), mMergeId(deferInfo.mergeId) {
mOps.clear();
}
virtual ~DrawBatch() { mOps.clear(); }
- void add(DrawOp* op) {
+ virtual void add(DrawOp* op, const DeferredDisplayState* state, bool opaqueOverBounds) {
// NOTE: ignore empty bounds special case, since we don't merge across those ops
- mBounds.unionWith(op->state.mBounds);
- mOps.add(op);
+ mBounds.unionWith(state->mBounds);
+ mAllOpsOpaque &= opaqueOverBounds;
+ mOps.add(OpStatePair(op, state));
}
- bool intersects(Rect& rect) {
+ bool intersects(const Rect& rect) {
if (!rect.intersects(mBounds)) return false;
for (unsigned int i = 0; i < mOps.size(); i++) {
- if (rect.intersects(mOps[i]->state.mBounds)) {
+ if (rect.intersects(mOps[i].state->mBounds)) {
#if DEBUG_DEFER
- DEFER_LOGD("op intersects with op %p with bounds %f %f %f %f:", mOps[i],
- mOps[i]->state.mBounds.left, mOps[i]->state.mBounds.top,
- mOps[i]->state.mBounds.right, mOps[i]->state.mBounds.bottom);
- mOps[i]->output(2);
+ DEFER_LOGD("op intersects with op %p with bounds %f %f %f %f:", mOps[i].op,
+ mOps[i].state->mBounds.left, mOps[i].state->mBounds.top,
+ mOps[i].state->mBounds.right, mOps[i].state->mBounds.bottom);
+ mOps[i].op->output(2);
#endif
return true;
}
@@ -85,15 +91,15 @@ 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();
for (unsigned int i = 0; i < mOps.size(); i++) {
- DrawOp* op = mOps[i];
-
- renderer.restoreDisplayState(op->state);
+ DrawOp* op = mOps[i].op;
+ const DeferredDisplayState* state = mOps[i].state;
+ renderer.restoreDisplayState(*state);
#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
renderer.eventMark(op->name());
@@ -102,7 +108,7 @@ public:
status |= op->applyDraw(renderer, dirty);
#if DEBUG_MERGE_BEHAVIOR
- Rect& bounds = mOps[i]->state.mBounds;
+ const Rect& bounds = state->mBounds;
int batchColor = 0x1f000000;
if (getBatchId() & 0x1) batchColor |= 0x0000ff;
if (getBatchId() & 0x2) batchColor |= 0x00ff00;
@@ -114,14 +120,28 @@ public:
return status;
}
+ virtual bool purelyDrawBatch() { return true; }
+
+ virtual bool coversBounds(const Rect& bounds) {
+ if (CC_LIKELY(!mAllOpsOpaque || !mBounds.contains(bounds) || count() == 1)) return false;
+
+ Region uncovered(android::Rect(bounds.left, bounds.top, bounds.right, bounds.bottom));
+ for (unsigned int i = 0; i < mOps.size(); i++) {
+ const Rect &r = mOps[i].state->mBounds;
+ uncovered.subtractSelf(android::Rect(r.left, r.top, r.right, r.bottom));
+ }
+ return uncovered.isEmpty();
+ }
+
inline int getBatchId() const { return mBatchId; }
inline mergeid_t getMergeId() const { return mMergeId; }
inline int count() const { return mOps.size(); }
protected:
- Vector<DrawOp*> mOps;
- Rect mBounds;
+ Vector<OpStatePair> mOps;
+ Rect mBounds; // union of bounds of contained ops
private:
+ bool mAllOpsOpaque;
int mBatchId;
mergeid_t mMergeId;
};
@@ -132,39 +152,77 @@ private:
class MergingDrawBatch : public DrawBatch {
public:
- MergingDrawBatch(int batchId, mergeid_t mergeId) : DrawBatch(batchId, mergeId) {}
+ MergingDrawBatch(DeferInfo& deferInfo, int width, int height) :
+ DrawBatch(deferInfo), mClipRect(width, height),
+ mClipSideFlags(kClipSide_None) {}
+
+ /*
+ * Helper for determining if a new op can merge with a MergingDrawBatch based on their bounds
+ * and clip side flags. Positive bounds delta means new bounds fit in old.
+ */
+ static inline bool checkSide(const int currentFlags, const int newFlags, const int side,
+ float boundsDelta) {
+ bool currentClipExists = currentFlags & side;
+ bool newClipExists = newFlags & side;
+
+ // if current is clipped, we must be able to fit new bounds in current
+ if (boundsDelta > 0 && currentClipExists) return false;
+
+ // if new is clipped, we must be able to fit current bounds in new
+ if (boundsDelta < 0 && newClipExists) return false;
+
+ return true;
+ }
/*
* Checks if a (mergeable) op can be merged into this batch
*
* If true, the op's multiDraw must be guaranteed to handle both ops simultaneously, so it is
* important to consider all paint attributes used in the draw calls in deciding both a) if an
- * op tries to merge at all, and b) if the op
+ * op tries to merge at all, and b) if the op can merge with another set of ops
*
* False positives can lead to information from the paints of subsequent merged operations being
* 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;
-
+ bool canMergeWith(const DrawOp* op, const DeferredDisplayState* state) {
bool isTextBatch = getBatchId() == DeferredDisplayList::kOpBatch_Text ||
getBatchId() == DeferredDisplayList::kOpBatch_ColorText;
// Overlapping other operations is only allowed for text without shadow. For other ops,
// multiDraw isn't guaranteed to overdraw correctly
- if (!isTextBatch || op->state.mDrawModifiers.mHasShadow) {
- if (intersects(op->state.mBounds)) return false;
+ if (!isTextBatch || state->mDrawModifiers.mHasShadow) {
+ if (intersects(state->mBounds)) return false;
}
+ const DeferredDisplayState* lhs = state;
+ const DeferredDisplayState* rhs = mOps[0].state;
- const DeferredDisplayState& lhs = op->state;
- const DeferredDisplayState& rhs = mOps[0]->state;
+ if (NEQ_FALPHA(lhs->mAlpha, rhs->mAlpha)) return false;
- if (NEQ_FALPHA(lhs.mAlpha, rhs.mAlpha)) return false;
+ /* Clipping compatibility check
+ *
+ * Exploits the fact that if a op or batch is clipped on a side, its bounds will equal its
+ * clip for that side.
+ */
+ const int currentFlags = mClipSideFlags;
+ const int newFlags = state->mClipSideFlags;
+ if (currentFlags != kClipSide_None || newFlags != kClipSide_None) {
+ const Rect& opBounds = state->mBounds;
+ float boundsDelta = mBounds.left - opBounds.left;
+ if (!checkSide(currentFlags, newFlags, kClipSide_Left, boundsDelta)) return false;
+ boundsDelta = mBounds.top - opBounds.top;
+ if (!checkSide(currentFlags, newFlags, kClipSide_Top, boundsDelta)) return false;
+
+ // right and bottom delta calculation reversed to account for direction
+ boundsDelta = opBounds.right - mBounds.right;
+ if (!checkSide(currentFlags, newFlags, kClipSide_Right, boundsDelta)) return false;
+ boundsDelta = opBounds.bottom - mBounds.bottom;
+ if (!checkSide(currentFlags, newFlags, kClipSide_Bottom, boundsDelta)) return false;
+ }
// if paints are equal, then modifiers + paint attribs don't need to be compared
- if (op->mPaint == mOps[0]->mPaint) return true;
+ if (op->mPaint == mOps[0].op->mPaint) return true;
- if (op->getPaintAlpha() != mOps[0]->getPaintAlpha()) return false;
+ if (op->getPaintAlpha() != mOps[0].op->getPaintAlpha()) return false;
/* Draw Modifiers compatibility check
*
@@ -178,9 +236,8 @@ public:
*
* These ignore cases prevent us from simply memcmp'ing the drawModifiers
*/
-
- const DrawModifiers& lhsMod = lhs.mDrawModifiers;
- const DrawModifiers& rhsMod = rhs.mDrawModifiers;
+ const DrawModifiers& lhsMod = lhs->mDrawModifiers;
+ const DrawModifiers& rhsMod = rhs->mDrawModifiers;
if (lhsMod.mShader != rhsMod.mShader) return false;
if (lhsMod.mColorFilter != rhsMod.mColorFilter) return false;
@@ -192,17 +249,37 @@ public:
return true;
}
+ virtual void add(DrawOp* op, const DeferredDisplayState* state, bool opaqueOverBounds) {
+ DrawBatch::add(op, state, opaqueOverBounds);
+
+ const int newClipSideFlags = state->mClipSideFlags;
+ mClipSideFlags |= newClipSideFlags;
+ if (newClipSideFlags & kClipSide_Left) mClipRect.left = state->mClip.left;
+ if (newClipSideFlags & kClipSide_Top) mClipRect.top = state->mClip.top;
+ if (newClipSideFlags & kClipSide_Right) mClipRect.right = state->mClip.right;
+ if (newClipSideFlags & kClipSide_Bottom) mClipRect.bottom = state->mClip.bottom;
+ }
+
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(), getBatchId(), getMergeId());
+ DEFER_LOGD("%d replaying MergingDrawBatch %p, with %d ops,"
+ " clip flags %x (batch id %x, merge id %p)",
+ index, this, mOps.size(), mClipSideFlags, getBatchId(), getMergeId());
if (mOps.size() == 1) {
- return DrawBatch::replay(renderer, dirty, false);
+ return DrawBatch::replay(renderer, dirty, -1);
}
- DrawOp* op = mOps[0];
+ // clipping in the merged case is done ahead of time since all ops share the clip (if any)
+ renderer.setupMergedMultiDraw(mClipSideFlags ? &mClipRect : NULL);
+
+ DrawOp* op = mOps[0].op;
DisplayListLogBuffer& buffer = DisplayListLogBuffer::getInstance();
buffer.writeCommand(0, "multiDraw");
buffer.writeCommand(1, op->name());
+
+#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
+ renderer.eventMark("multiDraw");
+ renderer.eventMark(op->name());
+#endif
status_t status = op->multiDraw(renderer, dirty, mOps, mBounds);
#if DEBUG_MERGE_BEHAVIOR
@@ -211,16 +288,25 @@ public:
#endif
return status;
}
+
+private:
+ /*
+ * Contains the effective clip rect shared by all merged ops. Initialized to the layer viewport,
+ * it will shrink if an op must be clipped on a certain side. The clipped sides are reflected in
+ * mClipSideFlags.
+ */
+ Rect mClipRect;
+ int mClipSideFlags;
};
class StateOpBatch : public Batch {
public:
// creates a single operation batch
- StateOpBatch(StateOp* op) : mOp(op) {}
+ StateOpBatch(const StateOp* op, const DeferredDisplayState* state) : mOp(op), mState(state) {}
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
DEFER_LOGD("replaying state op batch %p", this);
- renderer.restoreDisplayState(mOp->state);
+ renderer.restoreDisplayState(*mState);
// use invalid save count because it won't be used at flush time - RestoreToCountOp is the
// only one to use it, and we don't use that class at flush time, instead calling
@@ -232,16 +318,18 @@ public:
private:
const StateOp* mOp;
+ const DeferredDisplayState* mState;
};
class RestoreToCountBatch : public Batch {
public:
- RestoreToCountBatch(StateOp* op, int restoreCount) : mOp(op), mRestoreCount(restoreCount) {}
+ RestoreToCountBatch(const StateOp* op, const DeferredDisplayState* state, int restoreCount) :
+ mOp(op), mState(state), mRestoreCount(restoreCount) {}
virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) {
DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount);
- renderer.restoreDisplayState(mOp->state);
+ renderer.restoreDisplayState(*mState);
renderer.restoreToCount(mRestoreCount);
return DrawGlInfo::kStatusDone;
}
@@ -249,6 +337,8 @@ public:
private:
// we use the state storage for the RestoreToCountOp, but don't replay the op itself
const StateOp* mOp;
+ const DeferredDisplayState* mState;
+
/*
* The count used here represents the flush() time saveCount. This is as opposed to the
* DisplayList record time, or defer() time values (which are RestoreToCountOp's mCount, and
@@ -294,6 +384,7 @@ void DeferredDisplayList::clear() {
mBatches.clear();
mSaveStack.clear();
mEarliestBatchIndex = 0;
+ mEarliestUnclearedIndex = 0;
}
/////////////////////////////////////////////////////////////////////////////////
@@ -398,22 +489,45 @@ void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* o
}
void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
- if (renderer.storeDisplayState(op->state, getDrawOpDeferFlags())) {
+ /* 1: op calculates local bounds */
+ DeferredDisplayState* const state = createState();
+ if (op->getLocalBounds(renderer.getDrawModifiers(), state->mBounds)) {
+ if (state->mBounds.isEmpty()) {
+ // valid empty bounds, don't bother deferring
+ tryRecycleState(state);
+ return;
+ }
+ } else {
+ state->mBounds.setEmpty();
+ }
+
+ /* 2: renderer calculates global bounds + stores state */
+ if (renderer.storeDisplayState(*state, getDrawOpDeferFlags())) {
+ tryRecycleState(state);
return; // quick rejected
}
- int batchId = kOpBatch_None;
- mergeid_t mergeId = (mergeid_t) -1;
- bool mergeable = op->onDefer(renderer, &batchId, &mergeId);
+ /* 3: ask op for defer info, given renderer state */
+ DeferInfo deferInfo;
+ op->onDefer(renderer, deferInfo, *state);
// complex clip has a complex set of expectations on the renderer state - for now, avoid taking
// the merge path in those cases
- mergeable &= !recordingComplexClip();
+ deferInfo.mergeable &= !recordingComplexClip();
+ deferInfo.opaqueOverBounds &= !recordingComplexClip() && mSaveStack.isEmpty();
+
+ if (CC_LIKELY(mAvoidOverdraw) && mBatches.size() &&
+ state->mClipSideFlags != kClipSide_ConservativeFull &&
+ deferInfo.opaqueOverBounds && state->mBounds.contains(mBounds)) {
+ // avoid overdraw by resetting drawing state + discarding drawing ops
+ discardDrawingBatches(mBatches.size() - 1);
+ resetBatchingState();
+ }
if (CC_UNLIKELY(renderer.getCaches().drawReorderDisabled)) {
// TODO: elegant way to reuse batches?
- DrawBatch* b = new DrawBatch(batchId, mergeId);
- b->add(op);
+ DrawBatch* b = new DrawBatch(deferInfo);
+ b->add(op, state, deferInfo.opaqueOverBounds);
mBatches.add(b);
return;
}
@@ -425,10 +539,10 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
// (eventually, should be similar shader)
int insertBatchIndex = mBatches.size();
if (!mBatches.isEmpty()) {
- if (op->state.mBounds.isEmpty()) {
+ if (state->mBounds.isEmpty()) {
// don't know the bounds for op, so add to last batch and start from scratch on next op
- DrawBatch* b = new DrawBatch(batchId, mergeId);
- b->add(op);
+ DrawBatch* b = new DrawBatch(deferInfo);
+ b->add(op, state, deferInfo.opaqueOverBounds);
mBatches.add(b);
resetBatchingState();
#if DEBUG_DEFER
@@ -438,19 +552,19 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
return;
}
- if (mergeable) {
+ if (deferInfo.mergeable) {
// Try to merge with any existing batch with same mergeId.
- if (mMergingBatches[batchId].get(mergeId, targetBatch)) {
- if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op)) {
+ if (mMergingBatches[deferInfo.batchId].get(deferInfo.mergeId, targetBatch)) {
+ if (!((MergingDrawBatch*) targetBatch)->canMergeWith(op, state)) {
targetBatch = NULL;
}
}
} else {
// join with similar, non-merging batch
- targetBatch = (DrawBatch*)mBatchLookup[batchId];
+ targetBatch = (DrawBatch*)mBatchLookup[deferInfo.batchId];
}
- if (targetBatch || mergeable) {
+ if (targetBatch || deferInfo.mergeable) {
// iterate back toward target to see if anything drawn since should overlap the new op
// if no target, merging ops still interate to find similar batch to insert after
for (int i = mBatches.size() - 1; i >= mEarliestBatchIndex; i--) {
@@ -459,19 +573,19 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
if (overBatch == targetBatch) break;
// TODO: also consider shader shared between batch types
- if (batchId == overBatch->getBatchId()) {
+ if (deferInfo.batchId == overBatch->getBatchId()) {
insertBatchIndex = i + 1;
if (!targetBatch) break; // found insert position, quit
}
- if (overBatch->intersects(op->state.mBounds)) {
+ if (overBatch->intersects(state->mBounds)) {
// NOTE: it may be possible to optimize for special cases where two operations
// of the same batch/paint could swap order, such as with a non-mergeable
// (clipped) and a mergeable text operation
targetBatch = NULL;
#if DEBUG_DEFER
- DEFER_LOGD("op couldn't join batch %d, was intersected by batch %d",
- targetIndex, i);
+ DEFER_LOGD("op couldn't join batch %p, was intersected by batch %d",
+ targetBatch, i);
op->output(2);
#endif
break;
@@ -481,27 +595,30 @@ void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
}
if (!targetBatch) {
- if (mergeable) {
- targetBatch = new MergingDrawBatch(batchId, mergeId);
- mMergingBatches[batchId].put(mergeId, targetBatch);
+ if (deferInfo.mergeable) {
+ targetBatch = new MergingDrawBatch(deferInfo,
+ renderer.getViewportWidth(), renderer.getViewportHeight());
+ mMergingBatches[deferInfo.batchId].put(deferInfo.mergeId, targetBatch);
} else {
- targetBatch = new DrawBatch(batchId, mergeId);
- mBatchLookup[batchId] = targetBatch;
- DEFER_LOGD("creating Batch %p, bid %x, at %d",
- targetBatch, batchId, insertBatchIndex);
+ targetBatch = new DrawBatch(deferInfo);
+ mBatchLookup[deferInfo.batchId] = targetBatch;
}
+ DEFER_LOGD("creating %singBatch %p, bid %x, at %d",
+ deferInfo.mergeable ? "Merg" : "Draw",
+ targetBatch, deferInfo.batchId, insertBatchIndex);
mBatches.insertAt(targetBatch, insertBatchIndex);
}
- targetBatch->add(op);
+ targetBatch->add(op, state, deferInfo.opaqueOverBounds);
}
void DeferredDisplayList::storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op) {
DEFER_LOGD("%p adding state op barrier at pos %d", this, mBatches.size());
- renderer.storeDisplayState(op->state, getStateOpDeferFlags());
- mBatches.add(new StateOpBatch(op));
+ DeferredDisplayState* state = createState();
+ renderer.storeDisplayState(*state, getStateOpDeferFlags());
+ mBatches.add(new StateOpBatch(op, state));
resetBatchingState();
}
@@ -512,8 +629,9 @@ void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, S
// store displayState for the restore operation, as it may be associated with a saveLayer that
// doesn't have kClip_SaveFlag set
- renderer.storeDisplayState(op->state, getStateOpDeferFlags());
- mBatches.add(new RestoreToCountBatch(op, newSaveCount));
+ DeferredDisplayState* state = createState();
+ renderer.storeDisplayState(*state, getStateOpDeferFlags());
+ mBatches.add(new RestoreToCountBatch(op, state, newSaveCount));
resetBatchingState();
}
@@ -526,7 +644,9 @@ static status_t replayBatchList(const Vector<Batch*>& batchList,
status_t status = DrawGlInfo::kStatusDone;
for (unsigned int i = 0; i < batchList.size(); i++) {
- status |= batchList[i]->replay(renderer, dirty, i);
+ if (batchList[i]) {
+ status |= batchList[i]->replay(renderer, dirty, i);
+ }
}
DEFER_LOGD("--flushed, drew %d batches", batchList.size());
return status;
@@ -548,6 +668,13 @@ status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
DrawModifiers restoreDrawModifiers = renderer.getDrawModifiers();
renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+ if (CC_LIKELY(mAvoidOverdraw)) {
+ for (unsigned int i = 1; i < mBatches.size(); i++) {
+ if (mBatches[i] && mBatches[i]->coversBounds(mBounds)) {
+ discardDrawingBatches(i - 1);
+ }
+ }
+ }
// NOTE: depth of the save stack at this point, before playback, should be reflected in
// FLUSH_SAVE_STACK_DEPTH, so that save/restores match up correctly
status |= replayBatchList(mBatches, renderer, dirty);
@@ -560,5 +687,17 @@ status_t DeferredDisplayList::flush(OpenGLRenderer& renderer, Rect& dirty) {
return status;
}
+void DeferredDisplayList::discardDrawingBatches(const unsigned int maxIndex) {
+ for (unsigned int i = mEarliestUnclearedIndex; i <= maxIndex; i++) {
+ // leave deferred state ops alone for simplicity (empty save restore pairs may now exist)
+ if (mBatches[i] && mBatches[i]->purelyDrawBatch()) {
+ DrawBatch* b = (DrawBatch*) mBatches[i];
+ delete mBatches[i];
+ mBatches.replaceAt(NULL, i);
+ }
+ }
+ mEarliestUnclearedIndex = maxIndex + 1;
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 9782c1c..3dcbd0b 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -18,11 +18,13 @@
#define ANDROID_HWUI_DEFERRED_DISPLAY_LIST_H
#include <utils/Errors.h>
+#include <utils/LinearAllocator.h>
#include <utils/Vector.h>
+#include <utils/TinyHashMap.h>
#include "Matrix.h"
+#include "OpenGLRenderer.h"
#include "Rect.h"
-#include "utils/TinyHashMap.h"
class SkBitmap;
@@ -34,18 +36,56 @@ class DrawOp;
class SaveOp;
class SaveLayerOp;
class StateOp;
+
+class DeferredDisplayState;
class OpenGLRenderer;
class Batch;
class DrawBatch;
class MergingDrawBatch;
-typedef void* mergeid_t;
+typedef const void* mergeid_t;
+
+class DeferredDisplayState {
+public:
+ /** static void* operator new(size_t size); PURPOSELY OMITTED **/
+ static void* operator new(size_t size, LinearAllocator& allocator) {
+ return allocator.alloc(size);
+ }
+
+ // 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;
+ int mClipSideFlags; // specifies which sides of the bounds are clipped, unclipped if cleared
+ bool mClipped;
+ mat4 mMatrix;
+ DrawModifiers mDrawModifiers;
+ float mAlpha;
+};
+
+class OpStatePair {
+public:
+ OpStatePair()
+ : op(NULL), state(NULL) {}
+ OpStatePair(DrawOp* newOp, const DeferredDisplayState* newState)
+ : op(newOp), state(newState) {}
+ OpStatePair(const OpStatePair& other)
+ : op(other.op), state(other.state) {}
+ DrawOp* op;
+ const DeferredDisplayState* state;
+};
class DeferredDisplayList {
public:
- DeferredDisplayList() { clear(); }
+ DeferredDisplayList(const Rect& bounds, bool avoidOverdraw = true) :
+ mBounds(bounds), mAvoidOverdraw(avoidOverdraw) {
+ clear();
+ }
~DeferredDisplayList() { clear(); }
+ void reset(const Rect& bounds) { mBounds.set(bounds); }
enum OpBatchId {
kOpBatch_None = 0, // Don't batch
@@ -75,11 +115,19 @@ public:
/**
* Add a draw op into the DeferredDisplayList, reordering as needed (for performance) if
- * disallowReorder is false, respecting draw order when overlaps occur
+ * disallowReorder is false, respecting draw order when overlaps occur.
*/
void addDrawOp(OpenGLRenderer& renderer, DrawOp* op);
private:
+ DeferredDisplayState* createState() {
+ return new (mAllocator) DeferredDisplayState();
+ }
+
+ void tryRecycleState(DeferredDisplayState* state) {
+ mAllocator.rewindIfLastAlloc(state, sizeof(DeferredDisplayState));
+ }
+
/**
* Resets the batching back-pointers, creating a barrier in the operation stream so that no ops
* added in the future will be inserted into a batch that already exist.
@@ -96,6 +144,12 @@ private:
int getStateOpDeferFlags() const;
int getDrawOpDeferFlags() const;
+ void discardDrawingBatches(const unsigned int maxIndex);
+
+ // layer space bounds of rendering
+ Rect mBounds;
+ const bool mAvoidOverdraw;
+
/**
* At defer time, stores the *defer time* savecount of save/saveLayer ops that were deferred, so
* that when an associated restoreToCount is deferred, it can be recorded as a
@@ -112,12 +166,35 @@ private:
// Points to the index after the most recent barrier
int mEarliestBatchIndex;
+ // Points to the first index that may contain a pure drawing batch
+ int mEarliestUnclearedIndex;
+
/**
* Maps the mergeid_t returned by an op's getMergeId() to the most recently seen
* MergingDrawBatch of that id. These ids are unique per draw type and guaranteed to not
* collide, which avoids the need to resolve mergeid collisions.
*/
TinyHashMap<mergeid_t, DrawBatch*> mMergingBatches[kOpBatch_Count];
+
+ LinearAllocator mAllocator;
+};
+
+/**
+ * Struct containing information that instructs the defer
+ */
+struct DeferInfo {
+public:
+ DeferInfo() :
+ batchId(DeferredDisplayList::kOpBatch_None),
+ mergeId((mergeid_t) -1),
+ mergeable(false),
+ opaqueOverBounds(false) {
+ };
+
+ int batchId;
+ mergeid_t mergeId;
+ bool mergeable;
+ bool opaqueOverBounds; // opaque over bounds in DeferredDisplayState - can skip ops below
};
}; // namespace uirenderer
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 1cbd531..bb6526e 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -44,13 +44,14 @@ void DisplayList::outputLogBuffer(int fd) {
}
DisplayList::DisplayList(const DisplayListRenderer& recorder) :
- mTransformMatrix(NULL), mTransformCamera(NULL), mTransformMatrix3D(NULL),
+ mDestroyed(false), mTransformMatrix(NULL), mTransformCamera(NULL), mTransformMatrix3D(NULL),
mStaticMatrix(NULL), mAnimationMatrix(NULL) {
initFromDisplayListRenderer(recorder);
}
DisplayList::~DisplayList() {
+ mDestroyed = true;
clearResources();
}
@@ -63,7 +64,6 @@ void DisplayList::destroyDisplayListDeferred(DisplayList* displayList) {
void DisplayList::clearResources() {
mDisplayListData = NULL;
- mSize = 0; // TODO: shouldn't be needed, WAR possible use after delete
mClipRectOp = NULL;
mSaveLayerOp = NULL;
@@ -100,6 +100,10 @@ void DisplayList::clearResources() {
caches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i));
}
+ for (size_t i = 0; i < mPatchResources.size(); i++) {
+ caches.resourceCache.decrementRefcountLocked(mPatchResources.itemAt(i));
+ }
+
for (size_t i = 0; i < mShaders.size(); i++) {
caches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i));
caches.resourceCache.destructorLocked(mShaders.itemAt(i));
@@ -134,6 +138,7 @@ void DisplayList::clearResources() {
mBitmapResources.clear();
mOwnedBitmapResources.clear();
mFilterResources.clear();
+ mPatchResources.clear();
mShaders.clear();
mSourcePaths.clear();
mPaints.clear();
@@ -169,6 +174,10 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde
mSaveLayerOp = new (alloc) SaveLayerOp();
mSaveOp = new (alloc) SaveOp();
mRestoreToCountOp = new (alloc) RestoreToCountOp();
+ if (CC_UNLIKELY(!mSaveOp)) { // temporary debug logging
+ ALOGW("Error: %s's SaveOp not allocated, size %d", getName(), mSize);
+ CRASH();
+ }
mFunctorCount = recorder.getFunctorCount();
@@ -197,6 +206,13 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde
caches.resourceCache.incrementRefcountLocked(resource);
}
+ const Vector<Res_png_9patch*>& patchResources = recorder.getPatchResources();
+ for (size_t i = 0; i < patchResources.size(); i++) {
+ Res_png_9patch* resource = patchResources.itemAt(i);
+ mPatchResources.add(resource);
+ caches.resourceCache.incrementRefcountLocked(resource);
+ }
+
const Vector<SkiaShader*>& shaders = recorder.getShaders();
for (size_t i = 0; i < shaders.size(); i++) {
SkiaShader* resource = shaders.itemAt(i);
@@ -342,7 +358,7 @@ void DisplayList::outputViewProperties(const int level) {
}
if (mAnimationMatrix) {
ALOGD("%*sConcatMatrix (animation) %p: " MATRIX_STRING,
- level * 2, "", mAnimationMatrix, MATRIX_ARGS(mStaticMatrix));
+ level * 2, "", mAnimationMatrix, MATRIX_ARGS(mAnimationMatrix));
}
if (mMatrixFlags != 0) {
if (mMatrixFlags == TRANSLATION) {
@@ -352,6 +368,8 @@ void DisplayList::outputViewProperties(const int level) {
level * 2, "", mTransformMatrix, MATRIX_ARGS(mTransformMatrix));
}
}
+
+ bool clipToBoundsNeeded = mCaching ? false : mClipToBounds;
if (mAlpha < 1) {
if (mCaching) {
ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mAlpha);
@@ -359,15 +377,16 @@ void DisplayList::outputViewProperties(const int level) {
ALOGD("%*sScaleAlpha %.2f", level * 2, "", mAlpha);
} else {
int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
- if (mClipToBounds) {
+ if (clipToBoundsNeeded) {
flags |= SkCanvas::kClipToLayer_SaveFlag;
+ clipToBoundsNeeded = false; // clipping done by save layer
}
ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
(float) 0, (float) 0, (float) mRight - mLeft, (float) mBottom - mTop,
(int)(mAlpha * 255), flags);
}
}
- if (mClipToBounds && !mCaching) {
+ if (clipToBoundsNeeded) {
ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
(float) mRight - mLeft, (float) mBottom - mTop);
}
@@ -402,6 +421,7 @@ void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler,
renderer.concatMatrix(mTransformMatrix);
}
}
+ bool clipToBoundsNeeded = mCaching ? false : mClipToBounds;
if (mAlpha < 1) {
if (mCaching) {
renderer.setOverrideLayerAlpha(mAlpha);
@@ -412,15 +432,16 @@ void DisplayList::setViewProperties(OpenGLRenderer& renderer, T& handler,
// have to pass it into this call. In fact, this information might be in the
// location/size info that we store with the new native transform data.
int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
- if (mClipToBounds) {
+ if (clipToBoundsNeeded) {
saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
+ clipToBoundsNeeded = false; // clipping done by saveLayer
}
handler(mSaveLayerOp->reinit(0, 0, mRight - mLeft, mBottom - mTop,
mAlpha * 255, SkXfermode::kSrcOver_Mode, saveFlags), PROPERTY_SAVECOUNT,
mClipToBounds);
}
}
- if (mClipToBounds && !mCaching) {
+ if (clipToBoundsNeeded) {
handler(mClipRectOp->reinit(0, 0, mRight - mLeft, mBottom - mTop, SkRegion::kIntersect_Op),
PROPERTY_SAVECOUNT, mClipToBounds);
}
@@ -480,7 +501,11 @@ void DisplayList::replay(ReplayStateStruct& replayStruct, const int level) {
*/
template <class T>
void DisplayList::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
- if (mSize == 0 || mAlpha <= 0 || CC_UNLIKELY(!mSaveOp)) { // TODO: shouldn't need mSaveOp check
+ if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
+ ALOGW("Error: %s is drawing after destruction, size %d", getName(), mSize);
+ CRASH();
+ }
+ if (mSize == 0 || mAlpha <= 0) {
DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
return;
}
@@ -514,6 +539,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..1cd5f1c 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -26,13 +26,15 @@
#include <private/hwui/DrawGlInfo.h>
+#include <utils/LinearAllocator.h>
#include <utils/RefBase.h>
#include <utils/SortedVector.h>
#include <utils/String8.h>
#include <utils/Vector.h>
+
#include <cutils/compiler.h>
-#include "utils/LinearAllocator.h"
+#include <androidfw/ResourceTypes.h>
#include "Debug.h"
@@ -111,7 +113,6 @@ public:
void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false);
-
void defer(DeferStateStruct& deferStruct, const int level);
void replay(ReplayStateStruct& replayStruct, const int level);
@@ -129,7 +130,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);
+ }
}
}
@@ -479,6 +485,7 @@ private:
Vector<SkBitmap*> mBitmapResources;
Vector<SkBitmap*> mOwnedBitmapResources;
Vector<SkiaColorFilter*> mFilterResources;
+ Vector<Res_png_9patch*> mPatchResources;
Vector<SkPaint*> mPaints;
Vector<SkPath*> mPaths;
@@ -496,6 +503,7 @@ private:
uint32_t mFunctorCount;
String8 mName;
+ bool mDestroyed; // used for debugging crash, TODO: remove once invalid state crash fixed
// View properties
bool mClipToBounds;
@@ -525,11 +533,11 @@ private:
* an alpha causes a SaveLayerAlpha to occur). These operations point into mDisplayListData's
* allocation, or null if uninitialized.
*
- * These are initialized (via friend constructors) when a displayList is issued in either replay
- * or deferred mode. If replaying, the ops are not used until the next frame. If deferring, the
- * ops may be stored in the DeferredDisplayList to be played back a second time.
+ * These are initialized (via friend re-constructors) when a displayList is issued in either
+ * replay or deferred mode. If replaying, the ops are not used until the next frame. If
+ * deferring, the ops may be stored in the DeferredDisplayList to be played back a second time.
*
- * They should be used at most once per frame (one call to iterate)
+ * They should be used at most once per frame (one call to 'iterate') to avoid overwriting data
*/
ClipRectOp* mClipRectOp;
SaveLayerOp* mSaveLayerOp;
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index a0290e3..326805a 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -26,26 +26,19 @@
#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 { \
- *(int *)(uintptr_t)0xbbadbeef = 0; \
+ *(int *)(uintptr_t) 0xbbadbeef = 0; \
((void(*)())0)(); /* More reliable, but doesn't say BBADBEEF */ \
} while(false)
-#define MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]"
-#define MATRIX_ARGS(m) \
- m->get(0), m->get(1), m->get(2), \
- m->get(3), m->get(4), m->get(5), \
- m->get(6), m->get(7), m->get(8)
-#define RECT_STRING "%.2f %.2f %.2f %.2f"
-#define RECT_ARGS(r) \
- r.left, r.top, r.right, r.bottom
-
// Use OP_LOG for logging with arglist, OP_LOGS if just printing char*
-#define OP_LOGS(s) OP_LOG("%s", s)
+#define OP_LOGS(s) OP_LOG("%s", (s))
#define OP_LOG(s, ...) ALOGD( "%*s" s, level * 2, "", __VA_ARGS__ )
namespace android {
@@ -84,20 +77,11 @@ public:
virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
bool useQuickReject) = 0;
- virtual void output(int level, uint32_t logFlags = 0) = 0;
+ virtual void output(int level, uint32_t logFlags = 0) const = 0;
// NOTE: it would be nice to declare constants and overriding the implementation in each op to
// point at the constants, but that seems to require a .cpp file
virtual const char* name() = 0;
-
- /**
- * Stores the relevant canvas state of the object between deferral and replay (if the canvas
- * state supports being stored) See OpenGLRenderer::simpleClipAndState()
- *
- * TODO: don't reserve space for StateOps that won't be deferred
- */
- DeferredDisplayState state;
-
};
class StateOp : public DisplayListOp {
@@ -136,11 +120,6 @@ public:
return;
}
- if (!getLocalBounds(state.mBounds)) {
- // empty bounds signify bounds can't be calculated
- state.mBounds.setEmpty();
- }
-
deferStruct.mDeferredList.addDrawOp(deferStruct.mRenderer, this);
}
@@ -163,16 +142,16 @@ public:
* reducing which operations are tagged as mergeable.
*/
virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
- const Vector<DrawOp*>& ops, const Rect& bounds) {
+ const Vector<OpStatePair>& ops, const Rect& bounds) {
status_t status = DrawGlInfo::kStatusDone;
for (unsigned int i = 0; i < ops.size(); i++) {
- renderer.restoreDisplayState(ops[i]->state);
- status |= ops[i]->applyDraw(renderer, dirty);
+ renderer.restoreDisplayState(*(ops[i].state), true);
+ status |= ops[i].op->applyDraw(renderer, dirty);
}
return status;
}
- /*
+ /**
* When this method is invoked the state field is initialized to have the
* final rendering state. We can thus use it to process data as it will be
* used at draw time.
@@ -180,21 +159,25 @@ public:
* Additionally, this method allows subclasses to provide defer-time preferences for batching
* and merging.
*
- * Return true if the op can merge with others of its kind (such subclasses should implement
- * multiDraw)
+ * if a subclass can set deferInfo.mergeable to true, it should implement multiDraw()
*/
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {}
+
+ /**
+ * Query the conservative, local bounds (unmapped) bounds of the op.
+ *
+ * returns true if bounds exist
+ */
+ virtual bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) {
return false;
}
- // returns true if bounds exist
- virtual bool getLocalBounds(Rect& localBounds) { return false; }
-
// TODO: better refine localbounds usage
void setQuickRejected(bool quickRejected) { mQuickRejected = quickRejected; }
bool getQuickRejected() { return mQuickRejected; }
- inline int getPaintAlpha() {
+ inline int getPaintAlpha() const {
return OpenGLRenderer::getAlphaDirect(mPaint);
}
@@ -209,6 +192,23 @@ protected:
return renderer.filterPaint(mPaint);
}
+ // Helper method for determining op opaqueness. Assumes op fills its bounds in local
+ // coordinates, and that paint's alpha is used
+ inline bool isOpaqueOverBounds(const DeferredDisplayState& state) {
+ // ensure that local bounds cover mapped bounds
+ if (!state.mMatrix.isSimple()) return false;
+
+ // check state/paint for transparency
+ if (state.mDrawModifiers.mShader ||
+ state.mAlpha != 1.0f ||
+ (mPaint && mPaint->getAlpha() != 0xFF)) return false;
+
+ SkXfermode::Mode mode = OpenGLRenderer::getXfermodeDirect(mPaint);
+ return (mode == SkXfermode::kSrcOver_Mode ||
+ mode == SkXfermode::kSrc_Mode);
+
+ }
+
SkPaint* mPaint; // should be accessed via getPaint() when applying
bool mQuickRejected;
};
@@ -218,6 +218,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)
@@ -232,24 +235,20 @@ public:
}
// default empty constructor for bounds, to be overridden in child constructor body
- DrawBoundedOp(SkPaint* paint)
- : DrawOp(paint) {}
+ DrawBoundedOp(SkPaint* paint): DrawOp(paint) { }
- bool getLocalBounds(Rect& localBounds) {
+ bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) {
localBounds.set(mLocalBounds);
+ if (drawModifiers.mHasShadow) {
+ // TODO: inspect paint's looper directly
+ Rect shadow(mLocalBounds);
+ shadow.translate(drawModifiers.mShadowDx, drawModifiers.mShadowDy);
+ shadow.outset(drawModifiers.mShadowRadius);
+ localBounds.unionWith(shadow);
+ }
return true;
}
- bool mergeAllowed() {
- if (!state.mMatrix.isPureTranslate()) return false;
-
- // checks that we're unclipped, and srcover
- const Rect& opBounds = state.mBounds;
- return fabs(opBounds.getWidth() - mLocalBounds.getWidth()) < 0.1 &&
- fabs(opBounds.getHeight() - mLocalBounds.getHeight()) < 0.1 &&
- (OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode);
- }
-
protected:
Rect mLocalBounds; // displayed area in LOCAL coord. doesn't incorporate stroke, so check paint
};
@@ -275,7 +274,7 @@ public:
renderer.save(mFlags);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Save flags %x", mFlags);
}
@@ -309,7 +308,7 @@ public:
renderer.restoreToCount(saveCount + mCount);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Restore to count %d", mCount);
}
@@ -348,7 +347,7 @@ public:
renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom, mAlpha, mMode, mFlags);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("SaveLayer%s of area " RECT_STRING,
(isSaveLayerAlpha() ? "Alpha" : ""),RECT_ARGS(mArea));
}
@@ -369,7 +368,7 @@ private:
return this;
}
- bool isSaveLayerAlpha() { return mAlpha < 255 && mMode == SkXfermode::kSrcOver_Mode; }
+ bool isSaveLayerAlpha() const { return mAlpha < 255 && mMode == SkXfermode::kSrcOver_Mode; }
Rect mArea;
int mAlpha;
SkXfermode::Mode mMode;
@@ -385,7 +384,7 @@ public:
renderer.translate(mDx, mDy);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Translate by %f %f", mDx, mDy);
}
@@ -405,7 +404,7 @@ public:
renderer.rotate(mDegrees);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Rotate by %f degrees", mDegrees);
}
@@ -424,7 +423,7 @@ public:
renderer.scale(mSx, mSy);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Scale by %f %f", mSx, mSy);
}
@@ -444,7 +443,7 @@ public:
renderer.skew(mSx, mSy);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Skew by %f %f", mSx, mSy);
}
@@ -464,8 +463,12 @@ public:
renderer.setMatrix(mMatrix);
}
- virtual void output(int level, uint32_t logFlags) {
- OP_LOG("SetMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
+ virtual void output(int level, uint32_t logFlags) const {
+ if (mMatrix) {
+ OP_LOG("SetMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
+ } else {
+ OP_LOGS("SetMatrix (reset)");
+ }
}
virtual const char* name() { return "SetMatrix"; }
@@ -483,7 +486,7 @@ public:
renderer.concatMatrix(mMatrix);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("ConcatMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
}
@@ -527,7 +530,7 @@ public:
renderer.clipRect(mArea.left, mArea.top, mArea.right, mArea.bottom, mOp);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("ClipRect " RECT_STRING, RECT_ARGS(mArea));
}
@@ -556,7 +559,7 @@ public:
renderer.clipPath(mPath, mOp);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
SkRect bounds = mPath->getBounds();
OP_LOG("ClipPath bounds " RECT_STRING,
bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
@@ -577,7 +580,7 @@ public:
renderer.clipRegion(mRegion, mOp);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
SkIRect bounds = mRegion->getBounds();
OP_LOG("ClipRegion bounds %d %d %d %d",
bounds.left(), bounds.top(), bounds.right(), bounds.bottom());
@@ -587,7 +590,6 @@ public:
private:
SkRegion* mRegion;
- SkRegion::Op mOp;
};
class ResetShaderOp : public StateOp {
@@ -596,7 +598,7 @@ public:
renderer.resetShader();
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOGS("ResetShader");
}
@@ -611,7 +613,7 @@ public:
renderer.setupShader(mShader);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("SetupShader, shader %p", mShader);
}
@@ -627,7 +629,7 @@ public:
renderer.resetColorFilter();
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOGS("ResetColorFilter");
}
@@ -643,7 +645,7 @@ public:
renderer.setupColorFilter(mColorFilter);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("SetupColorFilter, filter %p", mColorFilter);
}
@@ -659,7 +661,7 @@ public:
renderer.resetShadow();
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOGS("ResetShadow");
}
@@ -675,7 +677,7 @@ public:
renderer.setupShadow(mRadius, mDx, mDy, mColor);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("SetupShadow, radius %f, %f, %f, color %#x", mRadius, mDx, mDy, mColor);
}
@@ -694,7 +696,7 @@ public:
renderer.resetPaintFilter();
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOGS("ResetPaintFilter");
}
@@ -710,7 +712,7 @@ public:
renderer.setupPaintFilter(mClearBits, mSetBits);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("SetupPaintFilter, clear %#x, set %#x", mClearBits, mSetBits);
}
@@ -721,7 +723,6 @@ private:
int mSetBits;
};
-
///////////////////////////////////////////////////////////////////////////////
// DRAW OPERATIONS - these are operations that can draw to the canvas's device
///////////////////////////////////////////////////////////////////////////////
@@ -729,34 +730,63 @@ 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), mAtlas(Caches::getInstance().assetAtlas) {
+ mEntry = mAtlas.getEntry(bitmap);
+ if (mEntry) {
+ mEntryGenerationId = mAtlas.getGenerationId();
+ mUvMapper = mEntry->uvMapper;
+ }
+ }
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
return renderer.drawBitmap(mBitmap, mLocalBounds.left, mLocalBounds.top,
getPaint(renderer));
}
+ AssetAtlas::Entry* getAtlasEntry() {
+ // The atlas entry is stale, let's get a new one
+ if (mEntry && mEntryGenerationId != mAtlas.getGenerationId()) {
+ mEntryGenerationId = mAtlas.getGenerationId();
+ mEntry = mAtlas.getEntry(mBitmap);
+ mUvMapper = mEntry->uvMapper;
+ }
+ return mEntry;
+ }
+
#define SET_TEXTURE(ptr, posRect, offsetRect, texCoordsRect, xDim, yDim) \
TextureVertex::set(ptr++, posRect.xDim - offsetRect.left, posRect.yDim - offsetRect.top, \
texCoordsRect.xDim, texCoordsRect.yDim)
+ /**
+ * This multi-draw operation builds a mesh on the stack by generating a quad
+ * for each bitmap in the batch. This method is also responsible for dirtying
+ * the current layer, if any.
+ */
virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
- const Vector<DrawOp*>& ops, const Rect& bounds) {
- renderer.restoreDisplayState(state, true); // restore all but the clip
- renderer.setFullScreenClip(); // ensure merged ops aren't clipped
+ const Vector<OpStatePair>& ops, const Rect& bounds) {
+ const DeferredDisplayState& firstState = *(ops[0].state);
+ renderer.restoreDisplayState(firstState, true); // restore all but the clip
+
TextureVertex vertices[6 * ops.size()];
TextureVertex* vertex = &vertices[0];
- // TODO: manually handle rect clip for bitmaps by adjusting texCoords per op, and allowing
- // them to be merged in getBatchId()
- const Rect texCoords(0, 0, 1, 1);
+ const bool hasLayer = renderer.hasLayer();
+ bool pureTranslate = true;
- 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;
+ const DeferredDisplayState& state = *(ops[i].state);
+ const Rect& opBounds = 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
+ pureTranslate &= state.mMatrix.isPureTranslate();
+
+ Rect texCoords(0, 0, 1, 1);
+ ((DrawBitmapOp*) ops[i].op)->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);
@@ -764,29 +794,45 @@ public:
SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, bottom);
SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top);
SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, bottom);
+
+ if (hasLayer) {
+ renderer.dirtyLayer(opBounds.left, opBounds.top, opBounds.right, opBounds.bottom);
+ }
}
- return renderer.drawBitmaps(mBitmap, ops.size(), &vertices[0], bounds, mPaint);
+ return renderer.drawBitmaps(mBitmap, mEntry, ops.size(), &vertices[0],
+ pureTranslate, bounds, mPaint);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw bitmap %p at %f %f", mBitmap, mLocalBounds.left, mLocalBounds.top);
}
virtual const char* name() { return "DrawBitmap"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = DeferredDisplayList::kOpBatch_Bitmap;
- *mergeId = (mergeid_t)mBitmap;
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
+ deferInfo.mergeId = getAtlasEntry() ?
+ (mergeid_t) mEntry->getMergeId() : (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);
+ // Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation
+ // Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
+ // MergingDrawBatch::canMergeWith()
+ // TODO: support clipped bitmaps by handling them in SET_TEXTURE
+ deferInfo.mergeable = state.mMatrix.isSimple() && state.mMatrix.positiveScale() &&
+ !state.mClipSideFlags &&
+ OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode &&
+ (mBitmap->getConfig() != SkBitmap::kA8_Config);
}
const SkBitmap* bitmap() { return mBitmap; }
protected:
SkBitmap* mBitmap;
+ const AssetAtlas& mAtlas;
+ uint32_t mEntryGenerationId;
+ AssetAtlas::Entry* mEntry;
+ UvMapper mUvMapper;
};
class DrawBitmapMatrixOp : public DrawBoundedOp {
@@ -802,15 +848,15 @@ public:
return renderer.drawBitmap(mBitmap, mMatrix, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw bitmap %p matrix " MATRIX_STRING, mBitmap, MATRIX_ARGS(mMatrix));
}
virtual const char* name() { return "DrawBitmapMatrix"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = DeferredDisplayList::kOpBatch_Bitmap;
- return false;
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
}
private:
@@ -831,16 +877,16 @@ public:
getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw bitmap %p src="RECT_STRING", dst="RECT_STRING,
mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds));
}
virtual const char* name() { return "DrawBitmapRect"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = DeferredDisplayList::kOpBatch_Bitmap;
- return false;
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
}
private:
@@ -858,15 +904,15 @@ public:
mLocalBounds.top, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw bitmap %p", mBitmap);
}
virtual const char* name() { return "DrawBitmapData"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = DeferredDisplayList::kOpBatch_Bitmap;
- return false;
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
}
};
@@ -883,15 +929,15 @@ public:
mVertices, mColors, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw bitmap %p mesh %d x %d", mBitmap, mMeshWidth, mMeshHeight);
}
virtual const char* name() { return "DrawBitmapMesh"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = DeferredDisplayList::kOpBatch_Bitmap;
- return false;
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap;
}
private:
@@ -904,45 +950,148 @@ 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)
- : DrawBoundedOp(left, top, right, bottom, 0),
- mBitmap(bitmap), mxDivs(xDivs), myDivs(yDivs),
- mColors(colors), mxDivsCount(width), myDivsCount(height),
- mNumColors(numColors), mAlpha(alpha), mMode(mode) {};
+ DrawPatchOp(SkBitmap* bitmap, Res_png_9patch* patch,
+ float left, float top, float right, float bottom, SkPaint* paint)
+ : DrawBoundedOp(left, top, right, bottom, paint),
+ mBitmap(bitmap), mPatch(patch), mGenerationId(0), mMesh(NULL),
+ mAtlas(Caches::getInstance().assetAtlas) {
+ mEntry = mAtlas.getEntry(bitmap);
+ if (mEntry) {
+ mEntryGenerationId = mAtlas.getGenerationId();
+ }
+ };
+
+ AssetAtlas::Entry* getAtlasEntry() {
+ // The atlas entry is stale, let's get a new one
+ if (mEntry && mEntryGenerationId != mAtlas.getGenerationId()) {
+ mEntryGenerationId = mAtlas.getGenerationId();
+ mEntry = mAtlas.getEntry(mBitmap);
+ }
+ return mEntry;
+ }
+
+ const Patch* getMesh(OpenGLRenderer& renderer) {
+ if (!mMesh || renderer.getCaches().patchCache.getGenerationId() != mGenerationId) {
+ PatchCache& cache = renderer.getCaches().patchCache;
+ mMesh = cache.get(getAtlasEntry(), mBitmap->width(), mBitmap->height(),
+ mLocalBounds.getWidth(), mLocalBounds.getHeight(), mPatch);
+ mGenerationId = cache.getGenerationId();
+ }
+ return mMesh;
+ }
+
+ /**
+ * This multi-draw operation builds an indexed mesh on the stack by copying
+ * and transforming the vertices of each 9-patch in the batch. This method
+ * is also responsible for dirtying the current layer, if any.
+ */
+ virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
+ const Vector<OpStatePair>& ops, const Rect& bounds) {
+ const DeferredDisplayState& firstState = *(ops[0].state);
+ renderer.restoreDisplayState(firstState, true); // restore all but the clip
+
+ // Batches will usually contain a small number of items so it's
+ // worth performing a first iteration to count the exact number
+ // of vertices we need in the new mesh
+ uint32_t totalVertices = 0;
+ for (unsigned int i = 0; i < ops.size(); i++) {
+ totalVertices += ((DrawPatchOp*) ops[i].op)->getMesh(renderer)->verticesCount;
+ }
+
+ const bool hasLayer = renderer.hasLayer();
+
+ uint32_t indexCount = 0;
+
+ TextureVertex vertices[totalVertices];
+ TextureVertex* vertex = &vertices[0];
+
+ // Create a mesh that contains the transformed vertices for all the
+ // 9-patch objects that are part of the batch. Note that onDefer()
+ // enforces ops drawn by this function to have a pure translate or
+ // identity matrix
+ for (unsigned int i = 0; i < ops.size(); i++) {
+ DrawPatchOp* patchOp = (DrawPatchOp*) ops[i].op;
+ const DeferredDisplayState* state = ops[i].state;
+ const Patch* opMesh = patchOp->getMesh(renderer);
+ uint32_t vertexCount = opMesh->verticesCount;
+ if (vertexCount == 0) continue;
+
+ // We use the bounds to know where to translate our vertices
+ // Using patchOp->state.mBounds wouldn't work because these
+ // bounds are clipped
+ const float tx = (int) floorf(state->mMatrix.getTranslateX() +
+ patchOp->mLocalBounds.left + 0.5f);
+ const float ty = (int) floorf(state->mMatrix.getTranslateY() +
+ patchOp->mLocalBounds.top + 0.5f);
+
+ // Copy & transform all the vertices for the current operation
+ TextureVertex* opVertices = opMesh->vertices;
+ for (uint32_t j = 0; j < vertexCount; j++, opVertices++) {
+ TextureVertex::set(vertex++,
+ opVertices->position[0] + tx, opVertices->position[1] + ty,
+ opVertices->texture[0], opVertices->texture[1]);
+ }
+
+ // Dirty the current layer if possible. When the 9-patch does not
+ // contain empty quads we can take a shortcut and simply set the
+ // dirty rect to the object's bounds.
+ if (hasLayer) {
+ if (!opMesh->hasEmptyQuads) {
+ renderer.dirtyLayer(tx, ty,
+ tx + patchOp->mLocalBounds.getWidth(),
+ ty + patchOp->mLocalBounds.getHeight());
+ } else {
+ const size_t count = opMesh->quads.size();
+ for (size_t i = 0; i < count; i++) {
+ const Rect& quadBounds = opMesh->quads[i];
+ const float x = tx + quadBounds.left;
+ const float y = ty + quadBounds.top;
+ renderer.dirtyLayer(x, y,
+ x + quadBounds.getWidth(), y + quadBounds.getHeight());
+ }
+ }
+ }
+
+ indexCount += opMesh->indexCount;
+ }
+
+ return renderer.drawPatches(mBitmap, getAtlasEntry(),
+ &vertices[0], indexCount, getPaint(renderer));
+ }
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,
- mLocalBounds.right, mLocalBounds.bottom, mAlpha, mMode);
+ // 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, getMesh(renderer), getAtlasEntry(),
+ mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom,
+ getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw patch "RECT_STRING, RECT_ARGS(mLocalBounds));
}
virtual const char* name() { return "DrawPatch"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = DeferredDisplayList::kOpBatch_Patch;
- *mergeId = (mergeid_t)mBitmap;
- return true;
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch;
+ deferInfo.mergeId = getAtlasEntry() ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap;
+ deferInfo.mergeable = state.mMatrix.isPureTranslate() &&
+ OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
+ deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) && mBitmap->isOpaque();
}
private:
SkBitmap* mBitmap;
- const int32_t* mxDivs;
- const int32_t* myDivs;
- const uint32_t* mColors;
- uint32_t mxDivsCount;
- uint32_t myDivsCount;
- int8_t mNumColors;
- int mAlpha;
- SkXfermode::Mode mMode;
+ Res_png_9patch* mPatch;
+
+ uint32_t mGenerationId;
+ const Patch* mMesh;
+
+ const AssetAtlas& mAtlas;
+ uint32_t mEntryGenerationId;
+ AssetAtlas::Entry* mEntry;
};
class DrawColorOp : public DrawOp {
@@ -954,7 +1103,7 @@ public:
return renderer.drawColor(mColor, mMode);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw color %#x, mode %d", mColor, mMode);
}
@@ -970,7 +1119,7 @@ public:
DrawStrokableOp(float left, float top, float right, float bottom, SkPaint* paint)
: DrawBoundedOp(left, top, right, bottom, paint) {};
- bool getLocalBounds(Rect& localBounds) {
+ bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) {
localBounds.set(mLocalBounds);
if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) {
localBounds.outset(strokeWidthOutset());
@@ -978,15 +1127,15 @@ public:
return true;
}
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
if (mPaint->getPathEffect()) {
- *batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
} else {
- *batchId = mPaint->isAntiAlias() ?
+ deferInfo.batchId = mPaint->isAntiAlias() ?
DeferredDisplayList::kOpBatch_AlphaVertices :
DeferredDisplayList::kOpBatch_Vertices;
}
- return false;
}
};
@@ -1000,10 +1149,17 @@ public:
mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds));
}
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
+ DrawStrokableOp::onDefer(renderer, deferInfo, state);
+ deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) &&
+ mPaint->getStyle() == SkPaint::kFill_Style;
+ }
+
virtual const char* name() { return "DrawRect"; }
};
@@ -1017,15 +1173,15 @@ public:
return renderer.drawRects(mRects, mCount, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Rects count %d", mCount);
}
virtual const char* name() { return "DrawRects"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = DeferredDisplayList::kOpBatch_Vertices;
- return false;
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_Vertices;
}
private:
@@ -1044,7 +1200,7 @@ public:
mLocalBounds.right, mLocalBounds.bottom, mRx, mRy, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw RoundRect "RECT_STRING", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy);
}
@@ -1065,7 +1221,7 @@ public:
return renderer.drawCircle(mX, mY, mRadius, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Circle x %f, y %f, r %f", mX, mY, mRadius);
}
@@ -1087,7 +1243,7 @@ public:
mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Oval "RECT_STRING, RECT_ARGS(mLocalBounds));
}
@@ -1107,7 +1263,7 @@ public:
mStartAngle, mSweepAngle, mUseCenter, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Arc "RECT_STRING", start %f, sweep %f, useCenter %d",
RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter);
}
@@ -1136,15 +1292,15 @@ public:
return renderer.drawPath(mPath, getPaint(renderer));
}
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
SkPaint* paint = getPaint(renderer);
renderer.getCaches().pathCache.precache(mPath, paint);
- *batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
- return false;
+ deferInfo.batchId = DeferredDisplayList::kOpBatch_AlphaMaskTexture;
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
}
@@ -1166,17 +1322,17 @@ public:
return renderer.drawLines(mPoints, mCount, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Lines count %d", mCount);
}
virtual const char* name() { return "DrawLines"; }
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
- *batchId = mPaint->isAntiAlias() ?
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
+ deferInfo.batchId = mPaint->isAntiAlias() ?
DeferredDisplayList::kOpBatch_AlphaVertices :
DeferredDisplayList::kOpBatch_Vertices;
- return false;
}
protected:
@@ -1193,7 +1349,7 @@ public:
return renderer.drawPoints(mPoints, mCount, getPaint(renderer));
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Points count %d", mCount);
}
@@ -1205,20 +1361,19 @@ public:
DrawSomeTextOp(const char* text, int bytesCount, int count, SkPaint* paint)
: DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {};
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw some text, %d bytes", mBytesCount);
}
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
SkPaint* paint = getPaint(renderer);
FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
fontRenderer.precache(paint, mText, mCount, mat4::identity());
- *batchId = mPaint->getColor() == 0xff000000 ?
+ deferInfo.batchId = mPaint->getColor() == 0xff000000 ?
DeferredDisplayList::kOpBatch_Text :
DeferredDisplayList::kOpBatch_ColorText;
-
- return false;
}
protected:
@@ -1270,27 +1425,14 @@ 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) {
memset(&mPrecacheTransform.data[0], 0xff, 16 * sizeof(float));
}
- virtual bool onDefer(OpenGLRenderer& renderer, int* batchId, mergeid_t* mergeId) {
+ virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo,
+ const DeferredDisplayState& state) {
SkPaint* paint = getPaint(renderer);
FontRenderer& fontRenderer = renderer.getCaches().fontRenderer->getFontRenderer(paint);
const mat4& transform = renderer.findBestFontTransform(state.mMatrix);
@@ -1298,39 +1440,45 @@ public:
fontRenderer.precache(paint, mText, mCount, transform);
mPrecacheTransform = transform;
}
- *batchId = mPaint->getColor() == 0xff000000 ?
+ deferInfo.batchId = mPaint->getColor() == 0xff000000 ?
DeferredDisplayList::kOpBatch_Text :
DeferredDisplayList::kOpBatch_ColorText;
- *mergeId = (mergeid_t)mPaint->getColor();
+ deferInfo.mergeId = (mergeid_t)mPaint->getColor();
// don't merge decorated text - the decorations won't draw in order
bool noDecorations = !(mPaint->getFlags() & (SkPaint::kUnderlineText_Flag |
SkPaint::kStrikeThruText_Flag));
- return mergeAllowed() && noDecorations;
+ deferInfo.mergeable = state.mMatrix.isPureTranslate() && noDecorations &&
+ OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode;
}
virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
+ Rect bounds;
+ getLocalBounds(renderer.getDrawModifiers(), bounds);
return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
- mPositions, getPaint(renderer), mLength);
+ mPositions, getPaint(renderer), mTotalAdvance, bounds);
}
virtual status_t multiDraw(OpenGLRenderer& renderer, Rect& dirty,
- const Vector<DrawOp*>& ops, const Rect& bounds) {
+ const Vector<OpStatePair>& ops, const Rect& bounds) {
status_t status = DrawGlInfo::kStatusDone;
- renderer.setFullScreenClip(); // ensure merged ops aren't clipped
for (unsigned int i = 0; i < ops.size(); i++) {
+ const DeferredDisplayState& state = *(ops[i].state);
DrawOpMode drawOpMode = (i == ops.size() - 1) ? kDrawOpMode_Flush : kDrawOpMode_Defer;
- renderer.restoreDisplayState(ops[i]->state, true); // restore all but the clip
+ renderer.restoreDisplayState(state, true); // restore all but the clip
- DrawTextOp& op = *((DrawTextOp*)ops[i]);
+ DrawTextOp& op = *((DrawTextOp*)ops[i].op);
+ // quickReject() will not occure in drawText() so we can use mLocalBounds
+ // directly, we do not need to account for shadow by calling getLocalBounds()
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;
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Text of count %d, bytes %d", mCount, mBytesCount);
}
@@ -1343,7 +1491,7 @@ private:
float mX;
float mY;
const float* mPositions;
- float mLength;
+ float mTotalAdvance;
mat4 mPrecacheTransform;
};
@@ -1363,7 +1511,7 @@ public:
return ret;
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Functor %p", mFunctor);
}
@@ -1397,7 +1545,7 @@ public:
return DrawGlInfo::kStatusDone;
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Display List %p, flags %#x", mDisplayList, mFlags);
if (mDisplayList && (logFlags & kOpLogFlag_Recurse)) {
mDisplayList->output(level + 1);
@@ -1420,7 +1568,7 @@ public:
return renderer.drawLayer(mLayer, mX, mY);
}
- virtual void output(int level, uint32_t logFlags) {
+ virtual void output(int level, uint32_t logFlags) const {
OP_LOG("Draw Layer %p at %f %f", mLayer, mX, mY);
}
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 876c38a..8866029 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -57,6 +57,10 @@ void DisplayListRenderer::reset() {
mCaches.resourceCache.decrementRefcountLocked(mFilterResources.itemAt(i));
}
+ for (size_t i = 0; i < mPatchResources.size(); i++) {
+ mCaches.resourceCache.decrementRefcountLocked(mPatchResources.itemAt(i));
+ }
+
for (size_t i = 0; i < mShaders.size(); i++) {
mCaches.resourceCache.decrementRefcountLocked(mShaders.itemAt(i));
}
@@ -74,6 +78,7 @@ void DisplayListRenderer::reset() {
mBitmapResources.clear();
mOwnedBitmapResources.clear();
mFilterResources.clear();
+ mPatchResources.clear();
mSourcePaths.clear();
mShaders.clear();
@@ -313,20 +318,13 @@ 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) {
- int alpha;
- SkXfermode::Mode mode;
- OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode);
-
+status_t DisplayListRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch,
+ float left, float top, float right, float bottom, SkPaint* paint) {
bitmap = refBitmap(bitmap);
- xDivs = refBuffer<int>(xDivs, width);
- yDivs = refBuffer<int>(yDivs, height);
- colors = refBuffer<uint32_t>(colors, numColors);
+ patch = refPatch(patch);
+ paint = refPaint(paint);
- 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, paint));
return DrawGlInfo::kStatusDone;
}
@@ -423,17 +421,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;
}
@@ -467,10 +464,12 @@ void DisplayListRenderer::setupColorFilter(SkiaColorFilter* filter) {
void DisplayListRenderer::resetShadow() {
addStateOp(new (alloc()) ResetShadowOp());
+ OpenGLRenderer::resetShadow();
}
void DisplayListRenderer::setupShadow(float radius, float dx, float dy, int color) {
addStateOp(new (alloc()) SetupShadowOp(radius, dx, dy, color));
+ OpenGLRenderer::setupShadow(radius, dx, dy, color);
}
void DisplayListRenderer::resetPaintFilter() {
@@ -506,11 +505,12 @@ void DisplayListRenderer::addStateOp(StateOp* op) {
void DisplayListRenderer::addDrawOp(DrawOp* op) {
Rect localBounds;
- if (op->getLocalBounds(localBounds)) {
+ if (op->getLocalBounds(mDrawModifiers, localBounds)) {
bool rejected = quickRejectNoScissor(localBounds.left, localBounds.top,
localBounds.right, localBounds.bottom);
op->setQuickRejected(rejected);
}
+
mHasDrawOps = true;
addOpInternal(op);
}
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 75abad6..d233150 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);
@@ -156,6 +156,10 @@ public:
return mFilterResources;
}
+ const Vector<Res_png_9patch*>& getPatchResources() const {
+ return mPatchResources;
+ }
+
const Vector<SkiaShader*>& getShaders() const {
return mShaders;
}
@@ -265,11 +269,14 @@ private:
}
inline SkMatrix* refMatrix(SkMatrix* matrix) {
- // Copying the matrix is cheap and prevents against the user changing the original
- // matrix before the operation that uses it
- SkMatrix* copy = new SkMatrix(*matrix);
- mMatrices.add(copy);
- return copy;
+ if (matrix) {
+ // Copying the matrix is cheap and prevents against the user changing
+ // the original matrix before the operation that uses it
+ SkMatrix* copy = new SkMatrix(*matrix);
+ mMatrices.add(copy);
+ return copy;
+ }
+ return matrix;
}
inline Layer* refLayer(Layer* layer) {
@@ -315,9 +322,16 @@ private:
return colorFilter;
}
+ inline Res_png_9patch* refPatch(Res_png_9patch* patch) {
+ mPatchResources.add(patch);
+ mCaches.resourceCache.incrementRefcount(patch);
+ return patch;
+ }
+
Vector<SkBitmap*> mBitmapResources;
Vector<SkBitmap*> mOwnedBitmapResources;
Vector<SkiaColorFilter*> mFilterResources;
+ Vector<Res_png_9patch*> mPatchResources;
Vector<SkPaint*> mPaints;
DefaultKeyedVector<SkPaint*, SkPaint*> mPaintMap;
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
index 19b3849..77006a4 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;
+ bool useFloatTexture = Extensions::getInstance().hasFloatTextures();
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..84e7e65 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -14,8 +14,19 @@
* 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"
+#include "Properties.h"
namespace android {
@@ -40,33 +51,28 @@ 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);
+
+ char property[PROPERTY_VALUE_MAX];
+ if (property_get(PROPERTY_DEBUG_NV_PROFILING, property, NULL) > 0) {
+ mHasNvSystemTime = !strcmp(property, "true") && hasEglExtension("EGL_NV_system_time");
+ } else {
+ mHasNvSystemTime = false;
+ }
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:
@@ -80,7 +86,7 @@ Extensions::Extensions(): Singleton<Extensions>() {
// or more digits. The release number and vendor specific information are
// optional."
- if (sscanf(version, "OpenGL ES %d.%d", &mVersionMajor, &mVersionMinor) !=2) {
+ if (sscanf(version, "OpenGL ES %d.%d", &mVersionMajor, &mVersionMinor) != 2) {
// If we cannot parse the version number, assume OpenGL ES 2.0
mVersionMajor = 2;
mVersionMinor = 0;
@@ -88,22 +94,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 mGlExtensionList.indexOf(s) >= 0;
+}
+
+bool Extensions::hasEglExtension(const char* extension) const {
const String8 s(extension);
- return mExtensionList.indexOf(s) >= 0;
+ 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..25d4c5e 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,30 @@ 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 bool hasUnpackRowLength() const { return mVersionMajor >= 3; }
+ inline bool hasPixelBufferObjects() const { return mVersionMajor >= 3; }
+ inline bool hasOcclusionQueries() const { return mVersionMajor >= 3; }
+ inline bool hasFloatTextures() const { return mVersionMajor >= 3; }
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 +73,7 @@ private:
bool mHasTiledRendering;
bool mHas1BitStencil;
bool mHas4BitStencil;
+ bool mHasNvSystemTime;
int mVersionMajor;
int mVersionMinor;
diff --git a/libs/hwui/Fence.h b/libs/hwui/Fence.h
new file mode 100644
index 0000000..f175e98
--- /dev/null
+++ b/libs/hwui/Fence.h
@@ -0,0 +1,113 @@
+/*
+ * 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_FENCE_H
+#define ANDROID_HWUI_FENCE_H
+
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * Creating a Fence instance inserts a new sync fence in the OpenGL
+ * commands stream. The caller can then wait for the fence to be signaled
+ * by calling the wait method.
+ */
+class Fence {
+public:
+ enum {
+ /**
+ * Default timeout in nano-seconds for wait()
+ */
+ kDefaultTimeout = 1000000000
+ };
+
+ /**
+ * Inserts a new sync fence in the OpenGL commands stream.
+ */
+ Fence() {
+ mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+ if (mDisplay != EGL_NO_DISPLAY) {
+ mFence = eglCreateSyncKHR(mDisplay, EGL_SYNC_FENCE_KHR, NULL);
+ } else {
+ mFence = EGL_NO_SYNC_KHR;
+ }
+ }
+
+ /**
+ * Destroys the fence. Any caller waiting on the fence will be
+ * signaled immediately.
+ */
+ ~Fence() {
+ if (mFence != EGL_NO_SYNC_KHR) {
+ eglDestroySyncKHR(mDisplay, mFence);
+ }
+ }
+
+ /**
+ * Blocks the calling thread until this fence is signaled, or until
+ * <timeout> nanoseconds have passed.
+ *
+ * Returns true if waiting for the fence was successful, false if
+ * a timeout or an error occurred.
+ */
+ bool wait(EGLTimeKHR timeout = kDefaultTimeout) {
+ EGLint waitStatus = eglClientWaitSyncKHR(mDisplay, mFence,
+ EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, timeout);
+ if (waitStatus == EGL_FALSE) {
+ ALOGW("Failed to wait for the fence %#x", eglGetError());
+ }
+ return waitStatus == EGL_CONDITION_SATISFIED_KHR;
+ }
+
+private:
+ EGLDisplay mDisplay;
+ EGLSyncKHR mFence;
+
+}; // class Fence
+
+/**
+ * An AutoFence creates a Fence instance and waits for the fence
+ * to be signaled when the AutoFence is destroyed. This is useful
+ * to automatically wait for a series of OpenGL commands to be
+ * executed. For example:
+ *
+ * void drawAndWait() {
+ * glDrawElements();
+ * AutoFence fence;
+ * }
+ */
+class AutoFence {
+public:
+ AutoFence(EGLTimeKHR timeout = Fence::kDefaultTimeout): mTimeout(timeout) {
+ }
+
+ ~AutoFence() {
+ mFence.wait(mTimeout);
+ }
+
+private:
+ EGLTimeKHR mTimeout;
+ Fence mFence;
+
+}; // class AutoFence
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_FENCE_H
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 543cfa2..00e7870 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -21,10 +21,11 @@
#include <cutils/properties.h>
-#include <utils/Functor.h>
#include <utils/Log.h>
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
#include <RenderScript.h>
+#endif
#include "utils/Blur.h"
#include "utils/Timing.h"
@@ -33,6 +34,7 @@
#include "Debug.h"
#include "Extensions.h"
#include "FontRenderer.h"
+#include "OpenGLRenderer.h"
#include "PixelBuffer.h"
#include "Rect.h"
@@ -43,6 +45,52 @@ namespace uirenderer {
#define RS_MIN_INPUT_CUTOFF 10000
///////////////////////////////////////////////////////////////////////////////
+// TextSetupFunctor
+///////////////////////////////////////////////////////////////////////////////
+status_t TextSetupFunctor::operator ()(int what, void* data) {
+ Data* typedData = reinterpret_cast<Data*>(data);
+ GLenum glyphFormat = typedData ? typedData->glyphFormat : GL_ALPHA;
+
+ renderer->setupDraw();
+ renderer->setupDrawTextGamma(paint);
+ renderer->setupDrawDirtyRegionsDisabled();
+ renderer->setupDrawWithTexture(glyphFormat == GL_ALPHA);
+ switch (glyphFormat) {
+ case GL_ALPHA: {
+ renderer->setupDrawAlpha8Color(paint->getColor(), alpha);
+ break;
+ }
+ case GL_RGBA: {
+ float floatAlpha = alpha / 255.0f;
+ renderer->setupDrawColor(floatAlpha, floatAlpha, floatAlpha, floatAlpha);
+ break;
+ }
+ default: {
+#if DEBUG_FONT_RENDERER
+ ALOGD("TextSetupFunctor: called with unknown glyph format %x", glyphFormat);
+#endif
+ break;
+ }
+ }
+ renderer->setupDrawColorFilter();
+ renderer->setupDrawShader();
+ renderer->setupDrawBlending(true, mode);
+ renderer->setupDrawProgram();
+ renderer->setupDrawModelView(x, y, x, y, pureTranslate, true);
+ // Calling setupDrawTexture with the name 0 will enable the
+ // uv attributes and increase the texture unit count
+ // texture binding will be performed by the font renderer as
+ // needed
+ renderer->setupDrawTexture(0);
+ renderer->setupDrawPureColorUniforms();
+ renderer->setupDrawColorFilterUniforms();
+ renderer->setupDrawShaderUniforms(pureTranslate);
+ renderer->setupDrawTextGammaUniforms();
+
+ return NO_ERROR;
+}
+
+///////////////////////////////////////////////////////////////////////////////
// FontRenderer
///////////////////////////////////////////////////////////////////////////////
@@ -62,8 +110,6 @@ FontRenderer::FontRenderer() :
mLinearFiltering = false;
- mIndexBufferID = 0;
-
mSmallCacheWidth = DEFAULT_TEXT_SMALL_CACHE_WIDTH;
mSmallCacheHeight = DEFAULT_TEXT_SMALL_CACHE_HEIGHT;
mLargeCacheWidth = DEFAULT_TEXT_LARGE_CACHE_WIDTH;
@@ -103,17 +149,16 @@ FontRenderer::FontRenderer() :
sLogFontRendererCreate = false;
}
-FontRenderer::~FontRenderer() {
- for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
- delete mCacheTextures[i];
+void clearCacheTextures(Vector<CacheTexture*>& cacheTextures) {
+ for (uint32_t i = 0; i < cacheTextures.size(); i++) {
+ delete cacheTextures[i];
}
- mCacheTextures.clear();
+ cacheTextures.clear();
+}
- if (mInitialized) {
- // Unbinding the buffer shouldn't be necessary but it crashes with some drivers
- Caches::getInstance().unbindIndicesBuffer();
- glDeleteBuffers(1, &mIndexBufferID);
- }
+FontRenderer::~FontRenderer() {
+ clearCacheTextures(mACacheTextures);
+ clearCacheTextures(mRGBACacheTextures);
LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
while (it.next()) {
@@ -130,15 +175,19 @@ void FontRenderer::flushAllAndInvalidate() {
it.value()->invalidateTextureCache();
}
- for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
- mCacheTextures[i]->init();
+ for (uint32_t i = 0; i < mACacheTextures.size(); i++) {
+ mACacheTextures[i]->init();
+ }
+
+ for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) {
+ mRGBACacheTextures[i]->init();
}
}
-void FontRenderer::flushLargeCaches() {
+void FontRenderer::flushLargeCaches(Vector<CacheTexture*>& cacheTextures) {
// Start from 1; don't deallocate smallest/default texture
- for (uint32_t i = 1; i < mCacheTextures.size(); i++) {
- CacheTexture* cacheTexture = mCacheTextures[i];
+ for (uint32_t i = 1; i < cacheTextures.size(); i++) {
+ CacheTexture* cacheTexture = cacheTextures[i];
if (cacheTexture->getPixelBuffer()) {
cacheTexture->init();
LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts);
@@ -150,11 +199,16 @@ void FontRenderer::flushLargeCaches() {
}
}
-CacheTexture* FontRenderer::cacheBitmapInTexture(const SkGlyph& glyph,
- uint32_t* startX, uint32_t* startY) {
- for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
- if (mCacheTextures[i]->fitBitmap(glyph, startX, startY)) {
- return mCacheTextures[i];
+void FontRenderer::flushLargeCaches() {
+ flushLargeCaches(mACacheTextures);
+ flushLargeCaches(mRGBACacheTextures);
+}
+
+CacheTexture* FontRenderer::cacheBitmapInTexture(Vector<CacheTexture*>& cacheTextures,
+ const SkGlyph& glyph, uint32_t* startX, uint32_t* startY) {
+ for (uint32_t i = 0; i < cacheTextures.size(); i++) {
+ if (cacheTextures[i]->fitBitmap(glyph, startX, startY)) {
+ return cacheTextures[i];
}
}
// Could not fit glyph into current cache textures
@@ -175,9 +229,27 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
cachedGlyph->mIsValid = false;
+ // choose an appropriate cache texture list for this glyph format
+ SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
+ Vector<CacheTexture*>* cacheTextures = NULL;
+ switch (format) {
+ case SkMask::kA8_Format:
+ case SkMask::kBW_Format:
+ cacheTextures = &mACacheTextures;
+ break;
+ case SkMask::kARGB32_Format:
+ cacheTextures = &mRGBACacheTextures;
+ break;
+ default:
+#if DEBUG_FONT_RENDERER
+ ALOGD("getCacheTexturesForFormat: unknown SkMask format %x", format);
+#endif
+ return;
+ }
+
// If the glyph is too tall, don't cache it
if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 >
- mCacheTextures[mCacheTextures.size() - 1]->getHeight()) {
+ (*cacheTextures)[cacheTextures->size() - 1]->getHeight()) {
ALOGE("Font size too large to fit in cache. width, height = %i, %i",
(int) glyph.fWidth, (int) glyph.fHeight);
return;
@@ -187,14 +259,14 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
uint32_t startX = 0;
uint32_t startY = 0;
- CacheTexture* cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
+ CacheTexture* cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
if (!cacheTexture) {
if (!precaching) {
// If the new glyph didn't fit and we are not just trying to precache it,
// clear out the cache and try again
flushAllAndInvalidate();
- cacheTexture = cacheBitmapInTexture(glyph, &startX, &startY);
+ cacheTexture = cacheBitmapInTexture(*cacheTextures, glyph, &startX, &startY);
}
if (!cacheTexture) {
@@ -222,24 +294,21 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
cacheTexture->allocateMesh();
}
- // Tells us whether the glyphs is B&W (1 bit per pixel)
- // or anti-aliased (8 bits per pixel)
- SkMask::Format format = static_cast<SkMask::Format>(glyph.fMaskFormat);
-
uint8_t* cacheBuffer = cacheTexture->getPixelBuffer()->map();
- uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
-
- // Copy the glyph image, taking the mask format into account
uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
- int stride = glyph.rowBytes();
-
- uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
- memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
+ int srcStride = glyph.rowBytes();
+ // Copy the glyph image, taking the mask format into account
switch (format) {
case SkMask::kA8_Format: {
+ uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
+ uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
+ - TEXTURE_BORDER_SIZE;
+ // write leading border line
+ memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
+ // write glyph data
if (mGammaTable) {
- for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
+ for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
row = cacheY * cacheWidth;
cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
for (cacheX = startX, bX = 0; cacheX < endX; cacheX++, bX++) {
@@ -249,21 +318,55 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
}
} else {
- for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += stride) {
+ for (cacheY = startY, bY = 0; cacheY < endY; cacheY++, bY += srcStride) {
row = cacheY * cacheWidth;
memcpy(&cacheBuffer[row + startX], &bitmapBuffer[bY], glyph.fWidth);
cacheBuffer[row + startX - TEXTURE_BORDER_SIZE] = 0;
cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
}
}
+ // write trailing border line
+ row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
+ memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
+ break;
+ }
+ case SkMask::kARGB32_Format: {
+ // prep data lengths
+ const size_t formatSize = PixelBuffer::formatSize(GL_RGBA);
+ const size_t borderSize = formatSize * TEXTURE_BORDER_SIZE;
+ size_t rowSize = formatSize * glyph.fWidth;
+ // prep advances
+ size_t dstStride = formatSize * cacheWidth;
+ // prep indices
+ // - we actually start one row early, and then increment before first copy
+ uint8_t* src = &bitmapBuffer[0 - srcStride];
+ uint8_t* dst = &cacheBuffer[cacheTexture->getOffset(startX, startY - 1)];
+ uint8_t* dstEnd = &cacheBuffer[cacheTexture->getOffset(startX, endY - 1)];
+ uint8_t* dstL = dst - borderSize;
+ uint8_t* dstR = dst + rowSize;
+ // write leading border line
+ memset(dstL, 0, rowSize + 2 * borderSize);
+ // write glyph data
+ while (dst < dstEnd) {
+ memset(dstL += dstStride, 0, borderSize); // leading border column
+ memcpy(dst += dstStride, src += srcStride, rowSize); // glyph data
+ memset(dstR += dstStride, 0, borderSize); // trailing border column
+ }
+ // write trailing border line
+ memset(dstL += dstStride, 0, rowSize + 2 * borderSize);
break;
}
case SkMask::kBW_Format: {
+ uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
+ uint32_t row = (startY - TEXTURE_BORDER_SIZE) * cacheWidth + startX
+ - TEXTURE_BORDER_SIZE;
static const uint8_t COLORS[2] = { 0, 255 };
-
+ // write leading border line
+ memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
+ // write glyph data
for (cacheY = startY; cacheY < endY; cacheY++) {
cacheX = startX;
- int rowBytes = stride;
+ int rowBytes = srcStride;
uint8_t* buffer = bitmapBuffer;
row = cacheY * cacheWidth;
@@ -276,23 +379,24 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
}
cacheBuffer[row + endX + TEXTURE_BORDER_SIZE - 1] = 0;
- bitmapBuffer += stride;
+ bitmapBuffer += srcStride;
}
+ // write trailing border line
+ row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
+ memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
break;
}
default:
- ALOGW("Unkown glyph format: 0x%x", format);
+ ALOGW("Unknown glyph format: 0x%x", format);
break;
}
- row = (endY + TEXTURE_BORDER_SIZE - 1) * cacheWidth + startX - TEXTURE_BORDER_SIZE;
- memset(&cacheBuffer[row], 0, glyph.fWidth + 2 * TEXTURE_BORDER_SIZE);
-
cachedGlyph->mIsValid = true;
}
-CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
- CacheTexture* cacheTexture = new CacheTexture(width, height, gMaxNumberOfQuads);
+CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format,
+ bool allocate) {
+ CacheTexture* cacheTexture = new CacheTexture(width, height, format, gMaxNumberOfQuads);
if (allocate) {
Caches::getInstance().activeTexture(0);
@@ -304,44 +408,23 @@ CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool alloc
}
void FontRenderer::initTextTexture() {
- for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
- delete mCacheTextures[i];
- }
- mCacheTextures.clear();
+ clearCacheTextures(mACacheTextures);
+ clearCacheTextures(mRGBACacheTextures);
mUploadTexture = false;
- mCacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true));
- mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
- mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1, false));
- mCacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight, false));
- mCurrentCacheTexture = mCacheTextures[0];
-}
-
-// Avoid having to reallocate memory and render quad by quad
-void FontRenderer::initVertexArrayBuffers() {
- uint32_t numIndices = gMaxNumberOfQuads * 6;
- uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
- uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
-
- // Four verts, two triangles , six indices per quad
- for (uint32_t i = 0; i < gMaxNumberOfQuads; i++) {
- int i6 = i * 6;
- int i4 = i * 4;
-
- indexBufferData[i6 + 0] = i4 + 0;
- indexBufferData[i6 + 1] = i4 + 1;
- indexBufferData[i6 + 2] = i4 + 2;
-
- indexBufferData[i6 + 3] = i4 + 0;
- indexBufferData[i6 + 4] = i4 + 2;
- indexBufferData[i6 + 5] = i4 + 3;
- }
-
- glGenBuffers(1, &mIndexBufferID);
- Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
-
- free(indexBufferData);
+ mACacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
+ GL_ALPHA, true));
+ mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
+ GL_ALPHA, false));
+ mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
+ GL_ALPHA, false));
+ mACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight,
+ GL_ALPHA, false));
+ mRGBACacheTextures.push(createCacheTexture(mSmallCacheWidth, mSmallCacheHeight,
+ GL_RGBA, false));
+ mRGBACacheTextures.push(createCacheTexture(mLargeCacheWidth, mLargeCacheHeight >> 1,
+ GL_RGBA, false));
+ mCurrentCacheTexture = mACacheTextures[0];
}
// We don't want to allocate anything unless we actually draw text
@@ -351,11 +434,28 @@ void FontRenderer::checkInit() {
}
initTextTexture();
- initVertexArrayBuffers();
mInitialized = true;
}
+void checkTextureUpdateForCache(Caches& caches, Vector<CacheTexture*>& cacheTextures,
+ bool& resetPixelStore, GLuint& lastTextureId) {
+ for (uint32_t i = 0; i < cacheTextures.size(); i++) {
+ CacheTexture* cacheTexture = cacheTextures[i];
+ if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
+ if (cacheTexture->getTextureId() != lastTextureId) {
+ lastTextureId = cacheTexture->getTextureId();
+ caches.activeTexture(0);
+ caches.bindTexture(lastTextureId);
+ }
+
+ if (cacheTexture->upload()) {
+ resetPixelStore = true;
+ }
+ }
+ }
+}
+
void FontRenderer::checkTextureUpdate() {
if (!mUploadTexture) {
return;
@@ -368,25 +468,8 @@ void FontRenderer::checkTextureUpdate() {
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// Iterate over all the cache textures and see which ones need to be updated
- for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
- CacheTexture* cacheTexture = mCacheTextures[i];
- if (cacheTexture->isDirty() && cacheTexture->getPixelBuffer()) {
- if (cacheTexture->getTextureId() != lastTextureId) {
- lastTextureId = cacheTexture->getTextureId();
- caches.activeTexture(0);
- glBindTexture(GL_TEXTURE_2D, lastTextureId);
- }
-
- if (cacheTexture->upload()) {
- resetPixelStore = true;
- }
-
-#if DEBUG_FONT_RENDERER
- ALOGD("glTexSubimage for cacheTexture %d: x, y, width height = %d, %d, %d, %d",
- i, x, y, width, height);
-#endif
- }
- }
+ checkTextureUpdateForCache(caches, mACacheTextures, resetPixelStore, lastTextureId);
+ checkTextureUpdateForCache(caches, mRGBACacheTextures, resetPixelStore, lastTextureId);
// Unbind any PBO we might have used to update textures
caches.unbindPixelBuffer();
@@ -400,21 +483,21 @@ void FontRenderer::checkTextureUpdate() {
mUploadTexture = false;
}
-void FontRenderer::issueDrawCommand() {
+void FontRenderer::issueDrawCommand(Vector<CacheTexture*>& cacheTextures) {
+ Caches& caches = Caches::getInstance();
bool first = true;
bool force = false;
-
- GLuint lastId = 0;
- Caches& caches = Caches::getInstance();
-
- for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
- CacheTexture* texture = mCacheTextures[i];
+ for (uint32_t i = 0; i < cacheTextures.size(); i++) {
+ CacheTexture* texture = cacheTextures[i];
if (texture->canDraw()) {
if (first) {
- if (mFunctor) (*mFunctor)(0, NULL);
+ if (mFunctor) {
+ TextSetupFunctor::Data functorData(texture->getFormat());
+ (*mFunctor)(0, &functorData);
+ }
checkTextureUpdate();
- caches.bindIndicesBuffer(mIndexBufferID);
+ caches.bindIndicesBuffer();
if (!mDrawn) {
// If returns true, a VBO was bound and we must
@@ -427,7 +510,7 @@ void FontRenderer::issueDrawCommand() {
first = false;
}
- glBindTexture(GL_TEXTURE_2D, texture->getTextureId());
+ caches.bindTexture(texture->getTextureId());
texture->setLinearFiltering(mLinearFiltering, false);
TextureVertex* mesh = texture->mesh();
@@ -441,6 +524,11 @@ void FontRenderer::issueDrawCommand() {
texture->resetMesh();
}
}
+}
+
+void FontRenderer::issueDrawCommand() {
+ issueDrawCommand(mACacheTextures);
+ issueDrawCommand(mRGBACacheTextures);
mDrawn = true;
}
@@ -532,13 +620,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;
@@ -611,13 +704,13 @@ bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *t
bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path,
- float hOffset, float vOffset, Rect* bounds) {
+ float hOffset, float vOffset, Rect* bounds, Functor* functor) {
if (!mCurrentFont) {
ALOGE("No font set");
return false;
}
- initRender(clip, bounds, NULL);
+ initRender(clip, bounds, functor);
mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset);
finishRender();
@@ -633,49 +726,55 @@ 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 == 0) {
+ mRs = new RSC::RS();
+ if (!mRs->init(RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) {
+ 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 = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement);
+ }
- delete[] gaussian;
- delete[] scratch;
- return;
- }
+ RSC::sp<const RSC::Type> t = RSC::Type::create(mRs, mRsElement, width, height, 0);
+ RSC::sp<RSC::Allocation> ain = RSC::Allocation::createTyped(mRs, t,
+ RS_ALLOCATION_MIPMAP_NONE, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
+ *image);
+ RSC::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->setInput(ain);
+ mRsScript->forEach(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 {
+static uint32_t calculateCacheSize(const Vector<CacheTexture*>& cacheTextures) {
uint32_t size = 0;
- for (uint32_t i = 0; i < mCacheTextures.size(); i++) {
- CacheTexture* cacheTexture = mCacheTextures[i];
+ for (uint32_t i = 0; i < cacheTextures.size(); i++) {
+ CacheTexture* cacheTexture = cacheTextures[i];
if (cacheTexture && cacheTexture->getPixelBuffer()) {
size += cacheTexture->getPixelBuffer()->getSize();
}
@@ -683,5 +782,19 @@ uint32_t FontRenderer::getCacheSize() const {
return size;
}
+uint32_t FontRenderer::getCacheSize(GLenum format) const {
+ switch (format) {
+ case GL_ALPHA: {
+ return calculateCacheSize(mACacheTextures);
+ }
+ case GL_RGBA: {
+ return calculateCacheSize(mRGBACacheTextures);
+ }
+ default: {
+ return 0;
+ }
+ }
+}
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 307a1d9..aa7e776 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -17,8 +17,10 @@
#ifndef ANDROID_HWUI_FONT_RENDERER_H
#define ANDROID_HWUI_FONT_RENDERER_H
+#include <utils/Functor.h>
#include <utils/LruCache.h>
#include <utils/Vector.h>
+#include <utils/StrongPointer.h>
#include <SkPaint.h>
@@ -32,19 +34,55 @@
#include "Matrix.h"
#include "Properties.h"
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
+#include "RenderScript.h"
namespace RSC {
class Element;
class RS;
class ScriptIntrinsicBlur;
+ class sp;
}
+#endif
class Functor;
namespace android {
namespace uirenderer {
+class OpenGLRenderer;
+
///////////////////////////////////////////////////////////////////////////////
-// Renderer
+// TextSetupFunctor
+///////////////////////////////////////////////////////////////////////////////
+class TextSetupFunctor: public Functor {
+public:
+ struct Data {
+ Data(GLenum glyphFormat) : glyphFormat(glyphFormat) {
+ }
+
+ GLenum glyphFormat;
+ };
+
+ TextSetupFunctor(OpenGLRenderer* renderer, float x, float y, bool pureTranslate,
+ int alpha, SkXfermode::Mode mode, SkPaint* paint): Functor(),
+ renderer(renderer), x(x), y(y), pureTranslate(pureTranslate),
+ alpha(alpha), mode(mode), paint(paint) {
+ }
+ ~TextSetupFunctor() { }
+
+ status_t operator ()(int what, void* data);
+
+ OpenGLRenderer* renderer;
+ float x;
+ float y;
+ bool pureTranslate;
+ int alpha;
+ SkXfermode::Mode mode;
+ SkPaint* paint;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// FontRenderer
///////////////////////////////////////////////////////////////////////////////
class FontRenderer {
@@ -52,6 +90,7 @@ public:
FontRenderer();
~FontRenderer();
+ void flushLargeCaches(Vector<CacheTexture*>& cacheTextures);
void flushLargeCaches();
void setGammaTable(const uint8_t* gammaTable) {
@@ -70,7 +109,8 @@ public:
// bounds is an out parameter
bool renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
- uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds);
+ uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds,
+ Functor* functor);
struct DropShadow {
DropShadow() { };
@@ -97,30 +137,29 @@ public:
mLinearFiltering = linearFiltering;
}
- uint32_t getCacheSize() const;
+ uint32_t getCacheSize(GLenum format) const;
private:
friend class Font;
- static const uint32_t gMaxNumberOfQuads = 2048;
-
const uint8_t* mGammaTable;
void allocateTextureMemory(CacheTexture* cacheTexture);
void deallocateTextureMemory(CacheTexture* cacheTexture);
void initTextTexture();
- CacheTexture* createCacheTexture(int width, int height, bool allocate);
+ CacheTexture* createCacheTexture(int width, int height, GLenum format, bool allocate);
void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
uint32_t *retOriginX, uint32_t *retOriginY, bool precaching);
- CacheTexture* cacheBitmapInTexture(const SkGlyph& glyph, uint32_t* startX, uint32_t* startY);
+ CacheTexture* cacheBitmapInTexture(Vector<CacheTexture*>& cacheTextures, const SkGlyph& glyph,
+ uint32_t* startX, uint32_t* startY);
void flushAllAndInvalidate();
- void initVertexArrayBuffers();
void checkInit();
void initRender(const Rect* clip, Rect* bounds, Functor* functor);
void finishRender();
+ void issueDrawCommand(Vector<CacheTexture*>& cacheTextures);
void issueDrawCommand();
void appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
float x2, float y2, float u2, float v2,
@@ -148,7 +187,8 @@ private:
uint32_t mLargeCacheWidth;
uint32_t mLargeCacheHeight;
- Vector<CacheTexture*> mCacheTextures;
+ Vector<CacheTexture*> mACacheTextures;
+ Vector<CacheTexture*> mRGBACacheTextures;
Font* mCurrentFont;
LruCache<Font::FontDescription, Font*> mActiveFonts;
@@ -157,8 +197,6 @@ private:
bool mUploadTexture;
- uint32_t mIndexBufferID;
-
Functor* mFunctor;
const Rect* mClip;
Rect* mBounds;
@@ -168,10 +206,12 @@ private:
bool mLinearFiltering;
+#ifdef ANDROID_ENABLE_RENDERSCRIPT
// RS constructs
- sp<RSC::RS> mRs;
- sp<const RSC::Element> mRsElement;
- sp<RSC::ScriptIntrinsicBlur> mRsScript;
+ RSC::sp<RSC::RS> mRs;
+ RSC::sp<const RSC::Element> mRsElement;
+ RSC::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/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h
index bbfa66d..46cfd04 100644
--- a/libs/hwui/GammaFontRenderer.h
+++ b/libs/hwui/GammaFontRenderer.h
@@ -35,7 +35,7 @@ public:
virtual FontRenderer& getFontRenderer(const SkPaint* paint) = 0;
virtual uint32_t getFontRendererCount() const = 0;
- virtual uint32_t getFontRendererSize(uint32_t fontRenderer) const = 0;
+ virtual uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const = 0;
virtual void describe(ProgramDescription& description, const SkPaint* paint) const = 0;
virtual void setupProgram(ProgramDescription& description, Program* program) const = 0;
@@ -81,8 +81,8 @@ public:
return 1;
}
- uint32_t getFontRendererSize(uint32_t fontRenderer) const {
- return mRenderer ? mRenderer->getCacheSize() : 0;
+ uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const {
+ return mRenderer ? mRenderer->getCacheSize(format) : 0;
}
void describe(ProgramDescription& description, const SkPaint* paint) const;
@@ -128,8 +128,8 @@ public:
return 1;
}
- uint32_t getFontRendererSize(uint32_t fontRenderer) const {
- return mRenderer ? mRenderer->getCacheSize() : 0;
+ uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const {
+ return mRenderer ? mRenderer->getCacheSize(format) : 0;
}
void describe(ProgramDescription& description, const SkPaint* paint) const {
@@ -162,13 +162,13 @@ public:
return kGammaCount;
}
- uint32_t getFontRendererSize(uint32_t fontRenderer) const {
+ uint32_t getFontRendererSize(uint32_t fontRenderer, GLenum format) const {
if (fontRenderer >= kGammaCount) return 0;
FontRenderer* renderer = mRenderers[fontRenderer];
if (!renderer) return 0;
- return renderer->getCacheSize();
+ return renderer->getCacheSize(format);
}
void describe(ProgramDescription& description, const SkPaint* paint) const {
diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp
index 507ed95..0916942 100644
--- a/libs/hwui/GradientCache.cpp
+++ b/libs/hwui/GradientCache.cpp
@@ -78,7 +78,7 @@ GradientCache::GradientCache():
mCache.setOnEntryRemovedListener(this);
const Extensions& extensions = Extensions::getInstance();
- mUseFloatTexture = extensions.getMajorGlVersion() >= 3;
+ mUseFloatTexture = extensions.hasFloatTextures();
mHasNpot = extensions.hasNPot();
}
@@ -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..bd371a3 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -28,9 +28,9 @@
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;
cacheable = true;
dirty = false;
@@ -47,16 +47,15 @@ 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();
delete[] mesh;
- delete[] meshIndices;
delete deferredList;
}
@@ -76,7 +75,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 +88,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 +119,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,21 +137,55 @@ 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::defer() {
- if (!deferredList) {
- deferredList = new DeferredDisplayList;
+void Layer::bindTexture() const {
+ if (texture.id) {
+ caches.bindTexture(renderTarget, texture.id);
}
- DeferStateStruct deferredState(*deferredList, *renderer,
- DisplayList::kReplayFlag_ClipChildren);
+}
+
+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);
+ }
+}
+
+void Layer::defer() {
const float width = layer.getWidth();
const float height = layer.getHeight();
@@ -161,6 +194,14 @@ void Layer::defer() {
dirtyRect.set(0, 0, width, height);
}
+ if (deferredList) {
+ deferredList->reset(dirtyRect);
+ } else {
+ deferredList = new DeferredDisplayList(dirtyRect);
+ }
+ DeferStateStruct deferredState(*deferredList, *renderer,
+ DisplayList::kReplayFlag_ClipChildren);
+
renderer->initViewport(width, height);
renderer->setupFrameState(dirtyRect.left, dirtyRect.top,
dirtyRect.right, dirtyRect.bottom, !isBlend());
@@ -170,8 +211,19 @@ void Layer::defer() {
deferredUpdateScheduled = false;
}
-void Layer::flush() {
+void Layer::cancelDefer() {
+ renderer = NULL;
+ displayList = NULL;
+ deferredUpdateScheduled = false;
if (deferredList) {
+ delete deferredList;
+ deferredList = NULL;
+ }
+}
+
+void Layer::flush() {
+ // renderer is checked as layer may be destroyed/put in layer cache with flush scheduled
+ if (deferredList && renderer) {
renderer->setViewport(layer.getWidth(), layer.getHeight());
renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
!isBlend());
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 7186603..b70042f 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;
@@ -275,6 +245,7 @@ struct Layer {
}
void defer();
+ void cancelDefer();
void flush();
void render();
@@ -306,7 +277,6 @@ struct Layer {
* If the layer can be rendered as a mesh, this is non-null.
*/
TextureVertex* mesh;
- uint16_t* meshIndices;
GLsizei meshElementCount;
/**
@@ -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/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index a0709af..6be0146 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -155,9 +155,7 @@ bool LayerCache::put(Layer* layer) {
victim->layer.getHeight());
}
- layer->deferredUpdateScheduled = false;
- layer->renderer = NULL;
- layer->displayList = NULL;
+ layer->cancelDefer();
LayerEntry entry(layer);
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 3e55fff..f8076cc 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -130,10 +130,7 @@ void LayerRenderer::generateMesh() {
if (mLayer->region.isRect() || mLayer->region.isEmpty()) {
if (mLayer->mesh) {
delete[] mLayer->mesh;
- delete[] mLayer->meshIndices;
-
mLayer->mesh = NULL;
- mLayer->meshIndices = NULL;
mLayer->meshElementCount = 0;
}
@@ -154,17 +151,11 @@ void LayerRenderer::generateMesh() {
if (mLayer->mesh && mLayer->meshElementCount < elementCount) {
delete[] mLayer->mesh;
- delete[] mLayer->meshIndices;
-
mLayer->mesh = NULL;
- mLayer->meshIndices = NULL;
}
- bool rebuildIndices = false;
if (!mLayer->mesh) {
mLayer->mesh = new TextureVertex[count * 4];
- mLayer->meshIndices = new uint16_t[elementCount];
- rebuildIndices = true;
}
mLayer->meshElementCount = elementCount;
@@ -173,7 +164,6 @@ void LayerRenderer::generateMesh() {
const float height = mLayer->layer.getHeight();
TextureVertex* mesh = mLayer->mesh;
- uint16_t* indices = mLayer->meshIndices;
for (size_t i = 0; i < count; i++) {
const android::Rect* r = &rects[i];
@@ -187,17 +177,6 @@ void LayerRenderer::generateMesh() {
TextureVertex::set(mesh++, r->right, r->top, u2, v1);
TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
-
- if (rebuildIndices) {
- uint16_t quad = i * 4;
- int index = i * 6;
- indices[index ] = quad; // top-left
- indices[index + 1] = quad + 1; // top-right
- indices[index + 2] = quad + 2; // bottom-left
- indices[index + 3] = quad + 2; // bottom-left
- indices[index + 4] = quad + 1; // top-right
- indices[index + 5] = quad + 3; // bottom-right
- }
}
}
@@ -436,7 +415,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 +446,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 +477,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.cpp b/libs/hwui/Matrix.cpp
index 6a5ea51..ba22071 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -72,7 +72,7 @@ static bool isZero(float f) {
return fabs(f) <= EPSILON;
}
-uint32_t Matrix4::getType() const {
+uint8_t Matrix4::getType() const {
if (mType & kTypeUnknown) {
mType = kTypeIdentity;
@@ -114,7 +114,7 @@ uint32_t Matrix4::getType() const {
return mType;
}
-uint32_t Matrix4::getGeometryType() const {
+uint8_t Matrix4::getGeometryType() const {
return getType() & sGeometryMask;
}
@@ -122,12 +122,16 @@ bool Matrix4::rectToRect() const {
return getType() & kTypeRectToRect;
}
+bool Matrix4::positiveScale() const {
+ return (data[kScaleX] > 0.0f && data[kScaleY] > 0.0f);
+}
+
bool Matrix4::changesBounds() const {
return getType() & (kTypeScale | kTypeAffine | kTypePerspective);
}
bool Matrix4::isPureTranslate() const {
- return getGeometryType() == kTypeTranslate;
+ return getGeometryType() <= kTypeTranslate;
}
bool Matrix4::isSimple() const {
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 75e280c..b861ba4 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -26,6 +26,12 @@
namespace android {
namespace uirenderer {
+#define MATRIX_STRING "[%.2f %.2f %.2f] [%.2f %.2f %.2f] [%.2f %.2f %.2f]"
+#define MATRIX_ARGS(m) \
+ (m)->get(0), (m)->get(1), (m)->get(2), \
+ (m)->get(3), (m)->get(4), (m)->get(5), \
+ (m)->get(6), (m)->get(7), (m)->get(8)
+
///////////////////////////////////////////////////////////////////////////////
// Classes
///////////////////////////////////////////////////////////////////////////////
@@ -118,7 +124,7 @@ public:
void loadOrtho(float left, float right, float bottom, float top, float near, float far);
- uint32_t getType() const;
+ uint8_t getType() const;
void multiply(const Matrix4& v) {
Matrix4 u;
@@ -128,10 +134,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
+ uint8_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) {
@@ -160,6 +183,7 @@ public:
bool isIdentity() const;
bool isPerspective() const;
bool rectToRect() const;
+ bool positiveScale() const;
bool changesBounds() const;
@@ -179,7 +203,7 @@ public:
static const Matrix4& identity();
private:
- mutable uint32_t mType;
+ mutable uint8_t mType;
inline float get(int i, int j) const {
return data[i * 4 + j];
@@ -189,7 +213,7 @@ private:
data[i * 4 + j] = v;
}
- uint32_t getGeometryType() const;
+ uint8_t getGeometryType() const;
}; // class Matrix4
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 722cc63..4d76bed 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>
@@ -34,6 +33,7 @@
#include "OpenGLRenderer.h"
#include "DeferredDisplayList.h"
#include "DisplayListRenderer.h"
+#include "Fence.h"
#include "PathTessellator.h"
#include "Properties.h"
#include "Vector.h"
@@ -107,6 +107,15 @@ static const Blender gBlendsSwap[] = {
};
///////////////////////////////////////////////////////////////////////////////
+// Functions
+///////////////////////////////////////////////////////////////////////////////
+
+template<typename T>
+static inline T min(T a, T b) {
+ return a < b ? a : b;
+}
+
+///////////////////////////////////////////////////////////////////////////////
// Constructors/destructor
///////////////////////////////////////////////////////////////////////////////
@@ -120,6 +129,7 @@ OpenGLRenderer::OpenGLRenderer():
mFirstSnapshot = new Snapshot;
mFrameStarted = false;
+ mCountOverdraw = false;
mScissorOptimizationDisabled = false;
}
@@ -222,6 +232,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 +264,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 +346,10 @@ void OpenGLRenderer::finish() {
#endif
}
+ if (mCountOverdraw) {
+ countOverdraw();
+ }
+
mFrameStarted = false;
}
@@ -345,6 +360,7 @@ void OpenGLRenderer::interrupt() {
mCaches.currentProgram = NULL;
}
}
+ mCaches.resetActiveTexture();
mCaches.unbindMeshBuffer();
mCaches.unbindIndicesBuffer();
mCaches.resetVertexPointers();
@@ -366,6 +382,7 @@ void OpenGLRenderer::resume() {
dirtyClip();
mCaches.activeTexture(0);
+ mCaches.resetBoundTextures();
mCaches.blend = true;
glEnable(GL_BLEND);
@@ -432,13 +449,8 @@ status_t OpenGLRenderer::invokeFunctors(Rect& dirty) {
status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
- interrupt();
detachFunctor(functor);
- mCaches.enableScissor();
- if (mDirtyClip) {
- setScissorFromClip();
- }
Rect clip(*mSnapshot->clipRect);
clip.snapToPixelBoundaries();
@@ -459,7 +471,18 @@ 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;
+ bool dirtyClip = mDirtyClip;
+ // setup GL state for functor
+ if (mDirtyClip) {
+ setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt()
+ }
+ if (mCaches.enableScissor() || dirtyClip) {
+ setScissorFromClip();
+ }
+ interrupt();
+
+ // call functor immediately after GL state setup
+ status_t result = (*functor)(DrawGlInfo::kModeDraw, &info);
if (result != DrawGlInfo::kStatusDone) {
Rect localDirty(info.dirtyLeft, info.dirtyTop, info.dirtyRight, info.dirtyBottom);
@@ -471,7 +494,7 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
}
resume();
- return result;
+ return result | DrawGlInfo::kStatusDrew;
}
///////////////////////////////////////////////////////////////////////////////
@@ -512,18 +535,41 @@ void OpenGLRenderer::renderOverdraw() {
mCaches.setScissor(clip->left, mFirstSnapshot->height - clip->bottom,
clip->right - clip->left, clip->bottom - clip->top);
+ // 1x overdraw
mCaches.stencil.enableDebugTest(2);
- drawColor(0x2f0000ff, SkXfermode::kSrcOver_Mode);
+ drawColor(mCaches.getOverdrawColor(1), SkXfermode::kSrcOver_Mode);
+
+ // 2x overdraw
mCaches.stencil.enableDebugTest(3);
- drawColor(0x2f00ff00, SkXfermode::kSrcOver_Mode);
+ drawColor(mCaches.getOverdrawColor(2), SkXfermode::kSrcOver_Mode);
+
+ // 3x overdraw
mCaches.stencil.enableDebugTest(4);
- drawColor(0x3fff0000, SkXfermode::kSrcOver_Mode);
+ drawColor(mCaches.getOverdrawColor(3), SkXfermode::kSrcOver_Mode);
+
+ // 4x overdraw and higher
mCaches.stencil.enableDebugTest(4, true);
- drawColor(0x7fff0000, SkXfermode::kSrcOver_Mode);
+ drawColor(mCaches.getOverdrawColor(4), SkXfermode::kSrcOver_Mode);
+
mCaches.stencil.disable();
}
}
+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
///////////////////////////////////////////////////////////////////////////////
@@ -531,6 +577,8 @@ void OpenGLRenderer::renderOverdraw() {
bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
if (layer->deferredUpdateScheduled && layer->renderer &&
layer->displayList && layer->displayList->isRenderable()) {
+ ATRACE_CALL();
+
Rect& dirty = layer->dirtyRect;
if (inFrame) {
@@ -598,8 +646,11 @@ void OpenGLRenderer::flushLayers() {
sprintf(layerName, "Layer #%d", i);
startMark(layerName);
+ ATRACE_BEGIN("flushLayer");
Layer* layer = mLayerUpdates.itemAt(i);
layer->flush();
+ ATRACE_END();
+
mCaches.resourceCache.decrementRefcount(layer);
endMark();
@@ -628,6 +679,18 @@ void OpenGLRenderer::pushLayerUpdate(Layer* layer) {
}
}
+void OpenGLRenderer::cancelLayerUpdate(Layer* layer) {
+ if (layer) {
+ for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
+ if (mLayerUpdates.itemAt(i) == layer) {
+ mLayerUpdates.removeAt(i);
+ mCaches.resourceCache.decrementRefcount(layer);
+ break;
+ }
+ }
+ }
+}
+
void OpenGLRenderer::clearLayerUpdates() {
size_t count = mLayerUpdates.size();
if (count > 0) {
@@ -640,6 +703,14 @@ void OpenGLRenderer::clearLayerUpdates() {
}
}
+void OpenGLRenderer::flushLayerUpdates() {
+ syncState();
+ updateLayers();
+ flushLayers();
+ // Wait for all the layer updates to be executed
+ AutoFence fence;
+}
+
///////////////////////////////////////////////////////////////////////////////
// State management
///////////////////////////////////////////////////////////////////////////////
@@ -780,6 +851,7 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float
if (!mSnapshot->isIgnored()) {
mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
+ mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
}
}
@@ -961,6 +1033,10 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
const Rect& rect = layer->layer;
const bool fboLayer = current->flags & Snapshot::kFlagIsFboLayer;
+ bool clipRequired = false;
+ quickRejectNoScissor(rect, &clipRequired); // safely ignore return, should never be rejected
+ mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
+
if (fboLayer) {
endTiling();
@@ -1019,7 +1095,7 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) {
}
void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
- float alpha = layer->getAlpha() / 255.0f * mSnapshot->alpha;
+ float alpha = getLayerAlpha(layer);
setupDraw();
if (layer->getRenderTarget() == GL_TEXTURE_2D) {
@@ -1055,8 +1131,6 @@ void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
setupDrawMesh(&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
-
- finishDrawTexture();
}
void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
@@ -1125,8 +1199,6 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
return;
}
- // TODO: See LayerRenderer.cpp::generateMesh() for important
- // information about this implementation
if (CC_LIKELY(!layer->region.isEmpty())) {
size_t count;
const android::Rect* rects;
@@ -1149,7 +1221,7 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
// after we setup drawing in case we need to mess with the
// stencil buffer in setupDraw()
TextureVertex* mesh = mCaches.getRegionMesh();
- GLsizei numQuads = 0;
+ uint32_t numQuads = 0;
setupDrawWithTexture();
setupDrawColor(alpha, alpha, alpha, alpha);
@@ -1188,7 +1260,7 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
numQuads++;
- if (numQuads >= REGION_MESH_QUAD_COUNT) {
+ if (numQuads >= gMaxNumberOfQuads) {
DRAW_DOUBLE_STENCIL(glDrawElements(GL_TRIANGLES, numQuads * 6,
GL_UNSIGNED_SHORT, NULL));
numQuads = 0;
@@ -1201,8 +1273,6 @@ void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) {
GL_UNSIGNED_SHORT, NULL));
}
- finishDrawTexture();
-
#if DEBUG_LAYERS_AS_REGIONS
drawRegionRects(layer->region);
#endif
@@ -1239,7 +1309,6 @@ void OpenGLRenderer::drawRegionRects(const Region& region) {
void OpenGLRenderer::drawRegionRects(const SkRegion& region, int color,
SkXfermode::Mode mode, bool dirty) {
- int count = 0;
Vector<float> rects;
SkRegion::Iterator it(region);
@@ -1249,11 +1318,10 @@ void OpenGLRenderer::drawRegionRects(const SkRegion& region, int color,
rects.push(r.fTop);
rects.push(r.fRight);
rects.push(r.fBottom);
- count += 4;
it.next();
}
- drawColorRects(rects.array(), count, color, mode, true, dirty, false);
+ drawColorRects(rects.array(), rects.size(), color, mode, true, dirty, false);
}
void OpenGLRenderer::dirtyLayer(const float left, const float top,
@@ -1283,6 +1351,21 @@ void OpenGLRenderer::dirtyLayerUnchecked(Rect& bounds, Region* region) {
}
}
+void OpenGLRenderer::drawIndexedQuads(Vertex* mesh, GLsizei quadsCount) {
+ GLsizei elementsCount = quadsCount * 6;
+ while (elementsCount > 0) {
+ GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6);
+
+ setupDrawIndexedVertices(&mesh[0].position[0]);
+ glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL);
+
+ elementsCount -= drawCount;
+ // Though there are 4 vertices in a quad, we use 6 indices per
+ // quad to draw with GL_TRIANGLES
+ mesh += (drawCount / 6) * 4;
+ }
+}
+
void OpenGLRenderer::clearLayerRegions() {
const size_t count = mLayers.size();
if (count == 0) return;
@@ -1297,17 +1380,15 @@ void OpenGLRenderer::clearLayerRegions() {
// is likely different so we need to disable clipping here
bool scissorChanged = mCaches.disableScissor();
- Vertex mesh[count * 6];
+ Vertex mesh[count * 4];
Vertex* vertex = mesh;
for (uint32_t i = 0; i < count; i++) {
Rect* bounds = mLayers.itemAt(i);
- Vertex::set(vertex++, bounds->left, bounds->bottom);
Vertex::set(vertex++, bounds->left, bounds->top);
Vertex::set(vertex++, bounds->right, bounds->top);
Vertex::set(vertex++, bounds->left, bounds->bottom);
- Vertex::set(vertex++, bounds->right, bounds->top);
Vertex::set(vertex++, bounds->right, bounds->bottom);
delete bounds;
@@ -1323,9 +1404,8 @@ void OpenGLRenderer::clearLayerRegions() {
setupDrawProgram();
setupDrawPureColorUniforms();
setupDrawModelViewTranslate(0.0f, 0.0f, 0.0f, 0.0f, true);
- setupDrawVertices(&mesh[0].position[0]);
- glDrawArrays(GL_TRIANGLES, 0, count * 6);
+ drawIndexedQuads(&mesh[0], count);
if (scissorChanged) mCaches.enableScissor();
} else {
@@ -1348,11 +1428,30 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
// state has bounds initialized in local coordinates
if (!state.mBounds.isEmpty()) {
currentMatrix.mapRect(state.mBounds);
- if (!state.mBounds.intersect(currentClip)) {
+ Rect clippedBounds(state.mBounds);
+ // NOTE: if we ever want to use this clipping info to drive whether the scissor
+ // is used, it should more closely duplicate the quickReject logic (in how it uses
+ // snapToPixelBoundaries)
+
+ if(!clippedBounds.intersect(currentClip)) {
// quick rejected
return true;
}
+
+ state.mClipSideFlags = kClipSide_None;
+ if (!currentClip.contains(state.mBounds)) {
+ int& flags = state.mClipSideFlags;
+ // op partially clipped, so record which sides are clipped for clip-aware merging
+ if (currentClip.left > state.mBounds.left) flags |= kClipSide_Left;
+ if (currentClip.top > state.mBounds.top) flags |= kClipSide_Top;
+ if (currentClip.right < state.mBounds.right) flags |= kClipSide_Right;
+ if (currentClip.bottom < state.mBounds.bottom) flags |= kClipSide_Bottom;
+ }
+ state.mBounds.set(clippedBounds);
} else {
+ // Empty bounds implies size unknown. Label op as conservatively clipped to disable
+ // overdraw avoidance (since we don't know what it overlaps)
+ state.mClipSideFlags = kClipSide_ConservativeFull;
state.mBounds.set(currentClip);
}
}
@@ -1376,14 +1475,27 @@ void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool
mSnapshot->alpha = state.mAlpha;
if (state.mClipValid && !skipClipRestore) {
- mSnapshot->setClip(state.mClip.left, state.mClip.top, state.mClip.right, state.mClip.bottom);
+ mSnapshot->setClip(state.mClip.left, state.mClip.top,
+ state.mClip.right, state.mClip.bottom);
dirtyClip();
}
}
-void OpenGLRenderer::setFullScreenClip() {
- mSnapshot->setClip(0, 0, mWidth, mHeight);
+/**
+ * Merged multidraw (such as in drawText and drawBitmaps rely on the fact that no clipping is done
+ * in the draw path. Instead, clipping is done ahead of time - either as a single clip rect (when at
+ * least one op is clipped), or disabled entirely (because no merged op is clipped)
+ *
+ * This method should be called when restoreDisplayState() won't be restoring the clip
+ */
+void OpenGLRenderer::setupMergedMultiDraw(const Rect* clipRect) {
+ if (clipRect != NULL) {
+ mSnapshot->setClip(clipRect->left, clipRect->top, clipRect->right, clipRect->bottom);
+ } else {
+ mSnapshot->setClip(0, 0, mWidth, mHeight);
+ }
dirtyClip();
+ mCaches.setScissorEnabled(clipRect != NULL || mScissorOptimizationDisabled);
}
///////////////////////////////////////////////////////////////////////////////
@@ -1391,7 +1503,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) {
@@ -1514,65 +1626,49 @@ const Rect& OpenGLRenderer::getClipBounds() {
return mSnapshot->getLocalClip();
}
-bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, float bottom) {
- if (mSnapshot->isIgnored()) {
+bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, float bottom,
+ bool snapOut, bool* clipRequired) {
+ if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
return true;
}
Rect r(left, top, right, bottom);
currentTransform().mapRect(r);
- r.snapToPixelBoundaries();
+ r.snapGeometryToPixelBoundaries(snapOut);
Rect clipRect(*mSnapshot->clipRect);
clipRect.snapToPixelBoundaries();
- return !clipRect.intersects(r);
-}
-
-bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, float bottom,
- Rect& transformed, Rect& clip) {
- if (mSnapshot->isIgnored()) {
- return true;
- }
-
- transformed.set(left, top, right, bottom);
- currentTransform().mapRect(transformed);
- transformed.snapToPixelBoundaries();
+ if (!clipRect.intersects(r)) return true;
- clip.set(*mSnapshot->clipRect);
- clip.snapToPixelBoundaries();
-
- return !clip.intersects(transformed);
+ if (clipRequired) *clipRequired = !clipRect.contains(r);
+ return false;
}
bool OpenGLRenderer::quickRejectPreStroke(float left, float top, float right, float bottom,
SkPaint* paint) {
+ // AA geometry will likely have a ramp around it (not accounted for in local bounds). Snap out
+ // the final mapped rect to ensure correct clipping behavior for the ramp.
+ bool snapOut = paint->isAntiAlias();
+
if (paint->getStyle() != SkPaint::kFill_Style) {
float outset = paint->getStrokeWidth() * 0.5f;
- return quickReject(left - outset, top - outset, right + outset, bottom + outset);
+ return quickReject(left - outset, top - outset, right + outset, bottom + outset, snapOut);
} else {
- return quickReject(left, top, right, bottom);
+ return quickReject(left, top, right, bottom, snapOut);
}
}
-bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) {
- if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
+bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom, bool snapOut) {
+ bool clipRequired = false;
+ if (quickRejectNoScissor(left, top, right, bottom, snapOut, &clipRequired)) {
return true;
}
- Rect r(left, top, right, bottom);
- currentTransform().mapRect(r);
- r.snapToPixelBoundaries();
-
- Rect clipRect(*mSnapshot->clipRect);
- clipRect.snapToPixelBoundaries();
-
- bool rejected = !clipRect.intersects(r);
- if (!isDeferred() && !rejected) {
- mCaches.setScissorEnabled(mScissorOptimizationDisabled || !clipRect.contains(r));
+ if (!isDeferred()) {
+ mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
}
-
- return rejected;
+ return false;
}
void OpenGLRenderer::debugClip() {
@@ -1595,7 +1691,7 @@ bool OpenGLRenderer::clipRect(float left, float top, float right, float bottom,
SkPath path;
path.addRect(left, top, right, bottom);
- return clipPath(&path, op);
+ return OpenGLRenderer::clipPath(&path, op);
}
bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) {
@@ -1606,11 +1702,15 @@ bool OpenGLRenderer::clipPath(SkPath* path, SkRegion::Op op) {
path->transform(transform, &transformed);
SkRegion clip;
- if (!mSnapshot->clipRegion->isEmpty()) {
- clip.setRegion(*mSnapshot->clipRegion);
+ if (!mSnapshot->previous->clipRegion->isEmpty()) {
+ clip.setRegion(*mSnapshot->previous->clipRegion);
} else {
- Rect* bounds = mSnapshot->clipRect;
- clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom);
+ if (mSnapshot->previous == mFirstSnapshot) {
+ clip.setRect(0, 0, mWidth, mHeight);
+ } else {
+ Rect* bounds = mSnapshot->previous->clipRect;
+ clip.setRect(bounds->left, bounds->top, bounds->right, bounds->bottom);
+ }
}
SkRegion region;
@@ -1665,6 +1765,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) {
@@ -1690,11 +1792,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;
@@ -1809,11 +1906,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);
@@ -1882,7 +1974,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();
@@ -1913,21 +2005,28 @@ 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);
}
}
-void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) {
+void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) {
bool force = mCaches.unbindMeshBuffer();
+ mCaches.bindIndicesBuffer();
mCaches.bindPositionVertexPointer(force, vertices, gVertexStride);
- mCaches.unbindIndicesBuffer();
-}
-
-void OpenGLRenderer::finishDrawTexture() {
}
///////////////////////////////////////////////////////////////////////////////
@@ -1947,7 +2046,8 @@ status_t OpenGLRenderer::drawDisplayList(DisplayList* displayList, Rect& dirty,
return status | replayStruct.mDrawGlStatus;
}
- DeferredDisplayList deferredList;
+ bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs!
+ DeferredDisplayList deferredList(*(mSnapshot->clipRect), avoidOverdraw);
DeferStateStruct deferStruct(deferredList, *this, replayFlags);
displayList->defer(deferStruct, 0);
@@ -1989,20 +2089,24 @@ 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) {
-
- // merged draw operations don't need scissor, but clip should still be valid
- mCaches.setScissorEnabled(mScissorOptimizationDisabled);
-
+/**
+ * Important note: this method is intended to draw batches of bitmaps and
+ * will not set the scissor enable or dirty the current layer, if any.
+ * The caller is responsible for properly dirtying the current layer.
+ */
+status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
+ TextureVertex* vertices, bool pureTranslate, const Rect& bounds, SkPaint* paint) {
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);
int alpha;
@@ -2010,7 +2114,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(pureTranslate ? GL_NEAREST : FILTER(paint), true);
const float x = (int) floorf(bounds.left + 0.5f);
const float y = (int) floorf(bounds.top + 0.5f);
@@ -2019,12 +2123,12 @@ status_t OpenGLRenderer::drawBitmaps(SkBitmap* bitmap, int bitmapCount, TextureV
drawAlpha8TextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
texture->id, paint != NULL, color, alpha, mode,
&vertices[0].position[0], &vertices[0].texture[0],
- GL_TRIANGLES, bitmapCount * 6, true, true);
+ GL_TRIANGLES, bitmapCount * 6, true, true, false);
} else {
drawTextureMesh(x, y, x + bounds.getWidth(), y + bounds.getHeight(),
texture->id, alpha / 255.0f, mode, texture->blend,
&vertices[0].position[0], &vertices[0].texture[0],
- GL_TRIANGLES, bitmapCount * 6, false, true, 0, true);
+ GL_TRIANGLES, bitmapCount * 6, false, true, 0, true, false);
}
return DrawGlInfo::kStatusDrew;
@@ -2039,7 +2143,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);
@@ -2062,7 +2166,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);
@@ -2107,6 +2211,9 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes
return DrawGlInfo::kStatusDone;
}
+ // TODO: use quickReject on bounds from vertices
+ mCaches.enableScissor();
+
float left = FLT_MAX;
float top = FLT_MAX;
float right = FLT_MIN;
@@ -2125,6 +2232,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;
@@ -2134,6 +2245,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;
@@ -2163,11 +2276,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);
@@ -2199,8 +2313,6 @@ status_t OpenGLRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int mes
glDrawArrays(GL_TRIANGLES, 0, count);
- finishDrawTexture();
-
int slot = mCaches.currentProgram->getAttrib("colors");
if (slot >= 0) {
glDisableVertexAttribArray(slot);
@@ -2220,17 +2332,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);
@@ -2301,37 +2415,38 @@ 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);
+ if (quickReject(left, top, right, bottom)) {
+ return DrawGlInfo::kStatusDone;
+ }
+
+ AssetAtlas::Entry* entry = mCaches.assetAtlas.getEntry(bitmap);
+ const Patch* mesh = mCaches.patchCache.get(entry, bitmap->width(), bitmap->height(),
+ right - left, bottom - top, patch);
- return drawPatch(bitmap, xDivs, yDivs, colors, width, height, numColors,
- left, top, right, bottom, alpha, mode);
+ return drawPatch(bitmap, mesh, entry, left, top, right, bottom, paint);
}
-status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs,
- const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors,
- float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode) {
+status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const Patch* mesh, AssetAtlas::Entry* entry,
+ float left, float top, float right, float bottom, SkPaint* paint) {
if (quickReject(left, top, right, bottom)) {
return DrawGlInfo::kStatusDone;
}
- alpha *= mSnapshot->alpha;
-
- const Patch* mesh = mCaches.patchCache.get(bitmap->width(), bitmap->height(),
- right - left, bottom - top, xDivs, yDivs, colors, width, height, numColors);
-
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);
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
+
const bool pureTranslate = currentTransform().isPureTranslate();
// Mark the current layer dirty where we are going to draw the patch
if (hasLayer() && mesh->hasEmptyQuads) {
@@ -2355,24 +2470,52 @@ status_t OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const
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);
}
}
return DrawGlInfo::kStatusDrew;
}
+/**
+ * Important note: this method is intended to draw batches of 9-patch objects and
+ * will not set the scissor enable or dirty the current layer, if any.
+ * The caller is responsible for properly dirtying the current layer.
+ */
+status_t OpenGLRenderer::drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry,
+ TextureVertex* vertices, uint32_t indexCount, SkPaint* paint) {
+ mCaches.activeTexture(0);
+ 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);
+
+ int alpha;
+ SkXfermode::Mode mode;
+ getAlphaAndMode(paint, &alpha, &mode);
+
+ drawIndexedTextureMesh(0.0f, 0.0f, 1.0f, 1.0f, texture->id, alpha / 255.0f,
+ mode, texture->blend, &vertices[0].position[0], &vertices[0].texture[0],
+ GL_TRIANGLES, indexCount, false, true, 0, true, false);
+
+ return DrawGlInfo::kStatusDrew;
+}
+
status_t OpenGLRenderer::drawVertexBuffer(const VertexBuffer& vertexBuffer, SkPaint* paint,
bool useOffset) {
- if (!vertexBuffer.getSize()) {
+ if (!vertexBuffer.getVertexCount()) {
// no vertices to draw
return DrawGlInfo::kStatusDone;
}
@@ -2410,7 +2553,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);
@@ -2473,65 +2616,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
-
- // 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;
-
- 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);
+ if (mSnapshot->isIgnored() || count < 2) return DrawGlInfo::kStatusDone;
- for (int i = 0; i < count; i += 2) {
- TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f);
- generatedVerticesCount++;
+ count &= ~0x1; // round down to nearest two
- 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) {
@@ -2747,48 +2847,6 @@ bool OpenGLRenderer::canSkipText(const SkPaint* paint) const {
return alpha == 0.0f && getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode;
}
-class TextSetupFunctor: public Functor {
-public:
- TextSetupFunctor(OpenGLRenderer& renderer, float x, float y, bool pureTranslate,
- int alpha, SkXfermode::Mode mode, SkPaint* paint): Functor(),
- renderer(renderer), x(x), y(y), pureTranslate(pureTranslate),
- alpha(alpha), mode(mode), paint(paint) {
- }
- ~TextSetupFunctor() { }
-
- status_t operator ()(int what, void* data) {
- renderer.setupDraw();
- renderer.setupDrawTextGamma(paint);
- renderer.setupDrawDirtyRegionsDisabled();
- renderer.setupDrawWithTexture(true);
- renderer.setupDrawAlpha8Color(paint->getColor(), alpha);
- renderer.setupDrawColorFilter();
- renderer.setupDrawShader();
- renderer.setupDrawBlending(true, mode);
- renderer.setupDrawProgram();
- renderer.setupDrawModelView(x, y, x, y, pureTranslate, true);
- // Calling setupDrawTexture with the name 0 will enable the
- // uv attributes and increase the texture unit count
- // texture binding will be performed by the font renderer as
- // needed
- renderer.setupDrawTexture(0);
- renderer.setupDrawPureColorUniforms();
- renderer.setupDrawColorFilterUniforms();
- renderer.setupDrawShaderUniforms(pureTranslate);
- renderer.setupDrawTextGammaUniforms();
-
- return NO_ERROR;
- }
-
- OpenGLRenderer& renderer;
- float x;
- float y;
- bool pureTranslate;
- int alpha;
- SkXfermode::Mode mode;
- SkPaint* paint;
-};
-
status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
const float* positions, SkPaint* paint) {
if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint)) {
@@ -2800,6 +2858,8 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count
return DrawGlInfo::kStatusDone;
}
+ mCaches.enableScissor();
+
float x = 0.0f;
float y = 0.0f;
const bool pureTranslate = currentTransform().isPureTranslate();
@@ -2832,7 +2892,7 @@ status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count
const bool hasActiveLayer = hasLayer();
- TextSetupFunctor functor(*this, x, y, pureTranslate, alpha, mode, paint);
+ TextSetupFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
if (fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y,
positions, hasActiveLayer ? &bounds : NULL, &functor)) {
if (hasActiveLayer) {
@@ -2862,36 +2922,17 @@ 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 &&
- (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint))) {
- 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)) {
+ // The checks for corner-case ignorable text and quick rejection is only done for immediate
+ // drawing as ops from DeferredDisplayList are already filtered for these
+ if (text == NULL || count == 0 || mSnapshot->isIgnored() || canSkipText(paint) ||
+ quickReject(bounds)) {
return DrawGlInfo::kStatusDone;
}
- } else {
- // merged draw operations don't need scissor, but clip should still be valid
- mCaches.setScissorEnabled(mScissorOptimizationDisabled);
}
const float oldX = x;
@@ -2939,10 +2980,10 @@ 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);
+ TextSetupFunctor functor(this, x, y, pureTranslate, alpha, mode, paint);
// don't call issuedrawcommand, do it at end of batch
bool forceFinish = (drawOpMode != kDrawOpMode_Defer);
@@ -2950,20 +2991,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;
}
@@ -2974,6 +3015,9 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co
return DrawGlInfo::kStatusDone;
}
+ // TODO: avoid scissor by calculating maximum bounds using path bounds + font metrics
+ mCaches.enableScissor();
+
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(paint);
fontRenderer.setFont(paint, mat4::identity());
fontRenderer.setTextureFiltering(true);
@@ -2981,26 +3025,7 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co
int alpha;
SkXfermode::Mode mode;
getAlphaAndMode(paint, &alpha, &mode);
-
- setupDraw();
- setupDrawTextGamma(paint);
- setupDrawDirtyRegionsDisabled();
- setupDrawWithTexture(true);
- setupDrawAlpha8Color(paint->getColor(), alpha);
- setupDrawColorFilter();
- setupDrawShader();
- setupDrawBlending(true, mode);
- setupDrawProgram();
- setupDrawModelView(0.0f, 0.0f, 0.0f, 0.0f, false, true);
- // Calling setupDrawTexture with the name 0 will enable the
- // uv attributes and increase the texture unit count
- // texture binding will be performed by the font renderer as
- // needed
- setupDrawTexture(0);
- setupDrawPureColorUniforms();
- setupDrawColorFilterUniforms();
- setupDrawShaderUniforms(false);
- setupDrawTextGammaUniforms();
+ TextSetupFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint);
const Rect* clip = &mSnapshot->getLocalClip();
Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f);
@@ -3008,7 +3033,7 @@ status_t OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int co
const bool hasActiveLayer = hasLayer();
if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path,
- hOffset, vOffset, hasActiveLayer ? &bounds : NULL)) {
+ hOffset, vOffset, hasActiveLayer ? &bounds : NULL, &functor)) {
if (hasActiveLayer) {
currentTransform().mapRect(bounds);
dirtyLayerUnchecked(bounds, getRegion());
@@ -3049,10 +3074,9 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
}
}
- Rect transformed;
- Rect clip;
+ bool clipRequired = false;
const bool rejected = quickRejectNoScissor(x, y,
- x + layer->layer.getWidth(), y + layer->layer.getHeight(), transformed, clip);
+ x + layer->layer.getWidth(), y + layer->layer.getHeight(), false, &clipRequired);
if (rejected) {
if (transform && !transform->isIdentity()) {
@@ -3063,7 +3087,7 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
updateLayer(layer, true);
- mCaches.setScissorEnabled(mScissorOptimizationDisabled || !clip.contains(transformed));
+ mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired);
mCaches.activeTexture(0);
if (CC_LIKELY(!layer->region.isEmpty())) {
@@ -3096,13 +3120,22 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
setupDrawModelViewTranslate(x, y,
x + layer->layer.getWidth(), y + layer->layer.getHeight());
}
- setupDrawMesh(&layer->mesh[0].position[0], &layer->mesh[0].texture[0]);
- DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
- glDrawElements(GL_TRIANGLES, layer->meshElementCount,
- GL_UNSIGNED_SHORT, layer->meshIndices));
+ TextureVertex* mesh = &layer->mesh[0];
+ GLsizei elementsCount = layer->meshElementCount;
+
+ while (elementsCount > 0) {
+ GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6);
- finishDrawTexture();
+ setupDrawMeshIndices(&mesh[0].position[0], &mesh[0].texture[0]);
+ DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
+ glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL));
+
+ elementsCount -= drawCount;
+ // Though there are 4 vertices in a quad, we use 6 indices per
+ // quad to draw with GL_TRIANGLES
+ mesh += (drawCount / 6) * 4;
+ }
#if DEBUG_LAYERS_AS_REGIONS
drawRegionRects(layer->region);
@@ -3137,7 +3170,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);
}
}
@@ -3205,6 +3238,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,8 +3271,6 @@ void OpenGLRenderer::drawPathTexture(const PathTexture* texture,
setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
-
- finishDrawTexture();
}
// Same values used by Skia
@@ -3239,17 +3278,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();
@@ -3315,8 +3349,7 @@ status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color
float right = FLT_MIN;
float bottom = FLT_MIN;
- int vertexCount = 0;
- Vertex mesh[count * 6];
+ Vertex mesh[count];
Vertex* vertex = mesh;
for (int index = 0; index < count; index += 4) {
@@ -3325,15 +3358,11 @@ status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color
float r = rects[index + 2];
float b = rects[index + 3];
- Vertex::set(vertex++, l, b);
Vertex::set(vertex++, l, t);
Vertex::set(vertex++, r, t);
Vertex::set(vertex++, l, b);
- Vertex::set(vertex++, r, t);
Vertex::set(vertex++, r, b);
- vertexCount += 6;
-
left = fminf(left, l);
top = fminf(top, t);
right = fmaxf(right, r);
@@ -3356,13 +3385,12 @@ status_t OpenGLRenderer::drawColorRects(const float* rects, int count, int color
setupDrawColorUniforms();
setupDrawShaderUniforms();
setupDrawColorFilterUniforms();
- setupDrawVertices((GLvoid*) &mesh[0].position[0]);
if (dirty && hasLayer()) {
dirtyLayer(left, top, right, bottom, currentTransform());
}
- glDrawArrays(GL_TRIANGLES, 0, vertexCount);
+ drawIndexedQuads(&mesh[0], count / 4);
return DrawGlInfo::kStatusDrew;
}
@@ -3398,19 +3426,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);
}
}
@@ -3443,8 +3487,31 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b
setupDrawMesh(vertices, texCoords, vbo);
glDrawArrays(drawMode, 0, elementsCount);
+}
+
+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) {
- finishDrawTexture();
+ 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);
}
void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, float bottom,
@@ -3474,12 +3541,23 @@ void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, f
setupDrawMesh(vertices, texCoords);
glDrawArrays(drawMode, 0, elementsCount);
-
- finishDrawTexture();
}
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..9afb7ad 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,12 +45,21 @@
#include "Vertex.h"
#include "SkiaShader.h"
#include "SkiaColorFilter.h"
+#include "UvMapper.h"
#include "Caches.h"
namespace android {
namespace uirenderer {
struct DrawModifiers {
+ DrawModifiers() {
+ reset();
+ }
+
+ void reset() {
+ memset(this, 0, sizeof(DrawModifiers));
+ }
+
SkiaShader* mShader;
SkiaColorFilter* mColorFilter;
float mOverrideLayerAlpha;
@@ -77,21 +88,21 @@ enum DrawOpMode {
kDrawOpMode_Flush
};
-struct DeferredDisplayState {
- Rect mBounds; // global op bounds, mapped by mMatrix to be in screen space coordinates, clipped.
-
- // the below are set and used by the OpenGLRenderer at record and deferred playback
- bool mClipValid;
- Rect mClip;
- mat4 mMatrix;
- DrawModifiers mDrawModifiers;
- float mAlpha;
+enum ClipSideFlags {
+ kClipSide_None = 0x0,
+ kClipSide_Left = 0x1,
+ kClipSide_Top = 0x2,
+ kClipSide_Right = 0x4,
+ kClipSide_Bottom = 0x8,
+ kClipSide_Full = 0xF,
+ kClipSide_ConservativeFull = 0x1F
};
///////////////////////////////////////////////////////////////////////////////
// Renderer
///////////////////////////////////////////////////////////////////////////////
+class DeferredDisplayState;
class DisplayList;
class TextSetupFunctor;
class VertexBuffer;
@@ -188,13 +199,23 @@ 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);
virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
ANDROID_API void pushLayerUpdate(Layer* layer);
+ ANDROID_API void cancelLayerUpdate(Layer* layer);
ANDROID_API void clearLayerUpdates();
+ ANDROID_API void flushLayerUpdates();
ANDROID_API int getSaveCount() const;
virtual int save(int flags);
@@ -228,8 +249,32 @@ public:
virtual void concatMatrix(SkMatrix* matrix);
ANDROID_API const Rect& getClipBounds();
- ANDROID_API bool quickReject(float left, float top, float right, float bottom);
- bool quickRejectNoScissor(float left, float top, float right, float bottom);
+
+ /**
+ * Performs a quick reject but adjust the bounds to account for stroke width if necessary,
+ * and handling snapOut for AA geometry.
+ */
+ bool quickRejectPreStroke(float left, float top, float right, float bottom, SkPaint* paint);
+
+ /**
+ * Returns false and sets scissor based upon bounds if drawing won't be clipped out
+ */
+ bool quickReject(float left, float top, float right, float bottom, bool snapOut = false);
+ bool quickReject(const Rect& bounds) {
+ return quickReject(bounds.left, bounds.top, bounds.right, bounds.bottom);
+ }
+
+ /**
+ * Same as quickReject, without the scissor, instead returning clipRequired through pointer.
+ * clipRequired will be only set if not rejected
+ */
+ ANDROID_API bool quickRejectNoScissor(float left, float top, float right, float bottom,
+ bool snapOut = false, bool* clipRequired = NULL);
+ bool quickRejectNoScissor(const Rect& bounds, bool* clipRequired = NULL) {
+ return quickRejectNoScissor(bounds.left, bounds.top, bounds.right, bounds.bottom,
+ clipRequired);
+ }
+
virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op);
virtual bool clipPath(SkPath* path, SkRegion::Op op);
virtual bool clipRegion(SkRegion* region, SkRegion::Op op);
@@ -239,8 +284,8 @@ public:
virtual void outputDisplayList(DisplayList* displayList);
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);
+ status_t drawBitmaps(SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount,
+ TextureVertex* vertices, bool pureTranslate, 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,12 +293,12 @@ 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,
+ status_t drawPatches(SkBitmap* bitmap, AssetAtlas::Entry* entry,
+ TextureVertex* vertices, uint32_t indexCount, SkPaint* paint);
+ 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 Patch* mesh, AssetAtlas::Entry* entry,
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,
- 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);
virtual status_t drawRoundRect(float left, float top, float right, float bottom,
@@ -270,7 +315,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);
@@ -291,9 +336,15 @@ public:
SkPaint* filterPaint(SkPaint* paint);
+ /**
+ * Store the current display state (most importantly, the current clip and transform), and
+ * additionally map the state's bounds from local to window coordinates.
+ *
+ * Returns true if quick-rejected
+ */
bool storeDisplayState(DeferredDisplayState& state, int stateDeferFlags);
void restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore = false);
- void setFullScreenClip();
+ void setupMergedMultiDraw(const Rect* clipRect);
const DrawModifiers& getDrawModifiers() { return mDrawModifiers; }
void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; }
@@ -311,6 +362,9 @@ public:
return mSnapshot->clipRegion->isEmpty();
}
+ int getViewportWidth() { return getSnapshot()->viewport.getWidth(); }
+ int getViewportHeight() { return getSnapshot()->viewport.getHeight(); }
+
/**
* Scales the alpha on the current snapshot. This alpha value will be modulated
* with other alpha values when drawing primitives.
@@ -356,7 +410,7 @@ public:
return getXfermode(paint->getXfermode());
}
- static inline int getAlphaDirect(SkPaint* paint) {
+ static inline int getAlphaDirect(const SkPaint* paint) {
if (!paint) return 255;
return paint->getAlpha();
}
@@ -579,18 +633,6 @@ private:
void setStencilFromClip();
/**
- * Performs a quick reject but does not affect the scissor. Returns
- * the transformed rect to test and the current clip.
- */
- bool quickRejectNoScissor(float left, float top, float right, float bottom,
- Rect& transformed, Rect& clip);
-
- /**
- * Performs a quick reject but adjust the bounds to account for stroke width if necessary
- */
- bool quickRejectPreStroke(float left, float top, float right, float bottom, SkPaint* paint);
-
- /**
* Given the local bounds of the layer, calculates ...
*/
void calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer);
@@ -798,22 +840,35 @@ 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,
bool ignoreTransform, bool ignoreScale = false, bool dirty = true);
/**
+ * Draws the specified list of vertices as quads using indexed GL_TRIANGLES.
+ * If the number of vertices to draw exceeds the number of indices we have
+ * pre-allocated, this method will generate several glDrawElements() calls.
+ */
+ void drawIndexedQuads(Vertex* mesh, GLsizei quadsCount);
+
+ /**
* Draws text underline and strike-through if needed.
*
* @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 +923,7 @@ private:
* prior to calling this method.
*/
inline void bindTexture(GLuint texture) {
- glBindTexture(GL_TEXTURE_2D, texture);
+ mCaches.bindTexture(texture);
}
/**
@@ -876,7 +931,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 +966,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 +983,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,9 +996,8 @@ 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 setupDrawVertices(GLvoid* vertices);
- void finishDrawTexture();
+ void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, GLuint vbo = 0);
+ void setupDrawIndexedVertices(GLvoid* vertices);
void accountForClear(SkXfermode::Mode mode);
bool updateLayer(Layer* layer, bool inFrame);
@@ -973,6 +1025,7 @@ private:
void debugOverdraw(bool enable, bool clear);
void renderOverdraw();
+ void countOverdraw();
/**
* Should be invoked every time the glScissor is modified.
@@ -985,6 +1038,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 +1074,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,12 +1117,19 @@ 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;
friend class DisplayListRenderer;
friend class Layer;
friend class TextSetupFunctor;
+ friend class DrawBitmapOp;
+ friend class DrawPatchOp;
}; // class OpenGLRenderer
diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp
index 45c619e..9b023f9 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,58 @@ 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(): vertices(NULL), 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 width, float height, const Res_png_9patch* patch) {
+ UvMapper mapper;
+ return createMesh(bitmapWidth, bitmapHeight, width, height, 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 width, float height, const UvMapper& mapper, const Res_png_9patch* patch) {
+ if (vertices) return vertices;
- bool matches = true;
+ int8_t emptyQuads = 0;
+ mColors = patch->colors;
- 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;
+ const int8_t numColors = patch->numColors;
+ if (uint8_t(numColors) < sizeof(uint32_t) * 4) {
+ for (int8_t i = 0; i < numColors; i++) {
+ if (mColors[i] == 0x0) {
+ emptyQuads++;
+ }
+ }
}
- 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* tempVertices = new TextureVertex[maxVertices];
+ TextureVertex* vertex = tempVertices;
- // 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,29 +93,28 @@ 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;
- const float xStretch = fmaxf(right - left - fixed, 0.0f);
+ const float xStretch = fmaxf(width - fixed, 0.0f);
stretchX = xStretch / xStretchTex;
- rescaleX = fixed == 0.0f ? 0.0f : fminf(fmaxf(right - left, 0.0f) / fixed, 1.0f);
+ rescaleX = fixed == 0.0f ? 0.0f : fminf(fmaxf(width, 0.0f) / fixed, 1.0f);
}
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;
- const float yStretch = fmaxf(bottom - top - fixed, 0.0f);
+ const float yStretch = fmaxf(height - fixed, 0.0f);
stretchY = yStretch / yStretchTex;
- rescaleY = fixed == 0.0f ? 0.0f : fminf(fmaxf(bottom - top, 0.0f) / fixed, 1.0f);
+ rescaleY = fixed == 0.0f ? 0.0f : fminf(fmaxf(height, 0.0f) / fixed, 1.0f);
}
- TextureVertex* vertex = mVertices;
uint32_t quadCount = 0;
float previousStepY = 0.0f;
@@ -155,8 +123,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 +140,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,
+ width, bitmapWidth, quadCount);
}
y1 = y2;
@@ -188,34 +151,25 @@ 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);
+ y2 = height;
+ generateRow(xDivs, xCount, vertex, y1, y2, v1, 1.0f, stretchX, rescaleX,
+ width, 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();
+ if (verticesCount == maxVertices) {
+ vertices = tempVertices;
+ } else {
+ vertices = new TextureVertex[verticesCount];
+ memcpy(vertices, tempVertices, verticesCount * sizeof(TextureVertex));
+ delete[] tempVertices;
}
- 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 +177,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 +192,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 +203,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);
}
}
@@ -275,11 +218,11 @@ void Patch::generateQuad(TextureVertex*& vertex, float x1, float y1, float x2, f
if (y2 < 0.0f) y2 = 0.0f;
// Skip degenerate and transparent (empty) quads
- if (((mColorKey >> oldQuadCount) & 0x1) || x1 >= x2 || y1 >= y2) {
+ if ((mColors[oldQuadCount] == 0) || x1 >= x2 || y1 >= y2) {
#if DEBUG_PATCHES_EMPTY_VERTICES
PATCH_LOGD(" quad %d (empty)", oldQuadCount);
- PATCH_LOGD(" left, top = %.2f, %.2f\t\tu1, v1 = %.4f, %.4f", x1, y1, u1, v1);
- PATCH_LOGD(" right, bottom = %.2f, %.2f\t\tu2, v2 = %.4f, %.4f", x2, y2, u2, v2);
+ PATCH_LOGD(" left, top = %.2f, %.2f\t\tu1, v1 = %.8f, %.8f", x1, y1, u1, v1);
+ PATCH_LOGD(" right, bottom = %.2f, %.2f\t\tu2, v2 = %.8f, %.8f", x2, y2, u2, v2);
#endif
return;
}
@@ -290,23 +233,20 @@ 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);
- PATCH_LOGD(" left, top = %.2f, %.2f\t\tu1, v1 = %.4f, %.4f", x1, y1, u1, v1);
- PATCH_LOGD(" right, bottom = %.2f, %.2f\t\tu2, v2 = %.4f, %.4f", x2, y2, u2, v2);
+ PATCH_LOGD(" left, top = %.2f, %.2f\t\tu1, v1 = %.8f, %.8f", x1, y1, u1, v1);
+ PATCH_LOGD(" right, bottom = %.2f, %.2f\t\tu2, v2 = %.8f, %.8f", x2, y2, u2, v2);
#endif
}
diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h
index ee7bf70..763a785 100644
--- a/libs/hwui/Patch.h
+++ b/libs/hwui/Patch.h
@@ -23,62 +23,51 @@
#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);
+ /**
+ * Returns the size of this patch's mesh in bytes.
+ */
+ uint32_t getSize() const;
- 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);
-
- GLuint meshBuffer;
+ TextureVertex* vertices;
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 width, float height, const Res_png_9patch* patch);
+ TextureVertex* createMesh(const float bitmapWidth, const float bitmapHeight,
+ float width, float height, 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* mColors;
+ UvMapper mUvMapper;
}; // struct Patch
}; // namespace uirenderer
diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp
index 62e38d3..dc0d98c 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,111 +30,243 @@ 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), mFreeBlocks(NULL), 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();
}
+void PatchCache::init(Caches& caches) {
+ bool created = false;
+ if (!mMeshBuffer) {
+ glGenBuffers(1, &mMeshBuffer);
+ created = true;
+ }
+
+ caches.bindMeshBuffer(mMeshBuffer);
+ caches.resetVertexPointers();
+
+ if (created) {
+ createVertexBuffer();
+ }
+}
+
///////////////////////////////////////////////////////////////////////////////
// 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;
+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);
+}
- if (lhs.pixelWidth < rhs.pixelWidth) return -1;
- if (lhs.pixelWidth > rhs.pixelWidth) return +1;
+int PatchCache::PatchDescription::compare(const PatchCache::PatchDescription& lhs,
+ const PatchCache::PatchDescription& rhs) {
+ return memcmp(&lhs, &rhs, sizeof(PatchDescription));
+}
- if (lhs.pixelHeight < rhs.pixelHeight) return -1;
- if (lhs.pixelHeight > rhs.pixelHeight) return +1;
+void PatchCache::clear() {
+ clearCache();
- deltaInt = lhs.xCount - rhs.xCount;
- if (deltaInt != 0) return deltaInt;
+ if (mMeshBuffer) {
+ Caches::getInstance().unbindMeshBuffer();
+ glDeleteBuffers(1, &mMeshBuffer);
+ mMeshBuffer = 0;
+ mSize = 0;
+ }
+}
- deltaInt = lhs.yCount - rhs.yCount;
- if (deltaInt != 0) return deltaInt;
+void PatchCache::clearCache() {
+ LruCache<PatchDescription, Patch*>::Iterator i(mCache);
+ while (i.next()) {
+ delete i.value();
+ }
+ mCache.clear();
- deltaInt = lhs.emptyCount - rhs.emptyCount;
- if (deltaInt != 0) return deltaInt;
+ BufferBlock* block = mFreeBlocks;
+ while (block) {
+ BufferBlock* next = block->next;
+ delete block;
+ block = next;
+ }
+ mFreeBlocks = NULL;
+}
- deltaInt = lhs.colorKey - rhs.colorKey;
- if (deltaInt != 0) return deltaInt;
+void PatchCache::remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch) {
+ LruCache<PatchDescription, Patch*>::Iterator i(mCache);
+ while (i.next()) {
+ const PatchDescription& key = i.key();
+ if (key.getPatch() == patch) {
+ patchesToRemove.push(patch_pair_t(&key, i.value()));
+ }
+ }
+}
- return 0;
+void PatchCache::removeDeferred(Res_png_9patch* patch) {
+ Mutex::Autolock _l(mLock);
+ mGarbage.push(patch);
}
-void PatchCache::clear() {
- size_t count = mCache.size();
- for (size_t i = 0; i < count; i++) {
- delete mCache.valueAt(i);
+void PatchCache::clearGarbage() {
+ Vector<patch_pair_t> patchesToRemove;
+
+ { // scope for the mutex
+ Mutex::Autolock _l(mLock);
+ size_t count = mGarbage.size();
+ for (size_t i = 0; i < count; i++) {
+ remove(patchesToRemove, mGarbage[i]);
+ }
+ mGarbage.clear();
}
- mCache.clear();
+
+ // TODO: We could sort patchesToRemove by offset to merge
+ // adjacent free blocks
+ for (size_t i = 0; i < patchesToRemove.size(); i++) {
+ const patch_pair_t& pair = patchesToRemove[i];
+
+ // Add a new free block to the list
+ const Patch* patch = pair.getSecond();
+ BufferBlock* block = new BufferBlock(patch->offset, patch->getSize());
+ block->next = mFreeBlocks;
+ mFreeBlocks = block;
+
+ mSize -= patch->getSize();
+
+ mCache.remove(*pair.getFirst());
+ }
+
+#if DEBUG_PATCHES
+ if (patchesToRemove.size() > 0) {
+ dumpFreeBlocks("Removed garbage");
+ }
+#endif
+}
+
+void PatchCache::createVertexBuffer() {
+ glBufferData(GL_ARRAY_BUFFER, mMaxSize, NULL, GL_DYNAMIC_DRAW);
+ mSize = 0;
+ mFreeBlocks = new BufferBlock(0, mMaxSize);
+ mGenerationId++;
}
-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) {
+/**
+ * Sets the mesh's offsets and copies its associated vertices into
+ * the mesh buffer (VBO).
+ */
+void PatchCache::setupMesh(Patch* newMesh, TextureVertex* vertices) {
+ // This call ensures the VBO exists and that it is bound
+ init(Caches::getInstance());
- int8_t transparentQuads = 0;
- uint32_t colorKey = 0;
+ // If we're running out of space, let's clear the entire cache
+ uint32_t size = newMesh->getSize();
+ if (mSize + size > mMaxSize) {
+ clearCache();
+ createVertexBuffer();
+ }
- if (uint8_t(numColors) < sizeof(uint32_t) * 4) {
- for (int8_t i = 0; i < numColors; i++) {
- if (colors[i] == 0x0) {
- transparentQuads++;
- colorKey |= 0x1 << i;
- }
+ // Find a block where we can fit the mesh
+ BufferBlock* previous = NULL;
+ BufferBlock* block = mFreeBlocks;
+ while (block) {
+ // The mesh fits
+ if (block->size >= size) {
+ break;
}
+ previous = block;
+ block = block->next;
}
- // If the 9patch is made of only transparent quads
- if (transparentQuads == int8_t((width + 1) * (height + 1))) {
- return NULL;
+ // We have enough space left in the buffer, but it's
+ // too fragmented, let's clear the cache
+ if (!block) {
+ clearCache();
+ createVertexBuffer();
+ previous = NULL;
+ block = mFreeBlocks;
}
- const PatchDescription description(bitmapWidth, bitmapHeight,
- pixelWidth, pixelHeight, width, height, transparentQuads, colorKey);
+ // Copy the 9patch mesh in the VBO
+ newMesh->offset = (GLintptr) (block->offset);
+ newMesh->textureOffset = newMesh->offset + gMeshTextureOffset;
+ glBufferSubData(GL_ARRAY_BUFFER, newMesh->offset, size, vertices);
- ssize_t index = mCache.indexOfKey(description);
- Patch* mesh = NULL;
- if (index >= 0) {
- mesh = mCache.valueAt(index);
+ // Remove the block since we've used it entirely
+ if (block->size == size) {
+ if (previous) {
+ previous->next = block->next;
+ } else {
+ mFreeBlocks = block->next;
+ }
+ } else {
+ // Resize the block now that it's occupied
+ block->offset += size;
+ block->size -= size;
}
+ mSize += size;
+}
+
+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) {
+
+ const PatchDescription description(bitmapWidth, bitmapHeight, pixelWidth, pixelHeight, patch);
+ const Patch* mesh = mCache.get(description);
+
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);
+ Patch* newMesh = new Patch();
+ TextureVertex* vertices;
+
+ if (entry) {
+ // An atlas entry has a UV mapper
+ vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
+ pixelWidth, pixelHeight, entry->uvMapper, patch);
+ } else {
+ vertices = newMesh->createMesh(bitmapWidth, bitmapHeight,
+ pixelWidth, pixelHeight, patch);
}
- 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);
+ if (vertices) {
+ setupMesh(newMesh, vertices);
+ }
+
+#if DEBUG_PATCHES
+ dumpFreeBlocks("Adding patch");
+#endif
+
+ mCache.put(description, newMesh);
+ return newMesh;
}
return mesh;
}
+#if DEBUG_PATCHES
+void PatchCache::dumpFreeBlocks(const char* prefix) {
+ String8 dump;
+ BufferBlock* block = mFreeBlocks;
+ while (block) {
+ dump.appendFormat("->(%d, %d)", block->offset, block->size);
+ block = block->next;
+ }
+ ALOGD("%s: Free blocks%s", prefix, dump.string());
+}
+#endif
+
}; // namespace uirenderer
}; // namespace android
diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h
index 0822cba..9f2c9a5 100644
--- a/libs/hwui/PatchCache.h
+++ b/libs/hwui/PatchCache.h
@@ -17,10 +17,16 @@
#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"
+#include "utils/Pair.h"
namespace android {
namespace uirenderer {
@@ -40,45 +46,64 @@ 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.
+ * Removes the entries associated with the specified 9-patch. This is meant
+ * to be called from threads that are not the EGL context thread (GC thread
+ * on the VM side for instance.)
*/
+ void removeDeferred(Res_png_9patch* patch);
+
+ /**
+ * Process deferred removals.
+ */
+ void clearGarbage();
+
+
+private:
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;
+
+ const Res_png_9patch* getPatch() const { return mPatch; }
+
static int compare(const PatchDescription& lhs, const PatchDescription& rhs);
bool operator==(const PatchDescription& other) const {
@@ -99,21 +124,63 @@ 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;
+ /**
+ * A buffer block represents an empty range in the mesh buffer
+ * that can be used to store vertices.
+ *
+ * The patch cache maintains a linked-list of buffer blocks
+ * to track available regions of memory in the VBO.
+ */
+ struct BufferBlock {
+ BufferBlock(uint32_t offset, uint32_t size): offset(offset), size(size), next(NULL) {
+ }
+
+ uint32_t offset;
+ uint32_t size;
+
+ BufferBlock* next;
+ }; // struct BufferBlock
+
+ typedef Pair<const PatchDescription*, Patch*> patch_pair_t;
+
+ void clearCache();
+ void createVertexBuffer();
+
+ void setupMesh(Patch* newMesh, TextureVertex* vertices);
+
+ void remove(Vector<patch_pair_t>& patchesToRemove, Res_png_9patch* patch);
+
+#if DEBUG_PATCHES
+ void dumpFreeBlocks(const char* prefix);
+#endif
+
+ uint32_t mMaxSize;
+ uint32_t mSize;
+
+ LruCache<PatchDescription, Patch*> mCache;
+
+ GLuint mMeshBuffer;
+ // First available free block inside the mesh buffer
+ BufferBlock* mFreeBlocks;
+
+ uint32_t mGenerationId;
+ // Garbage tracking, required to handle GC events on the VM side
+ Vector<Res_png_9patch*> mGarbage;
+ mutable Mutex mLock;
}; // class PatchCache
}; // namespace uirenderer
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index fdb10e2..5df6408 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;
@@ -214,7 +214,22 @@ void PathCache::operator()(PathDescription& entry, PathTexture*& texture) {
void PathCache::removeTexture(PathTexture* texture) {
if (texture) {
const uint32_t size = texture->width * texture->height;
- mSize -= size;
+
+ // If there is a pending task we must wait for it to return
+ // before attempting our cleanup
+ const sp<Task<SkBitmap*> >& task = texture->task();
+ if (task != NULL) {
+ SkBitmap* bitmap = task->getResult();
+ texture->clearTask();
+ } else {
+ // If there is a pending task, the path was not added
+ // to the cache and the size wasn't increased
+ if (size > mSize) {
+ ALOGE("Removing path texture of size %d will leave "
+ "the cache in an inconsistent state", size);
+ }
+ mSize -= size;
+ }
PATH_LOGD("PathCache::delete name, size, mSize = %d, %d, %d",
texture->id, size, mSize);
@@ -223,7 +238,7 @@ void PathCache::removeTexture(PathTexture* texture) {
}
if (texture->id) {
- glDeleteTextures(1, &texture->id);
+ Caches::getInstance().deleteTexture(texture->id);
}
delete texture;
}
@@ -283,6 +298,11 @@ void PathCache::generateTexture(const PathDescription& entry, SkBitmap* bitmap,
mCache.put(entry, texture);
}
} else {
+ // It's okay to add a texture that's bigger than the cache since
+ // we'll trim the cache later when addToCache is set to false
+ if (!addToCache) {
+ mSize += size;
+ }
texture->cleanup = true;
}
}
@@ -300,7 +320,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);
@@ -350,8 +370,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) {
// Paths
///////////////////////////////////////////////////////////////////////////////
-void PathCache::remove(const path_pair_t& pair) {
- Vector<PathDescription> pathsToRemove;
+void PathCache::remove(Vector<PathDescription>& pathsToRemove, const path_pair_t& pair) {
LruCache<PathDescription, PathTexture*>::Iterator i(mCache);
while (i.next()) {
@@ -362,10 +381,6 @@ void PathCache::remove(const path_pair_t& pair) {
pathsToRemove.push(key);
}
}
-
- for (size_t i = 0; i < pathsToRemove.size(); i++) {
- mCache.remove(pathsToRemove.itemAt(i));
- }
}
void PathCache::removeDeferred(SkPath* path) {
@@ -374,12 +389,20 @@ void PathCache::removeDeferred(SkPath* path) {
}
void PathCache::clearGarbage() {
- Mutex::Autolock l(mLock);
- size_t count = mGarbage.size();
- for (size_t i = 0; i < count; i++) {
- remove(mGarbage.itemAt(i));
+ Vector<PathDescription> pathsToRemove;
+
+ { // scope for the mutex
+ Mutex::Autolock l(mLock);
+ size_t count = mGarbage.size();
+ for (size_t i = 0; i < count; i++) {
+ remove(pathsToRemove, mGarbage.itemAt(i));
+ }
+ mGarbage.clear();
+ }
+
+ for (size_t i = 0; i < pathsToRemove.size(); i++) {
+ mCache.remove(pathsToRemove.itemAt(i));
}
- mGarbage.clear();
}
/**
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index dd1f996..16d20a8 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() {
@@ -269,7 +269,7 @@ private:
* Removes an entry.
* The pair must define first=path, second=sourcePath
*/
- void remove(const path_pair_t& pair);
+ void remove(Vector<PathDescription>& pathsToRemove, const path_pair_t& pair);
/**
* Ensures there is enough space in the cache for a texture of the specified
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..36e89c6 100644
--- a/libs/hwui/PixelBuffer.cpp
+++ b/libs/hwui/PixelBuffer.cpp
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include "Caches.h"
+#include "Debug.h"
#include "Extensions.h"
#include "PixelBuffer.h"
#include "Properties.h"
@@ -113,6 +114,14 @@ uint8_t* GpuPixelBuffer::map(AccessMode mode) {
if (mAccessMode == kAccessMode_None) {
mCaches.bindPixelBuffer(mBuffer);
mMappedPointer = (uint8_t*) glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode);
+#if DEBUG_OPENGL
+ if (!mMappedPointer) {
+ GLenum status = GL_NO_ERROR;
+ while ((status = glGetError()) != GL_NO_ERROR) {
+ ALOGE("Could not map GPU pixel buffer: 0x%x", status);
+ }
+ }
+#endif
mAccessMode = mode;
}
@@ -123,7 +132,10 @@ void GpuPixelBuffer::unmap() {
if (mAccessMode != kAccessMode_None) {
if (mMappedPointer) {
mCaches.bindPixelBuffer(mBuffer);
- glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
+ GLboolean status = glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
+ if (status == GL_FALSE) {
+ ALOGE("Corrupted GPU pixel buffer");
+ }
}
mAccessMode = kAccessMode_None;
mMappedPointer = NULL;
@@ -147,14 +159,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/PixelBuffer.h b/libs/hwui/PixelBuffer.h
index 32d5417..9725a61 100644
--- a/libs/hwui/PixelBuffer.h
+++ b/libs/hwui/PixelBuffer.h
@@ -112,13 +112,25 @@ public:
virtual uint8_t* getMappedPointer() const = 0;
/**
- * Upload the specified rectangle of this pixe buffer as a
+ * Upload the specified rectangle of this pixel buffer as a
* GL_TEXTURE_2D texture. Calling this method will trigger
* an unmap() if necessary.
*/
virtual void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height, int offset) = 0;
/**
+ * Upload the specified rectangle of this pixel buffer as a
+ * GL_TEXTURE_2D texture. Calling this method will trigger
+ * an unmap() if necessary.
+ *
+ * This is a convenience function provided to save callers the
+ * trouble of computing the offset parameter.
+ */
+ void upload(uint32_t x, uint32_t y, uint32_t width, uint32_t height) {
+ upload(x, y, width, height, getOffset(x, y));
+ }
+
+ /**
* Returns the width of the render buffer in pixels.
*/
uint32_t getWidth() const {
@@ -140,6 +152,13 @@ public:
}
/**
+ * Returns the offset of a pixel in this pixel buffer, in bytes.
+ */
+ uint32_t getOffset(uint32_t x, uint32_t y) const {
+ return (y * mWidth + x) * formatSize(mFormat);
+ }
+
+ /**
* Returns the number of bytes per pixel in the specified format.
*
* Supported formats:
diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp
index 14a2376..7814a01 100644
--- a/libs/hwui/Program.cpp
+++ b/libs/hwui/Program.cpp
@@ -15,8 +15,12 @@
*/
#define LOG_TAG "OpenGLRenderer"
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include <utils/Trace.h>
#include "Program.h"
+#include "Vertex.h"
namespace android {
namespace uirenderer {
@@ -25,7 +29,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 +53,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 +92,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 +140,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 +163,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(Vertex::gGeometryFudgeFactor, Vertex::gGeometryFudgeFactor);
+ 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..20b8f2f 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -70,10 +70,23 @@ enum DebugLevel {
#define PROPERTY_DEBUG_LAYERS_UPDATES "debug.hwui.show_layers_updates"
/**
- * Used to enable/disable overdraw debugging. The accepted values are
- * "true" and "false". The default value is "false".
+ * Used to enable/disable overdraw debugging.
+ *
+ * The accepted values are
+ * "show", to show overdraw
+ * "show_deuteranomaly", to show overdraw if you suffer from Deuteranomaly
+ * "count", to show an overdraw counter
+ * "false", to disable overdraw debugging
+ *
+ * The default value is "false".
+ */
+#define PROPERTY_DEBUG_OVERDRAW "debug.hwui.overdraw"
+
+/**
+ * Used to enable/disable PerfHUD ES profiling. The accepted values
+ * are "true" and "false". The default value is "false".
*/
-#define PROPERTY_DEBUG_OVERDRAW "debug.hwui.show_overdraw"
+#define PROPERTY_DEBUG_NV_PROFILING "debug.hwui.nv_profiling"
/**
* Used to enable/disable non-rectangular clipping debugging.
@@ -123,9 +136,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 +146,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 +192,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 +209,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/Query.h b/libs/hwui/Query.h
new file mode 100644
index 0000000..e25b16b
--- /dev/null
+++ b/libs/hwui/Query.h
@@ -0,0 +1,152 @@
+/*
+ * 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_QUERY_H
+#define ANDROID_HWUI_QUERY_H
+
+#include <GLES3/gl3.h>
+
+#include "Extensions.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * A Query instance can be used to perform occlusion queries. If the device
+ * does not support occlusion queries, the result of a query will always be
+ * 0 and the result will always be marked available.
+ *
+ * To run an occlusion query successfully, you must start end end the query:
+ *
+ * Query query;
+ * query.begin();
+ * // execute OpenGL calls
+ * query.end();
+ * GLuint result = query.getResult();
+ */
+class Query {
+public:
+ /**
+ * Possible query targets.
+ */
+ enum Target {
+ /**
+ * Indicates if any sample passed the depth & stencil tests.
+ */
+ kTargetSamples = GL_ANY_SAMPLES_PASSED,
+ /**
+ * Indicates if any sample passed the depth & stencil tests.
+ * The implementation may choose to use a less precise version
+ * of the test, potentially resulting in false positives.
+ */
+ kTargetConservativeSamples = GL_ANY_SAMPLES_PASSED_CONSERVATIVE,
+ };
+
+ /**
+ * Creates a new query with the specified target. The default
+ * target is kTargetSamples (of GL_ANY_SAMPLES_PASSED in OpenGL.)
+ */
+ Query(Target target = kTargetSamples): mActive(false), mTarget(target),
+ mCanQuery(Extensions::getInstance().hasOcclusionQueries()),
+ mQuery(0) {
+ }
+
+ ~Query() {
+ if (mQuery) {
+ glDeleteQueries(1, &mQuery);
+ }
+ }
+
+ /**
+ * Begins the query. If the query has already begun or if the device
+ * does not support occlusion queries, calling this method as no effect.
+ * After calling this method successfully, the query is marked active.
+ */
+ void begin() {
+ if (!mActive && mCanQuery) {
+ if (!mQuery) {
+ glGenQueries(1, &mQuery);
+ }
+
+ glBeginQuery(mTarget, mQuery);
+ mActive = true;
+ }
+ }
+
+ /**
+ * Ends the query. If the query has already begun or if the device
+ * does not support occlusion queries, calling this method as no effect.
+ * After calling this method successfully, the query is marked inactive.
+ */
+ void end() {
+ if (mQuery && mActive) {
+ glEndQuery(mTarget);
+ mActive = false;
+ }
+ }
+
+ /**
+ * Returns true if the query is active, false otherwise.
+ */
+ bool isActive() {
+ return mActive;
+ }
+
+ /**
+ * Returns true if the result of the query is available,
+ * false otherwise. Calling getResult() before the result
+ * is available may result in the calling thread being blocked.
+ * If the device does not support queries, this method always
+ * returns true.
+ */
+ bool isResultAvailable() {
+ if (!mQuery) return true;
+
+ GLuint result;
+ glGetQueryObjectuiv(mQuery, GL_QUERY_RESULT_AVAILABLE, &result);
+ return result == GL_TRUE;
+ }
+
+ /**
+ * Returns the result of the query. If the device does not
+ * support queries this method will return 0.
+ *
+ * Calling this method implicitely calls end() if the query
+ * is currently active.
+ */
+ GLuint getResult() {
+ if (!mQuery) return 0;
+
+ end();
+
+ GLuint result;
+ glGetQueryObjectuiv(mQuery, GL_QUERY_RESULT, &result);
+ return result;
+ }
+
+
+private:
+ bool mActive;
+ GLenum mTarget;
+ bool mCanQuery;
+ GLuint mQuery;
+
+}; // class Query
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_QUERY_H
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index f50ac3c..dabd8d4 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -21,9 +21,15 @@
#include <utils/Log.h>
+#include "Vertex.h"
+
namespace android {
namespace uirenderer {
+#define RECT_STRING "%7.2f %7.2f %7.2f %7.2f"
+#define RECT_ARGS(r) \
+ (r).left, (r).top, (r).right, (r).bottom
+
///////////////////////////////////////////////////////////////////////////////
// Structs
///////////////////////////////////////////////////////////////////////////////
@@ -125,11 +131,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);
}
@@ -166,6 +172,40 @@ public:
bottom += delta;
}
+ /**
+ * Similar to snapToPixelBoundaries, but estimates bounds conservatively to handle GL rounding
+ * errors.
+ *
+ * This function should be used whenever estimating the damage rect of geometry already mapped
+ * into layer space.
+ */
+ void snapGeometryToPixelBoundaries(bool snapOut) {
+ if (snapOut) {
+ /* For AA geometry with a ramp perimeter, don't snap by rounding - AA geometry will have
+ * a 0.5 pixel perimeter not accounted for in its bounds. Instead, snap by
+ * conservatively rounding out the bounds with floor/ceil.
+ *
+ * In order to avoid changing integer bounds with floor/ceil due to rounding errors
+ * inset the bounds first by the fudge factor. Very small fraction-of-a-pixel errors
+ * from this inset will only incur similarly small errors in output, due to transparency
+ * in extreme outside of the geometry.
+ */
+ left = floorf(left + Vertex::gGeometryFudgeFactor);
+ top = floorf(top + Vertex::gGeometryFudgeFactor);
+ right = ceilf(right - Vertex::gGeometryFudgeFactor);
+ bottom = ceilf(bottom - Vertex::gGeometryFudgeFactor);
+ } else {
+ /* For other geometry, we do the regular rounding in order to snap, but also outset the
+ * bounds by a fudge factor. This ensures that ambiguous geometry (e.g. a non-AA Rect
+ * with top left at (0.5, 0.5)) will err on the side of a larger damage rect.
+ */
+ left = floorf(left + 0.5f - Vertex::gGeometryFudgeFactor);
+ top = floorf(top + 0.5f - Vertex::gGeometryFudgeFactor);
+ right = floorf(right + 0.5f + Vertex::gGeometryFudgeFactor);
+ bottom = floorf(bottom + 0.5f + Vertex::gGeometryFudgeFactor);
+ }
+ }
+
void snapToPixelBoundaries() {
left = floorf(left + 0.5f);
top = floorf(top + 0.5f);
diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp
index 347bd78..3f77021 100644
--- a/libs/hwui/ResourceCache.cpp
+++ b/libs/hwui/ResourceCache.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "OpenGLRenderer"
+
#include <SkPixelRef.h>
#include "ResourceCache.h"
#include "Caches.h"
@@ -60,7 +62,7 @@ void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType)
}
void ResourceCache::incrementRefcount(SkBitmap* bitmapResource) {
- SkSafeRef(bitmapResource->pixelRef());
+ bitmapResource->pixelRef()->globalRef();
SkSafeRef(bitmapResource->getColorTable());
incrementRefcount((void*) bitmapResource, kBitmap);
}
@@ -79,6 +81,10 @@ void ResourceCache::incrementRefcount(SkiaColorFilter* filterResource) {
incrementRefcount((void*) filterResource, kColorFilter);
}
+void ResourceCache::incrementRefcount(Res_png_9patch* patchResource) {
+ incrementRefcount((void*) patchResource, kNinePatch);
+}
+
void ResourceCache::incrementRefcount(Layer* layerResource) {
incrementRefcount((void*) layerResource, kLayer);
}
@@ -94,7 +100,7 @@ void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourc
}
void ResourceCache::incrementRefcountLocked(SkBitmap* bitmapResource) {
- SkSafeRef(bitmapResource->pixelRef());
+ bitmapResource->pixelRef()->globalRef();
SkSafeRef(bitmapResource->getColorTable());
incrementRefcountLocked((void*) bitmapResource, kBitmap);
}
@@ -113,6 +119,10 @@ void ResourceCache::incrementRefcountLocked(SkiaColorFilter* filterResource) {
incrementRefcountLocked((void*) filterResource, kColorFilter);
}
+void ResourceCache::incrementRefcountLocked(Res_png_9patch* patchResource) {
+ incrementRefcountLocked((void*) patchResource, kNinePatch);
+}
+
void ResourceCache::incrementRefcountLocked(Layer* layerResource) {
incrementRefcountLocked((void*) layerResource, kLayer);
}
@@ -123,7 +133,7 @@ void ResourceCache::decrementRefcount(void* resource) {
}
void ResourceCache::decrementRefcount(SkBitmap* bitmapResource) {
- SkSafeUnref(bitmapResource->pixelRef());
+ bitmapResource->pixelRef()->globalUnref();
SkSafeUnref(bitmapResource->getColorTable());
decrementRefcount((void*) bitmapResource);
}
@@ -142,6 +152,10 @@ void ResourceCache::decrementRefcount(SkiaColorFilter* filterResource) {
decrementRefcount((void*) filterResource);
}
+void ResourceCache::decrementRefcount(Res_png_9patch* patchResource) {
+ decrementRefcount((void*) patchResource);
+}
+
void ResourceCache::decrementRefcount(Layer* layerResource) {
decrementRefcount((void*) layerResource);
}
@@ -160,7 +174,7 @@ void ResourceCache::decrementRefcountLocked(void* resource) {
}
void ResourceCache::decrementRefcountLocked(SkBitmap* bitmapResource) {
- SkSafeUnref(bitmapResource->pixelRef());
+ bitmapResource->pixelRef()->globalUnref();
SkSafeUnref(bitmapResource->getColorTable());
decrementRefcountLocked((void*) bitmapResource);
}
@@ -179,6 +193,10 @@ void ResourceCache::decrementRefcountLocked(SkiaColorFilter* filterResource) {
decrementRefcountLocked((void*) filterResource);
}
+void ResourceCache::decrementRefcountLocked(Res_png_9patch* patchResource) {
+ decrementRefcountLocked((void*) patchResource);
+}
+
void ResourceCache::decrementRefcountLocked(Layer* layerResource) {
decrementRefcountLocked((void*) layerResource);
}
@@ -265,6 +283,30 @@ void ResourceCache::destructorLocked(SkiaColorFilter* resource) {
}
}
+void ResourceCache::destructor(Res_png_9patch* resource) {
+ Mutex::Autolock _l(mLock);
+ destructorLocked(resource);
+}
+
+void ResourceCache::destructorLocked(Res_png_9patch* resource) {
+ ssize_t index = mCache->indexOfKey(resource);
+ ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL;
+ if (ref == NULL) {
+ if (Caches::hasInstance()) {
+ Caches::getInstance().patchCache.removeDeferred(resource);
+ }
+ // If we're not tracking this resource, just delete it
+ // A Res_png_9patch is actually an array of byte that's larger
+ // than sizeof(Res_png_9patch). It must be freed as an array.
+ delete[] (int8_t*) resource;
+ return;
+ }
+ ref->destroyed = true;
+ if (ref->refCount == 0) {
+ deleteResourceReferenceLocked(resource, ref);
+ }
+}
+
/**
* Return value indicates whether resource was actually recycled, which happens when RefCnt
* reaches 0.
@@ -335,6 +377,16 @@ void ResourceCache::deleteResourceReferenceLocked(void* resource, ResourceRefere
delete filter;
}
break;
+ case kNinePatch: {
+ if (Caches::hasInstance()) {
+ Caches::getInstance().patchCache.removeDeferred((Res_png_9patch*) resource);
+ }
+ // A Res_png_9patch is actually an array of byte that's larger
+ // than sizeof(Res_png_9patch). It must be freed as an array.
+ int8_t* patch = (int8_t*) resource;
+ delete[] patch;
+ }
+ break;
case kLayer: {
Layer* layer = (Layer*) resource;
Caches::getInstance().deleteLayerDeferred(layer);
diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h
index ab493e5..ea0c1b5 100644
--- a/libs/hwui/ResourceCache.h
+++ b/libs/hwui/ResourceCache.h
@@ -22,7 +22,11 @@
#include <SkBitmap.h>
#include <SkiaColorFilter.h>
#include <SkiaShader.h>
+
#include <utils/KeyedVector.h>
+
+#include <androidfw/ResourceTypes.h>
+
#include "Layer.h"
namespace android {
@@ -35,6 +39,7 @@ enum ResourceType {
kBitmap,
kShader,
kColorFilter,
+ kNinePatch,
kPath,
kLayer
};
@@ -69,35 +74,41 @@ public:
void incrementRefcount(SkBitmap* resource);
void incrementRefcount(SkiaShader* resource);
void incrementRefcount(SkiaColorFilter* resource);
+ void incrementRefcount(Res_png_9patch* resource);
void incrementRefcount(Layer* resource);
void incrementRefcountLocked(SkPath* resource);
void incrementRefcountLocked(SkBitmap* resource);
void incrementRefcountLocked(SkiaShader* resource);
void incrementRefcountLocked(SkiaColorFilter* resource);
+ void incrementRefcountLocked(Res_png_9patch* resource);
void incrementRefcountLocked(Layer* resource);
void decrementRefcount(SkBitmap* resource);
void decrementRefcount(SkPath* resource);
void decrementRefcount(SkiaShader* resource);
void decrementRefcount(SkiaColorFilter* resource);
+ void decrementRefcount(Res_png_9patch* resource);
void decrementRefcount(Layer* resource);
void decrementRefcountLocked(SkBitmap* resource);
void decrementRefcountLocked(SkPath* resource);
void decrementRefcountLocked(SkiaShader* resource);
void decrementRefcountLocked(SkiaColorFilter* resource);
+ void decrementRefcountLocked(Res_png_9patch* resource);
void decrementRefcountLocked(Layer* resource);
void destructor(SkPath* resource);
void destructor(SkBitmap* resource);
void destructor(SkiaShader* resource);
void destructor(SkiaColorFilter* resource);
+ void destructor(Res_png_9patch* resource);
void destructorLocked(SkPath* resource);
void destructorLocked(SkBitmap* resource);
void destructorLocked(SkiaShader* resource);
void destructorLocked(SkiaColorFilter* resource);
+ void destructorLocked(Res_png_9patch* resource);
bool recycle(SkBitmap* resource);
bool recycleLocked(SkBitmap* resource);
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/Stencil.cpp b/libs/hwui/Stencil.cpp
index ba2e6f2..2764523 100644
--- a/libs/hwui/Stencil.cpp
+++ b/libs/hwui/Stencil.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "Debug.h"
#include "Extensions.h"
#include "Properties.h"
#include "Stencil.h"
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..ed0a79a 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,25 +235,25 @@ 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:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), texture->height,
- GL_UNSIGNED_BYTE, bitmap->getPixels());
+ uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(),
+ texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
texture->blend = true;
break;
case SkBitmap::kRGB_565_Config:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
- uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), texture->height,
- GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
+ uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(),
+ texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels());
texture->blend = false;
break;
case SkBitmap::kARGB_8888_Config:
glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel());
- uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), texture->height,
- GL_UNSIGNED_BYTE, bitmap->getPixels());
+ uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(),
+ texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels());
// Do this after calling getPixels() to make sure Skia's deferred
// decoding happened
texture->blend = !bitmap->isOpaque();
@@ -293,17 +293,28 @@ void TextureCache::uploadLoFiTexture(bool resize, SkBitmap* bitmap,
SkCanvas canvas(rgbaBitmap);
canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL);
- uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), height,
+ uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), width, height,
GL_UNSIGNED_BYTE, rgbaBitmap.getPixels());
}
-void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height,
- GLenum type, const GLvoid * data) {
+void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride,
+ GLsizei width, GLsizei height, GLenum type, const GLvoid * data) {
+ // TODO: With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer
+ // if the stride doesn't match the width
+ const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength();
+ if (useStride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
+ }
+
if (resize) {
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data);
} else {
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data);
}
+
+ if (useStride) {
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+ }
}
}; // namespace uirenderer
diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h
index 80bb22e..57fc19a 100644
--- a/libs/hwui/TextureCache.h
+++ b/libs/hwui/TextureCache.h
@@ -125,8 +125,8 @@ private:
void generateTexture(SkBitmap* bitmap, Texture* texture, bool regenerate = false);
void uploadLoFiTexture(bool resize, SkBitmap* bitmap, uint32_t width, uint32_t height);
- void uploadToTexture(bool resize, GLenum format, GLsizei width, GLsizei height,
- GLenum type, const GLvoid * data);
+ void uploadToTexture(bool resize, GLenum format, GLsizei stride,
+ GLsizei width, GLsizei height, GLenum type, const GLvoid * data);
void init();
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..790d4fc 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 {
@@ -24,12 +26,30 @@ namespace uirenderer {
* Simple structure to describe a vertex with a position and a texture.
*/
struct Vertex {
+ /**
+ * Fudge-factor used to disambiguate geometry pixel positioning.
+ *
+ * Used to offset lines and points to avoid ambiguous intersection with pixel centers (see
+ * Program::set()), and used to make geometry damage rect calculation conservative (see
+ * Rect::snapGeometryToPixelBoundaries())
+ */
+ static const float gGeometryFudgeFactor = 0.0656f;
+
float position[2];
static inline void set(Vertex* vertex, float x, float y) {
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 +101,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..d5f38b5 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"
@@ -107,17 +108,18 @@ CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock* blockToRemove)
// CacheTexture
///////////////////////////////////////////////////////////////////////////////
-CacheTexture::CacheTexture(uint16_t width, uint16_t height, uint32_t maxQuadCount) :
- mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
+CacheTexture::CacheTexture(uint16_t width, uint16_t height, GLenum format, uint32_t maxQuadCount) :
+ mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height), mFormat(format),
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);
// OpenGL ES 3.0+ lets us specify the row length for unpack operations such
// as glTexSubImage2D(). This allows us to upload a sub-rectangle of a texture.
// With OpenGL ES 2.0 we have to upload entire stripes instead.
- mHasES3 = Extensions::getInstance().getMajorGlVersion() >= 3;
+ mHasUnpackRowLength = Extensions::getInstance().hasUnpackRowLength();
}
CacheTexture::~CacheTexture() {
@@ -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);
}
@@ -180,17 +182,17 @@ void CacheTexture::allocateMesh() {
void CacheTexture::allocateTexture() {
if (!mTexture) {
- mTexture = PixelBuffer::create(GL_ALPHA, mWidth, mHeight);
+ mTexture = PixelBuffer::create(mFormat, mWidth, mHeight);
}
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,
- GL_ALPHA, GL_UNSIGNED_BYTE, 0);
+ glTexImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0,
+ mFormat, GL_UNSIGNED_BYTE, 0);
const GLenum filtering = getLinearFiltering() ? GL_LINEAR : GL_NEAREST;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
@@ -204,22 +206,21 @@ void CacheTexture::allocateTexture() {
bool CacheTexture::upload() {
const Rect& dirtyRect = mDirtyRect;
- uint32_t x = mHasES3 ? dirtyRect.left : 0;
+ uint32_t x = mHasUnpackRowLength ? dirtyRect.left : 0;
uint32_t y = dirtyRect.top;
- uint32_t width = mHasES3 ? dirtyRect.getWidth() : mWidth;
+ uint32_t width = mHasUnpackRowLength ? dirtyRect.getWidth() : mWidth;
uint32_t height = dirtyRect.getHeight();
// The unpack row length only needs to be specified when a new
// texture is bound
- if (mHasES3) {
+ if (mHasUnpackRowLength) {
glPixelStorei(GL_UNPACK_ROW_LENGTH, mWidth);
}
- mTexture->upload(x, y, width, height, y * mWidth + x);
-
+ mTexture->upload(x, y, width, height);
setDirty(false);
- return mHasES3;
+ return mHasUnpackRowLength;
}
void CacheTexture::setDirty(bool dirty) {
@@ -230,6 +231,32 @@ void CacheTexture::setDirty(bool dirty) {
}
bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
+ switch (glyph.fMaskFormat) {
+ case SkMask::kA8_Format:
+ case SkMask::kBW_Format:
+ if (mFormat != GL_ALPHA) {
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: texture format %x is inappropriate for monochromatic glyphs",
+ mFormat);
+#endif
+ return false;
+ }
+ break;
+ case SkMask::kARGB32_Format:
+ if (mFormat != GL_RGBA) {
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: texture format %x is inappropriate for colour glyphs", mFormat);
+#endif
+ return false;
+ }
+ break;
+ default:
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: unknown glyph format %x encountered", glyph.fMaskFormat);
+#endif
+ return false;
+ }
+
if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mHeight) {
return false;
}
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
index ddcc836..61b38f8 100644
--- a/libs/hwui/font/CacheTexture.h
+++ b/libs/hwui/font/CacheTexture.h
@@ -24,13 +24,14 @@
#include <utils/Log.h>
#include "FontUtil.h"
+#include "../PixelBuffer.h"
#include "../Rect.h"
#include "../Vertex.h"
namespace android {
namespace uirenderer {
-class PixelBuffer;
+class Caches;
/**
* CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
@@ -73,7 +74,7 @@ struct CacheBlock {
class CacheTexture {
public:
- CacheTexture(uint16_t width, uint16_t height, uint32_t maxQuadCount);
+ CacheTexture(uint16_t width, uint16_t height, GLenum format, uint32_t maxQuadCount);
~CacheTexture();
void reset();
@@ -99,6 +100,14 @@ public:
return mHeight;
}
+ inline GLenum getFormat() const {
+ return mFormat;
+ }
+
+ inline uint32_t getOffset(uint16_t x, uint16_t y) const {
+ return (y * mWidth + x) * PixelBuffer::formatSize(mFormat);
+ }
+
inline const Rect* getDirtyRect() const {
return &mDirtyRect;
}
@@ -150,9 +159,9 @@ public:
float x3, float y3, float u3, float v3,
float x4, float y4, float u4, float v4) {
TextureVertex* mesh = mMesh + mCurrentQuad * 4;
- TextureVertex::set(mesh++, x1, y1, u1, v1);
TextureVertex::set(mesh++, x2, y2, u2, v2);
TextureVertex::set(mesh++, x3, y3, u3, v3);
+ TextureVertex::set(mesh++, x1, y1, u1, v1);
TextureVertex::set(mesh++, x4, y4, u4, v4);
mCurrentQuad++;
}
@@ -172,15 +181,17 @@ private:
GLuint mTextureId;
uint16_t mWidth;
uint16_t mHeight;
+ GLenum mFormat;
bool mLinearFiltering;
bool mDirty;
uint16_t mNumGlyphs;
TextureVertex* mMesh;
uint32_t mCurrentQuad;
uint32_t mMaxQuadCount;
+ Caches& mCaches;
CacheBlock* mCacheBlocks;
+ bool mHasUnpackRowLength;
Rect mDirtyRect;
- bool mHasES3;
};
}; // namespace uirenderer
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
index 011cfc1..18983d8 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -55,6 +55,7 @@ Font::FontDescription::FontDescription(const SkPaint* paint, const mat4& matrix)
mStyle = paint->getStyle();
mStrokeWidth = paint->getStrokeWidth();
mAntiAliasing = paint->isAntiAlias();
+ mHinting = paint->getHinting();
mLookupTransform.reset();
mLookupTransform[SkMatrix::kMScaleX] = roundf(fmaxf(1.0f, matrix[mat4::kScaleX]));
mLookupTransform[SkMatrix::kMScaleY] = roundf(fmaxf(1.0f, matrix[mat4::kScaleY]));
@@ -80,6 +81,7 @@ hash_t Font::FontDescription::hash() const {
hash = JenkinsHashMix(hash, android::hash_type(mStyle));
hash = JenkinsHashMix(hash, android::hash_type(mStrokeWidth));
hash = JenkinsHashMix(hash, int(mAntiAliasing));
+ hash = JenkinsHashMix(hash, android::hash_type(mHinting));
hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleX]));
hash = JenkinsHashMix(hash, android::hash_type(mLookupTransform[SkMatrix::kMScaleY]));
return JenkinsHashWhiten(hash);
@@ -111,6 +113,9 @@ int Font::FontDescription::compare(const Font::FontDescription& lhs,
deltaInt = int(lhs.mAntiAliasing) - int(rhs.mAntiAliasing);
if (deltaInt != 0) return deltaInt;
+ deltaInt = int(lhs.mHinting) - int(rhs.mHinting);
+ if (deltaInt != 0) return deltaInt;
+
if (lhs.mLookupTransform[SkMatrix::kMScaleX] <
rhs.mLookupTransform[SkMatrix::kMScaleX]) return -1;
if (lhs.mLookupTransform[SkMatrix::kMScaleX] >
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
index 52cca1c..9e7ec2d 100644
--- a/libs/hwui/font/Font.h
+++ b/libs/hwui/font/Font.h
@@ -69,6 +69,7 @@ public:
uint8_t mStyle;
float mStrokeWidth;
bool mAntiAliasing;
+ uint8_t mHinting;
SkMatrix mLookupTransform;
SkMatrix mInverseLookupTransform;
};
diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h
index f758666..cdcb23c 100644
--- a/libs/hwui/font/FontUtil.h
+++ b/libs/hwui/font/FontUtil.h
@@ -31,6 +31,9 @@
#define DEFAULT_TEXT_LARGE_CACHE_HEIGHT 512
#define TEXTURE_BORDER_SIZE 1
+#if TEXTURE_BORDER_SIZE != 1
+# error TEXTURE_BORDER_SIZE other than 1 is not currently supported
+#endif
#define CACHE_BLOCK_ROUNDING_SIZE 4
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index c8bfd9c..189895c 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -29,7 +29,7 @@ namespace uirenderer {
TaskManager::TaskManager() {
// Get the number of available CPUs. This value does not change over time.
- int cpuCount = sysconf(_SC_NPROCESSORS_ONLN);
+ int cpuCount = sysconf(_SC_NPROCESSORS_CONF);
for (int i = 0; i < cpuCount / 2; i++) {
String8 name;
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;