diff options
Diffstat (limited to 'libs/hwui')
30 files changed, 1655 insertions, 506 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index f4a0161..a98e4cd 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -9,6 +9,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) FontRenderer.cpp \ GammaFontRenderer.cpp \ Caches.cpp \ + DisplayListLogBuffer.cpp \ DisplayListRenderer.cpp \ FboCache.cpp \ GradientCache.cpp \ @@ -42,7 +43,6 @@ ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2 libskia libui LOCAL_MODULE := libhwui LOCAL_MODULE_TAGS := optional - LOCAL_PRELINK_MODULE := false include $(BUILD_SHARED_LIBRARY) diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 4f5edd5..b0d8cf9 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "OpenGLRenderer" #include <utils/Log.h> +#include <utils/String8.h> #include "Caches.h" #include "Properties.h" @@ -69,30 +70,43 @@ Caches::~Caches() { /////////////////////////////////////////////////////////////////////////////// void Caches::dumpMemoryUsage() { - LOGD("Current memory usage / total memory usage (bytes):"); - LOGD(" TextureCache %8d / %8d", textureCache.getSize(), textureCache.getMaxSize()); - LOGD(" LayerCache %8d / %8d", layerCache.getSize(), layerCache.getMaxSize()); - LOGD(" GradientCache %8d / %8d", gradientCache.getSize(), gradientCache.getMaxSize()); - LOGD(" PathCache %8d / %8d", pathCache.getSize(), pathCache.getMaxSize()); - LOGD(" CircleShapeCache %8d / %8d", + String8 stringLog; + dumpMemoryUsage(stringLog); + LOGD("%s", stringLog.string()); + delete stringLog; +} + +void Caches::dumpMemoryUsage(String8 &log) { + log.appendFormat("Current memory usage / total memory usage (bytes):\n"); + log.appendFormat(" TextureCache %8d / %8d\n", + textureCache.getSize(), textureCache.getMaxSize()); + log.appendFormat(" LayerCache %8d / %8d\n", + layerCache.getSize(), layerCache.getMaxSize()); + log.appendFormat(" GradientCache %8d / %8d\n", + gradientCache.getSize(), gradientCache.getMaxSize()); + log.appendFormat(" PathCache %8d / %8d\n", + pathCache.getSize(), pathCache.getMaxSize()); + log.appendFormat(" CircleShapeCache %8d / %8d\n", circleShapeCache.getSize(), circleShapeCache.getMaxSize()); - LOGD(" OvalShapeCache %8d / %8d", + log.appendFormat(" OvalShapeCache %8d / %8d\n", ovalShapeCache.getSize(), ovalShapeCache.getMaxSize()); - LOGD(" RoundRectShapeCache %8d / %8d", + log.appendFormat(" RoundRectShapeCache %8d / %8d\n", roundRectShapeCache.getSize(), roundRectShapeCache.getMaxSize()); - LOGD(" RectShapeCache %8d / %8d", + log.appendFormat(" RectShapeCache %8d / %8d\n", rectShapeCache.getSize(), rectShapeCache.getMaxSize()); - LOGD(" ArcShapeCache %8d / %8d", + log.appendFormat(" ArcShapeCache %8d / %8d\n", arcShapeCache.getSize(), arcShapeCache.getMaxSize()); - LOGD(" TextDropShadowCache %8d / %8d", dropShadowCache.getSize(), + log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(), dropShadowCache.getMaxSize()); for (uint32_t i = 0; i < fontRenderer.getFontRendererCount(); i++) { const uint32_t size = fontRenderer.getFontRendererSize(i); - LOGD(" FontRenderer %d %8d / %8d", i, size, size); + log.appendFormat(" FontRenderer %d %8d / %8d\n", i, size, size); } - LOGD("Other:"); - LOGD(" FboCache %8d / %8d", fboCache.getSize(), fboCache.getMaxSize()); - LOGD(" PatchCache %8d / %8d", patchCache.getSize(), patchCache.getMaxSize()); + 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(); @@ -109,9 +123,8 @@ void Caches::dumpMemoryUsage() { total += fontRenderer.getFontRendererSize(i); } - LOGD("Total memory usage:"); - LOGD(" %d bytes, %.2f MB", total, total / 1024.0f / 1024.0f); - LOGD("\n"); + log.appendFormat("Total memory usage:\n"); + log.appendFormat(" %d bytes, %.2f MB\n", total, total / 1024.0f / 1024.0f); } /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 0a9335f..596781e 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -60,7 +60,12 @@ static const TextureVertex gMeshVertices[] = { FV(1.0f, 1.0f, 1.0f, 1.0f) }; static const GLsizei gMeshStride = sizeof(TextureVertex); +static const GLsizei gVertexStride = sizeof(Vertex); +static const GLsizei gAlphaVertexStride = sizeof(AlphaVertex); +static const GLsizei gAAVertexStride = sizeof(AAVertex); static const GLsizei gMeshTextureOffset = 2 * sizeof(float); +static const GLsizei gVertexAAWidthOffset = 2 * sizeof(float); +static const GLsizei gVertexAALengthOffset = 3 * sizeof(float); static const GLsizei gMeshCount = 4; /////////////////////////////////////////////////////////////////////////////// @@ -140,6 +145,7 @@ public: * Displays the memory usage of each cache and the total sum. */ void dumpMemoryUsage(); + void dumpMemoryUsage(String8& log); bool blend; GLenum lastSrcMode; diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h index 14471bc..2cdc8c3 100644 --- a/libs/hwui/Debug.h +++ b/libs/hwui/Debug.h @@ -26,7 +26,7 @@ // Turn on to enable memory usage summary on each frame #define DEBUG_MEMORY_USAGE 0 -// Turn on to enable layers debugging when renderered as regions +// Turn on to enable layers debugging when rendered as regions #define DEBUG_LAYERS_AS_REGIONS 0 // Turn on to display debug info about vertex/fragment shaders @@ -35,8 +35,10 @@ // Turn on to display info about layers #define DEBUG_LAYERS 0 -// Turn on to display debug infor about 9patch objects +// 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/DisplayListLogBuffer.cpp b/libs/hwui/DisplayListLogBuffer.cpp new file mode 100644 index 0000000..f204644 --- /dev/null +++ b/libs/hwui/DisplayListLogBuffer.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DisplayListLogBuffer.h" + +// BUFFER_SIZE size must be one more than a multiple of COMMAND_SIZE to ensure +// that mStart always points at the next command, not just the next item +#define COMMAND_SIZE 2 +#define NUM_COMMANDS 50 +#define BUFFER_SIZE ((NUM_COMMANDS * COMMAND_SIZE) + 1) + +/** + * DisplayListLogBuffer is a utility class which logs the most recent display + * list operations in a circular buffer. The log is process-wide, because we + * only care about the most recent operations, not the operations on a per-window + * basis for a given activity. The purpose of the log is to provide more debugging + * information in a bug report, by telling us not just where a process hung (which + * generally is just reported as a stack trace at the Java level) or crashed, but + * also what happened immediately before that hang or crash. This may help track down + * problems in the native rendering code or driver interaction related to the display + * list operations that led up to the hang or crash. + * + * The log is implemented as a circular buffer for both space and performance + * reasons - we only care about the last several operations to give us context + * leading up to the problem, and we don't want to constantly copy data around or do + * additional mallocs to keep the most recent operations logged. Only numbers are + * logged to make the operation fast. If and when the log is output, we process this + * data into meaningful strings. + * + * There is an assumption about the format of the command (currently 2 ints: the + * opcode and the nesting level). If the type of information logged changes (for example, + * we may want to save a timestamp), then the size of the buffer and the way the + * information is recorded in writeCommand() should change to suit. + */ + +namespace android { + +#ifdef USE_OPENGL_RENDERER +using namespace uirenderer; +ANDROID_SINGLETON_STATIC_INSTANCE(DisplayListLogBuffer); +#endif + +namespace uirenderer { + + +DisplayListLogBuffer::DisplayListLogBuffer() { + mBufferFirst = (int*) malloc(BUFFER_SIZE * sizeof(int)); + mStart = mBufferFirst; + mBufferLast = mBufferFirst + BUFFER_SIZE - 1; + mEnd = mStart; +} + +DisplayListLogBuffer::~DisplayListLogBuffer() { + free(mBufferFirst); +} + +/** + * Called from DisplayListRenderer to output the current buffer into the + * specified FILE. This only happens in a dumpsys/bugreport operation. + */ +void DisplayListLogBuffer::outputCommands(FILE *file, const char* opNames[]) +{ + int *tmpBufferPtr = mStart; + while (true) { + if (tmpBufferPtr == mEnd) { + break; + } + int level = *tmpBufferPtr++; + if (tmpBufferPtr > mBufferLast) { + tmpBufferPtr = mBufferFirst; + } + int op = *tmpBufferPtr++; + if (tmpBufferPtr > mBufferLast) { + tmpBufferPtr = mBufferFirst; + } + uint32_t count = (level + 1) * 2; + char indent[count + 1]; + for (uint32_t i = 0; i < count; i++) { + indent[i] = ' '; + } + indent[count] = '\0'; + fprintf(file, "%s%s\n", indent, opNames[op]); + } +} + +void DisplayListLogBuffer::writeCommand(int level, int op) { + writeInt(level); + writeInt(op); +} + +/** + * Store the given value in the buffer and increment/wrap the mEnd + * and mStart values as appropriate. + */ +void DisplayListLogBuffer::writeInt(int value) { + *((int*)mEnd) = value; + if (mEnd == mBufferLast) { + mEnd = mBufferFirst; + } else { + mEnd++; + } + if (mEnd == mStart) { + mStart++; + if (mStart > mBufferLast) { + mStart = mBufferFirst; + } + } +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/DisplayListLogBuffer.h b/libs/hwui/DisplayListLogBuffer.h new file mode 100644 index 0000000..bf16f29 --- /dev/null +++ b/libs/hwui/DisplayListLogBuffer.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2011 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_DISPLAY_LIST_LOG_BUFFER_H +#define ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H + +#include <utils/Singleton.h> +#include <stdio.h> + +namespace android { +namespace uirenderer { + +class DisplayListLogBuffer: public Singleton<DisplayListLogBuffer> { + DisplayListLogBuffer(); + ~DisplayListLogBuffer(); + + friend class Singleton<DisplayListLogBuffer>; + +public: + void writeCommand(int level, int op); + void writeInt(int value); + void outputCommands(FILE *file, const char* opNames[]); + + bool isEmpty() { + return (mStart == mEnd); + } + +private: + int *mBufferFirst; // where the memory starts + int* mStart; // where the current command stream starts + int* mEnd; // where the current commands end + int* mBufferLast; // where the buffer memory ends + +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_DISPLAY_LIST_LOG_BUFFER_H diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 868290b..afab26a 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -16,11 +16,16 @@ #define LOG_TAG "OpenGLRenderer" + +#include "DisplayListLogBuffer.h" #include "DisplayListRenderer.h" +#include <utils/String8.h> +#include "Caches.h" namespace android { namespace uirenderer { + /////////////////////////////////////////////////////////////////////////////// // Display list /////////////////////////////////////////////////////////////////////////////// @@ -53,6 +58,7 @@ const char* DisplayList::OP_NAMES[] = { "DrawArc", "DrawPath", "DrawLines", + "DrawPoints", "DrawText", "ResetShader", "SetupShader", @@ -63,6 +69,20 @@ const char* DisplayList::OP_NAMES[] = { "DrawGLFunction" }; +void DisplayList::outputLogBuffer(int fd) { + DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); + if (logBuffer.isEmpty()) { + return; + } + String8 cachesLog; + Caches::getInstance().dumpMemoryUsage(cachesLog); + FILE *file = fdopen(fd, "a"); + fprintf(file, "\nCaches:\n%s", cachesLog.string()); + fprintf(file, "\nRecent DisplayList operations\n"); + logBuffer.outputCommands(file, OP_NAMES); + fflush(file); +} + DisplayList::DisplayList(const DisplayListRenderer& recorder) { initFromDisplayListRenderer(recorder); } @@ -92,13 +112,11 @@ void DisplayList::clearResources() { mPaints.clear(); for (size_t i = 0; i < mPaths.size(); i++) { - delete mPaths.itemAt(i); + SkPath* path = mPaths.itemAt(i); + caches.pathCache.remove(path); + delete path; } mPaths.clear(); - for (size_t i = 0; i < mOriginalPaths.size(); i++) { - caches.resourceCache.decrementRefcount(mOriginalPaths.itemAt(i)); - } - mOriginalPaths.clear(); for (size_t i = 0; i < mMatrices.size(); i++) { delete mMatrices.itemAt(i); @@ -150,13 +168,6 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde mPaths.add(paths.itemAt(i)); } - const Vector<SkPath*> &originalPaths = recorder.getOriginalPaths(); - for (size_t i = 0; i < originalPaths.size(); i++) { - SkPath* path = originalPaths.itemAt(i); - mOriginalPaths.add(path); - caches.resourceCache.incrementRefcount(path); - } - const Vector<SkMatrix*> &matrices = recorder.getMatrices(); for (size_t i = 0; i < matrices.size(); i++) { mMatrices.add(matrices.itemAt(i)); @@ -166,6 +177,331 @@ void DisplayList::initFromDisplayListRenderer(const DisplayListRenderer& recorde void DisplayList::init() { } +/** + * This function is a simplified version of replay(), where we simply retrieve and log the + * display list. This function should remain in sync with the replay() function. + */ +void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { + TextContainer text; + + uint32_t count = (level + 1) * 2; + char indent[count + 1]; + for (uint32_t i = 0; i < count; i++) { + indent[i] = ' '; + } + indent[count] = '\0'; + LOGD("%sStart display list (%p)", (char*) indent + 2, this); + + int saveCount = renderer.getSaveCount() - 1; + + mReader.rewind(); + + while (!mReader.eof()) { + int op = mReader.readInt(); + + switch (op) { + case DrawGLFunction: { + Functor *functor = (Functor *) getInt(); + LOGD("%s%s %p", (char*) indent, OP_NAMES[op], functor); + } + break; + case Save: { + int rendererNum = getInt(); + LOGD("%s%s %d", (char*) indent, OP_NAMES[op], rendererNum); + } + break; + case Restore: { + LOGD("%s%s", (char*) indent, OP_NAMES[op]); + } + break; + case RestoreToCount: { + int restoreCount = saveCount + getInt(); + LOGD("%s%s %d", (char*) indent, OP_NAMES[op], restoreCount); + } + break; + case SaveLayer: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + SkPaint* paint = getPaint(); + int flags = getInt(); + LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p, 0x%x", (char*) indent, + OP_NAMES[op], f1, f2, f3, f4, paint, flags); + } + break; + case SaveLayerAlpha: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + int alpha = getInt(); + int flags = getInt(); + LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d, 0x%x", (char*) indent, + OP_NAMES[op], f1, f2, f3, f4, alpha, flags); + } + break; + case Translate: { + float f1 = getFloat(); + float f2 = getFloat(); + LOGD("%s%s %.2f, %.2f", (char*) indent, OP_NAMES[op], f1, f2); + } + break; + case Rotate: { + float rotation = getFloat(); + LOGD("%s%s %.2f", (char*) indent, OP_NAMES[op], rotation); + } + break; + case Scale: { + float sx = getFloat(); + float sy = getFloat(); + LOGD("%s%s %.2f, %.2f", (char*) indent, OP_NAMES[op], sx, sy); + } + break; + case Skew: { + float sx = getFloat(); + float sy = getFloat(); + LOGD("%s%s %.2f, %.2f", (char*) indent, OP_NAMES[op], sx, sy); + } + break; + case SetMatrix: { + SkMatrix* matrix = getMatrix(); + LOGD("%s%s %p", (char*) indent, OP_NAMES[op], matrix); + } + break; + case ConcatMatrix: { + SkMatrix* matrix = getMatrix(); + LOGD("%s%s %p", (char*) indent, OP_NAMES[op], matrix); + } + break; + case ClipRect: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + int regionOp = getInt(); + LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d", (char*) indent, OP_NAMES[op], + f1, f2, f3, f4, regionOp); + } + break; + case DrawDisplayList: { + DisplayList* displayList = getDisplayList(); + uint32_t width = getUInt(); + uint32_t height = getUInt(); + LOGD("%s%s %p, %dx%d, %d", (char*) indent, OP_NAMES[op], + displayList, width, height, level + 1); + renderer.outputDisplayList(displayList, level + 1); + } + break; + case DrawLayer: { + Layer* layer = (Layer*) getInt(); + float x = getFloat(); + float y = getFloat(); + SkPaint* paint = getPaint(); + LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], + layer, x, y, paint); + } + break; + case DrawBitmap: { + SkBitmap* bitmap = getBitmap(); + float x = getFloat(); + float y = getFloat(); + SkPaint* paint = getPaint(); + LOGD("%s%s %p, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], + bitmap, x, y, paint); + } + break; + case DrawBitmapMatrix: { + SkBitmap* bitmap = getBitmap(); + SkMatrix* matrix = getMatrix(); + SkPaint* paint = getPaint(); + LOGD("%s%s %p, %p, %p", (char*) indent, OP_NAMES[op], + bitmap, matrix, paint); + } + break; + case DrawBitmapRect: { + SkBitmap* bitmap = getBitmap(); + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + float f5 = getFloat(); + float f6 = getFloat(); + float f7 = getFloat(); + float f8 = getFloat(); + SkPaint* paint = getPaint(); + LOGD("%s%s %p, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p", + (char*) indent, OP_NAMES[op], bitmap, f1, f2, f3, f4, f5, f6, f7, f8, paint); + } + break; + case DrawBitmapMesh: { + int verticesCount = 0; + uint32_t colorsCount = 0; + SkBitmap* bitmap = getBitmap(); + uint32_t meshWidth = getInt(); + uint32_t meshHeight = getInt(); + float* vertices = getFloats(verticesCount); + bool hasColors = getInt(); + int* colors = hasColors ? getInts(colorsCount) : NULL; + SkPaint* paint = getPaint(); + LOGD("%s%s", (char*) indent, OP_NAMES[op]); + } + break; + case DrawPatch: { + int32_t* xDivs = NULL; + int32_t* yDivs = NULL; + uint32_t* colors = NULL; + uint32_t xDivsCount = 0; + uint32_t yDivsCount = 0; + int8_t numColors = 0; + SkBitmap* bitmap = getBitmap(); + xDivs = getInts(xDivsCount); + yDivs = getInts(yDivsCount); + colors = getUInts(numColors); + DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); + getFloat(); + getFloat(); + getFloat(); + getFloat(); + getPaint(); + } + break; + case DrawColor: { + int color = getInt(); + int xferMode = getInt(); + LOGD("%s%s 0x%x %d", (char*) indent, OP_NAMES[op], color, xferMode); + } + break; + case DrawRect: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + SkPaint* paint = getPaint(); + LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], + f1, f2, f3, f4, paint); + } + break; + case DrawRoundRect: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + float f5 = getFloat(); + float f6 = getFloat(); + SkPaint* paint = getPaint(); + LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %p", + (char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, paint); + } + break; + case DrawCircle: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + SkPaint* paint = getPaint(); + LOGD("%s%s %.2f, %.2f, %.2f, %p", + (char*) indent, OP_NAMES[op], f1, f2, f3, paint); + } + break; + case DrawOval: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + SkPaint* paint = getPaint(); + LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p", + (char*) indent, OP_NAMES[op], f1, f2, f3, f4, paint); + } + break; + case DrawArc: { + float f1 = getFloat(); + float f2 = getFloat(); + float f3 = getFloat(); + float f4 = getFloat(); + float f5 = getFloat(); + float f6 = getFloat(); + int i1 = getInt(); + SkPaint* paint = getPaint(); + LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p", + (char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, i1, paint); + } + break; + case DrawPath: { + SkPath* path = getPath(); + SkPaint* paint = getPaint(); + LOGD("%s%s %p, %p", (char*) indent, OP_NAMES[op], path, paint); + } + break; + case DrawLines: { + int count = 0; + float* points = getFloats(count); + SkPaint* paint = getPaint(); + LOGD("%s%s", (char*) indent, OP_NAMES[op]); + } + break; + case DrawPoints: { + int count = 0; + float* points = getFloats(count); + SkPaint* paint = getPaint(); + LOGD("%s%s", (char*) indent, OP_NAMES[op]); + } + break; + case DrawText: { + getText(&text); + int count = getInt(); + float x = getFloat(); + float y = getFloat(); + SkPaint* paint = getPaint(); + LOGD("%s%s %s, %d, %d, %.2f, %.2f, %p", (char*) indent, OP_NAMES[op], + text.text(), text.length(), count, x, y, paint); + } + break; + case ResetShader: { + LOGD("%s%s", (char*) indent, OP_NAMES[op]); + } + break; + case SetupShader: { + SkiaShader* shader = getShader(); + LOGD("%s%s %p", (char*) indent, OP_NAMES[op], shader); + } + break; + case ResetColorFilter: { + LOGD("%s%s", (char*) indent, OP_NAMES[op]); + } + break; + case SetupColorFilter: { + SkiaColorFilter *colorFilter = getColorFilter(); + LOGD("%s%s %p", (char*) indent, OP_NAMES[op], colorFilter); + } + break; + case ResetShadow: { + LOGD("%s%s", (char*) indent, OP_NAMES[op]); + } + break; + case SetupShadow: { + float radius = getFloat(); + float dx = getFloat(); + float dy = getFloat(); + int color = getInt(); + LOGD("%s%s %.2f, %.2f, %.2f, 0x%x", (char*) indent, OP_NAMES[op], + radius, dx, dy, color); + } + break; + default: + LOGD("Display List error: op not handled: %s%s", + (char*) indent, OP_NAMES[op]); + break; + } + } + + LOGD("%sDone", (char*) indent + 2); +} + +/** + * Changes to replay(), specifically those involving opcode or parameter changes, should be mimicked + * in the output() function, since that function processes the same list of opcodes for the + * purposes of logging display list info for a given view. + */ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) { bool needsInvalidate = false; TextContainer text; @@ -181,9 +517,11 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) DISPLAY_LIST_LOGD("%sStart display list (%p)", (char*) indent + 2, this); #endif + DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); int saveCount = renderer.getSaveCount() - 1; while (!mReader.eof()) { int op = mReader.readInt(); + logBuffer.writeCommand(level, op); switch (op) { case DrawGLFunction: { @@ -452,6 +790,13 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) renderer.drawLines(points, count, getPaint()); } break; + case DrawPoints: { + int count = 0; + float* points = getFloats(count); + DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); + renderer.drawPoints(points, count, getPaint()); + } + break; case DrawText: { getText(&text); int count = getInt(); @@ -533,12 +878,6 @@ void DisplayListRenderer::reset() { } mBitmapResources.clear(); - for (size_t i = 0; i < mOriginalPaths.size(); i++) { - SkPath* resource = mOriginalPaths.itemAt(i); - caches.resourceCache.decrementRefcount(resource); - } - mOriginalPaths.clear(); - for (size_t i = 0; i < mShaders.size(); i++) { caches.resourceCache.decrementRefcount(mShaders.itemAt(i)); } @@ -804,8 +1143,15 @@ void DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint) { addPaint(paint); } +void DisplayListRenderer::drawPoints(float* points, int count, SkPaint* paint) { + addOp(DisplayList::DrawPoints); + addFloats(points, count); + addPaint(paint); +} + void DisplayListRenderer::drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint) { + if (count <= 0) return; addOp(DisplayList::DrawText); addText(text, bytesCount); addInt(count); diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index 6fc315c..dcf2cf2 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -26,6 +26,7 @@ #include <SkTDArray.h> #include <SkTSearch.h> +#include "DisplayListLogBuffer.h" #include "OpenGLRenderer.h" #include "utils/Functor.h" @@ -89,6 +90,7 @@ public: DrawArc, DrawPath, DrawLines, + DrawPoints, DrawText, ResetShader, SetupShader, @@ -105,6 +107,10 @@ public: bool replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level = 0); + void output(OpenGLRenderer& renderer, uint32_t level = 0); + + static void outputLogBuffer(int fd); + private: void init(); @@ -193,7 +199,6 @@ private: Vector<SkPaint*> mPaints; Vector<SkPath*> mPaths; - Vector<SkPath*> mOriginalPaths; Vector<SkMatrix*> mMatrices; Vector<SkiaShader*> mShaders; @@ -265,6 +270,7 @@ public: float startAngle, float sweepAngle, bool useCenter, SkPaint* paint); void drawPath(SkPath* path, SkPaint* paint); void drawLines(float* points, int count, SkPaint* paint); + void drawPoints(float* points, int count, SkPaint* paint); void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint); void resetShader(); @@ -298,10 +304,6 @@ public: return mPaths; } - const Vector<SkPath*>& getOriginalPaths() const { - return mOriginalPaths; - } - const Vector<SkMatrix*>& getMatrices() const { return mMatrices; } @@ -383,16 +385,9 @@ private: SkPath* pathCopy = mPathMap.valueFor(path); if (pathCopy == NULL || pathCopy->getGenerationID() != path->getGenerationID()) { - if (pathCopy == NULL) { - pathCopy = path; - mOriginalPaths.add(path); - Caches& caches = Caches::getInstance(); - caches.resourceCache.incrementRefcount(path); - } else { - pathCopy = new SkPath(*path); - mPaths.add(pathCopy); - } + pathCopy = new SkPath(*path); mPathMap.add(path, pathCopy); + mPaths.add(pathCopy); } addInt((int) pathCopy); @@ -469,7 +464,6 @@ private: Vector<SkPaint*> mPaints; DefaultKeyedVector<SkPaint*, SkPaint*> mPaintMap; - Vector<SkPath*> mOriginalPaths; Vector<SkPath*> mPaths; DefaultKeyedVector<SkPath*, SkPath*> mPathMap; diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index aa9b40e..9bf3de8 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -35,6 +35,10 @@ namespace uirenderer { #define DEFAULT_TEXT_CACHE_WIDTH 1024 #define DEFAULT_TEXT_CACHE_HEIGHT 256 +// We should query these values from the GL context +#define MAX_TEXT_CACHE_WIDTH 2048 +#define MAX_TEXT_CACHE_HEIGHT 2048 + /////////////////////////////////////////////////////////////////////////////// // Font /////////////////////////////////////////////////////////////////////////////// @@ -55,8 +59,7 @@ Font::~Font() { } for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) { - CachedGlyphInfo* glyph = mCachedGlyphs.valueAt(i); - delete glyph; + delete mCachedGlyphs.valueAt(i); } } @@ -131,48 +134,49 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, } -Font::CachedGlyphInfo* Font::getCachedUTFChar(SkPaint* paint, int32_t utfChar) { +Font::CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) { CachedGlyphInfo* cachedGlyph = NULL; - ssize_t index = mCachedGlyphs.indexOfKey(utfChar); + ssize_t index = mCachedGlyphs.indexOfKey(textUnit); if (index >= 0) { cachedGlyph = mCachedGlyphs.valueAt(index); } else { - cachedGlyph = cacheGlyph(paint, utfChar); + cachedGlyph = cacheGlyph(paint, textUnit); } // Is the glyph still in texture cache? if (!cachedGlyph->mIsValid) { - const SkGlyph& skiaGlyph = paint->getUnicharMetrics(utfChar); + const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit); updateGlyphCache(paint, skiaGlyph, cachedGlyph); } return cachedGlyph; } -void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, +void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) { if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) { - renderUTF(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap, + render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap, bitmapW, bitmapH, NULL); } else { - renderUTF(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, 0, 0, NULL); + render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL, + 0, 0, NULL); } } -void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, +void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, Rect *bounds) { if (bounds == NULL) { LOGE("No return rectangle provided to measure text"); return; } bounds->set(1e6, -1e6, -1e6, 1e6); - renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds); + render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds); } #define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16) -void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, +void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) { if (numGlyphs == 0 || text == NULL || len == 0) { @@ -192,14 +196,14 @@ void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t text += start; while (glyphsLeft > 0) { - int32_t utfChar = SkUTF16_NextUnichar((const uint16_t**) &text); + glyph_t glyph = GET_GLYPH(text); // Reached the end of the string - if (utfChar < 0) { + if (IS_END_OF_STRING(glyph)) { break; } - CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar); + CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta); prevRsbDelta = cachedGlyph->mRsbDelta; @@ -265,11 +269,11 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp mState->mUploadTexture = true; } -Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, int32_t glyph) { +Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) { CachedGlyphInfo* newGlyph = new CachedGlyphInfo(); mCachedGlyphs.add(glyph, newGlyph); - const SkGlyph& skiaGlyph = paint->getUnicharMetrics(glyph); + const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph); newGlyph->mGlyphIndex = skiaGlyph.fID; newGlyph->mIsValid = false; @@ -386,9 +390,17 @@ void FontRenderer::flushAllAndInvalidate() { bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) { // If the glyph is too tall, don't cache it if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { - LOGE("Font size to large to fit in cache. width, height = %i, %i", - (int) glyph.fWidth, (int) glyph.fHeight); - return false; + if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) { + // Default cache not large enough for large glyphs - resize cache to + // max size and try again + flushAllAndInvalidate(); + initTextTexture(true); + } + if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { + LOGE("Font size to large to fit in cache. width, height = %i, %i", + (int) glyph.fWidth, (int) glyph.fHeight); + return false; + } } // Now copy the bitmap into the cache texture @@ -446,16 +458,25 @@ bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint3 return true; } -void FontRenderer::initTextTexture() { +void FontRenderer::initTextTexture(bool largeFonts) { + mCacheLines.clear(); + if (largeFonts) { + mCacheWidth = MAX_TEXT_CACHE_WIDTH; + mCacheHeight = MAX_TEXT_CACHE_HEIGHT; + } + mTextTexture = new uint8_t[mCacheWidth * mCacheHeight]; memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t)); mUploadTexture = false; + if (mTextureId != 0) { + glDeleteTextures(1, &mTextureId); + } glGenTextures(1, &mTextureId); glBindTexture(GL_TEXTURE_2D, mTextureId); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - // Initialize texture dimentions + // Initialize texture dimensions glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0); @@ -480,6 +501,15 @@ void FontRenderer::initTextTexture() { nextLine += mCacheLines.top()->mMaxHeight; mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0)); nextLine += mCacheLines.top()->mMaxHeight; + if (largeFonts) { + int nextSize = 76; + // Make several new lines with increasing font sizes + while (nextSize < (int)(mCacheHeight - nextLine - (2 * nextSize))) { + mCacheLines.push(new CacheTextureLine(mCacheWidth, nextSize, nextLine, 0)); + nextLine += mCacheLines.top()->mMaxHeight; + nextSize += 50; + } + } mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0)); } @@ -643,7 +673,7 @@ void FontRenderer::precacheLatin(SkPaint* paint) { uint32_t remainingCapacity = getRemainingCacheCapacity(); uint32_t precacheIdx = 0; while (remainingCapacity > 25 && precacheIdx < mLatinPrecache.size()) { - mCurrentFont->getCachedUTFChar(paint, (int32_t) mLatinPrecache[precacheIdx]); + mCurrentFont->getCachedGlyph(paint, (int32_t) mLatinPrecache[precacheIdx]); remainingCapacity = getRemainingCacheCapacity(); precacheIdx ++; } @@ -685,7 +715,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch } Rect bounds; - mCurrentFont->measureUTF(paint, text, startIndex, len, numGlyphs, &bounds); + mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds); uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; uint8_t* dataBuffer = new uint8_t[paddedWidth * paddedHeight]; @@ -696,7 +726,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch int penX = radius - bounds.left; int penY = radius - bounds.bottom; - mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, penX, penY, + mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, dataBuffer, paddedWidth, paddedHeight); blurImage(dataBuffer, paddedWidth, paddedHeight, radius); @@ -726,7 +756,7 @@ bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text mDrawn = false; mBounds = bounds; mClip = clip; - mCurrentFont->renderUTF(paint, text, startIndex, len, numGlyphs, x, y); + mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y); mBounds = NULL; if (mCurrentQuadIndex != 0) { diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index f685d5f..24ed6fa 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -33,8 +33,32 @@ namespace android { namespace uirenderer { +/////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +#if RENDER_TEXT_AS_GLYPHS + typedef uint16_t glyph_t; + #define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph) + #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text) + #define IS_END_OF_STRING(glyph) false +#else + typedef SkUnichar glyph_t; + #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph) + #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text) + #define IS_END_OF_STRING(glyph) glyph < 0 +#endif + +/////////////////////////////////////////////////////////////////////////////// +// Declarations +/////////////////////////////////////////////////////////////////////////////// + class FontRenderer; +/////////////////////////////////////////////////////////////////////////////// +// Font +/////////////////////////////////////////////////////////////////////////////// + /** * Represents a font, defined by a Skia font id and a font size. A font is used * to generate glyphs and cache them in the FontState. @@ -51,9 +75,9 @@ public: * Renders the specified string of text. * If bitmap is specified, it will be used as the render target */ - void renderUTF(SkPaint* paint, const char *text, uint32_t start, uint32_t len, - int numGlyphs, int x, int y, - uint8_t *bitmap = NULL, uint32_t bitmapW = 0, uint32_t bitmapH = 0); + void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, + int numGlyphs, int x, int y, uint8_t *bitmap = NULL, + uint32_t bitmapW = 0, uint32_t bitmapH = 0); /** * Creates a new font associated with the specified font state. */ @@ -69,13 +93,12 @@ protected: MEASURE, }; - void renderUTF(SkPaint* paint, const char *text, uint32_t start, uint32_t len, - int numGlyphs, int x, int y, RenderMode mode, - uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, - Rect *bounds); + void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len, + int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, + uint32_t bitmapW, uint32_t bitmapH, Rect *bounds); - void measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, - int numGlyphs, Rect *bounds); + void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len, + int numGlyphs, Rect *bounds); struct CachedGlyphInfo { // Has the cache been invalidated? @@ -107,18 +130,26 @@ protected: Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle, uint32_t scaleX); - DefaultKeyedVector<int32_t, CachedGlyphInfo*> mCachedGlyphs; + // Cache of glyphs + DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs; void invalidateTextureCache(); - CachedGlyphInfo* cacheGlyph(SkPaint* paint, int32_t glyph); + CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph); void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo *glyph); void measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds); void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y); void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y, - uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH); + uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH); - CachedGlyphInfo* getCachedUTFChar(SkPaint* paint, int32_t utfChar); + CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit); + + static glyph_t nextGlyph(const uint16_t** srcPtr) { + const uint16_t* src = *srcPtr; + glyph_t g = *src++; + *srcPtr = src; + return g; + } FontRenderer* mState; uint32_t mFontId; @@ -128,6 +159,10 @@ protected: uint32_t mScaleX; }; +/////////////////////////////////////////////////////////////////////////////// +// Renderer +/////////////////////////////////////////////////////////////////////////////// + class FontRenderer { public: FontRenderer(); @@ -229,7 +264,7 @@ protected: } }; - void initTextTexture(); + void initTextTexture(bool largeFonts = false); bool cacheBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY); void flushAllAndInvalidate(); diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index bb28437..0310bc3 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -45,6 +45,9 @@ struct Layer { mesh = NULL; meshIndices = NULL; meshElementCount = 0; + isCacheable = true; + isTextureLayer = false; + renderTarget = GL_TEXTURE_2D; } ~Layer() { @@ -53,6 +56,23 @@ struct Layer { } /** + * Sets this layer's region to a rectangle. Computes the appropriate + * texture coordinates. + */ + void setRegionAsRect() { + const android::Rect& bounds = region.getBounds(); + regionRect.set(bounds.leftTop().x, bounds.leftTop().y, + bounds.rightBottom().x, bounds.rightBottom().y); + + const float texX = 1.0f / float(width); + const float texY = 1.0f / float(height); + const float height = layer.getHeight(); + texCoords.set( + regionRect.left * texX, (height - regionRect.top) * texY, + regionRect.right * texX, (height - regionRect.bottom) * texY); + } + + /** * Bounds of the layer. */ Rect layer; @@ -103,6 +123,11 @@ struct Layer { * have been drawn. */ Region region; + /** + * If the region is a rectangle, coordinates of the + * region are stored here. + */ + Rect regionRect; /** * Color filter used to draw this layer. Optional. @@ -115,6 +140,27 @@ struct Layer { TextureVertex* mesh; uint16_t* meshIndices; GLsizei meshElementCount; + + /** + * If set to true (by default), the layer can be reused. + */ + bool isCacheable; + + /** + * When set to true, this layer must be treated as a texture + * layer. + */ + bool isTextureLayer; + + /** + * Optional texture coordinates transform. + */ + mat4 texTransform; + + /** + * Indicates the render target. + */ + GLenum renderTarget; }; // struct Layer }; // namespace uirenderer diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index a9710ad..b2d795f 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -154,6 +154,8 @@ bool LayerCache::resize(Layer* layer, const uint32_t width, const uint32_t heigh } bool LayerCache::put(Layer* layer) { + if (!layer->isCacheable) return false; + const uint32_t size = layer->width * layer->height * 4; // Don't even try to cache a layer that's bigger than the cache if (size < mMaxSize) { diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index f92e20b..f316ba7 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -20,6 +20,7 @@ #include "LayerCache.h" #include "LayerRenderer.h" +#include "Matrix.h" #include "Properties.h" #include "Rect.h" @@ -92,11 +93,7 @@ Region* LayerRenderer::getRegion() { void LayerRenderer::generateMesh() { #if RENDER_LAYERS_AS_REGIONS -#if RENDER_LAYERS_RECT_AS_RECT if (mLayer->region.isRect() || mLayer->region.isEmpty()) { -#else - if (mLayer->region.isEmpty()) { -#endif if (mLayer->mesh) { delete mLayer->mesh; delete mLayer->meshIndices; @@ -105,6 +102,8 @@ void LayerRenderer::generateMesh() { mLayer->meshIndices = NULL; mLayer->meshElementCount = 0; } + + mLayer->setRegionAsRect(); return; } @@ -167,6 +166,30 @@ void LayerRenderer::generateMesh() { // Layers management /////////////////////////////////////////////////////////////////////////////// +Layer* LayerRenderer::createTextureLayer() { + LAYER_RENDERER_LOGD("Creating new texture layer"); + + Layer* layer = new Layer(0, 0); + layer->isCacheable = false; + layer->isTextureLayer = true; + layer->blend = true; + layer->empty = true; + layer->fbo = 0; + layer->colorFilter = NULL; + layer->fbo = 0; + layer->layer.set(0.0f, 0.0f, 0.0f, 0.0f); + layer->texCoords.set(0.0f, 1.0f, 0.0f, 1.0f); + layer->alpha = 255; + layer->mode = SkXfermode::kSrcOver_Mode; + layer->colorFilter = NULL; + layer->region.clear(); + + glActiveTexture(GL_TEXTURE0); + glGenTextures(1, &layer->texture); + + return layer; +} + Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) { LAYER_RENDERER_LOGD("Creating new layer %dx%d", width, height); @@ -246,6 +269,27 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) { return true; } +void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, + GLenum renderTarget, float* transform) { + if (layer) { + layer->width = width; + layer->height = height; + layer->layer.set(0.0f, 0.0f, width, height); + layer->region.set(width, height); + layer->regionRect.set(0.0f, 0.0f, width, height); + layer->texTransform.load(transform); + layer->renderTarget = renderTarget; + + glBindTexture(layer->renderTarget, layer->texture); + + glTexParameteri(layer->renderTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(layer->renderTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(layer->renderTarget, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(layer->renderTarget, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } +} + void LayerRenderer::destroyLayer(Layer* layer) { if (layer) { LAYER_RENDERER_LOGD("Destroying layer, fbo = %d", layer->fbo); diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h index d2f565e..59cab96 100644 --- a/libs/hwui/LayerRenderer.h +++ b/libs/hwui/LayerRenderer.h @@ -53,8 +53,11 @@ public: Region* getRegion(); GLint getTargetFbo(); + static Layer* createTextureLayer(); static Layer* createLayer(uint32_t width, uint32_t height, bool isOpaque = false); static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height); + static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, + GLenum renderTarget, float* transform); static void destroyLayer(Layer* layer); static void destroyLayerDeferred(Layer* layer); diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp index e7c0fe3..9fc5131 100644 --- a/libs/hwui/Matrix.cpp +++ b/libs/hwui/Matrix.cpp @@ -67,6 +67,10 @@ bool Matrix4::isPureTranslate() { ALMOST_EQUAL(data[kScaleX], 1.0f) && ALMOST_EQUAL(data[kScaleY], 1.0f); } +bool Matrix4::isSimple() { + return mSimpleMatrix; +} + void Matrix4::load(const float* v) { memcpy(data, v, sizeof(data)); mSimpleMatrix = false; diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index 08f5d77..2fa6ab7 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -111,6 +111,7 @@ public: } bool isPureTranslate(); + bool isSimple(); bool changesBounds(); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index d9d7d23..6c9c0eb 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -63,7 +63,7 @@ struct Blender { // In this array, the index of each Blender equals the value of the first // entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode] static const Blender gBlends[] = { - { SkXfermode::kClear_Mode, GL_ZERO, GL_ZERO }, + { SkXfermode::kClear_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO }, { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE }, { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, @@ -81,7 +81,7 @@ static const Blender gBlends[] = { // this array's SrcOver blending mode is actually DstOver. You can refer to // createLayer() for more information on the purpose of this array. static const Blender gBlendsSwap[] = { - { SkXfermode::kClear_Mode, GL_ZERO, GL_ZERO }, + { SkXfermode::kClear_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE }, { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO }, { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE }, @@ -633,26 +633,64 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { } } +void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { + float alpha = layer->alpha / 255.0f; + + setupDraw(); + if (layer->renderTarget == GL_TEXTURE_2D) { + setupDrawWithTexture(); + } else { + setupDrawWithExternalTexture(); + } + setupDrawTextureTransform(); + setupDrawColor(alpha, alpha, alpha, alpha); + setupDrawColorFilter(); + setupDrawBlending(layer->blend, layer->mode); + setupDrawProgram(); + setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom); + setupDrawPureColorUniforms(); + setupDrawColorFilterUniforms(); + if (layer->renderTarget == GL_TEXTURE_2D) { + setupDrawTexture(layer->texture); + } else { + setupDrawExternalTexture(layer->texture); + } + setupDrawTextureTransformUniforms(layer->texTransform); + 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) { - const Rect& texCoords = layer->texCoords; - resetDrawTextureTexCoords(texCoords.left, texCoords.top, texCoords.right, texCoords.bottom); + if (!layer->isTextureLayer) { + const Rect& texCoords = layer->texCoords; + resetDrawTextureTexCoords(texCoords.left, texCoords.top, + texCoords.right, texCoords.bottom); - drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture, - layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0], - &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, swap, swap); + drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture, + layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0], + &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, swap, swap); - resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); + resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); + } else { + resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f); + drawTextureLayer(layer, rect); + resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); + } } void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { #if RENDER_LAYERS_AS_REGIONS -#if RENDER_LAYERS_RECT_AS_RECT if (layer->region.isRect()) { - composeLayerRect(layer, rect); + layer->setRegionAsRect(); + + composeLayerRect(layer, layer->regionRect); + layer->region.clear(); return; } -#endif if (!layer->region.isEmpty()) { size_t count; @@ -881,12 +919,27 @@ void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) { mDescription.hasAlpha8Texture = isAlpha8; } +void OpenGLRenderer::setupDrawWithExternalTexture() { + mDescription.hasExternalTexture = true; +} + +void OpenGLRenderer::setupDrawAALine() { + mDescription.isAA = true; +} + +void OpenGLRenderer::setupDrawPoint(float pointSize) { + mDescription.isPoint = true; + mDescription.pointSize = pointSize; +} + void OpenGLRenderer::setupDrawColor(int color) { setupDrawColor(color, (color >> 24) & 0xFF); } void OpenGLRenderer::setupDrawColor(int color, int alpha) { mColorA = alpha / 255.0f; + // Second divide of a by 255 is an optimization, allowing us to simply multiply + // the rgb values by a instead of also dividing by 255 const float a = mColorA / 255.0f; mColorR = a * ((color >> 16) & 0xFF); mColorG = a * ((color >> 8) & 0xFF); @@ -897,6 +950,8 @@ void OpenGLRenderer::setupDrawColor(int color, int alpha) { void OpenGLRenderer::setupDrawAlpha8Color(int color, int alpha) { mColorA = alpha / 255.0f; + // Double-divide of a by 255 is an optimization, allowing us to simply multiply + // the rgb values by a instead of also dividing by 255 const float a = mColorA / 255.0f; mColorR = a * ((color >> 16) & 0xFF); mColorG = a * ((color >> 8) & 0xFF); @@ -935,12 +990,26 @@ void OpenGLRenderer::setupDrawColorFilter() { } } +void OpenGLRenderer::accountForClear(SkXfermode::Mode mode) { + if (mColorSet && mode == SkXfermode::kClear_Mode) { + mColorA = 1.0f; + mColorR = mColorG = mColorB = 0.0f; + mSetShaderColor = mDescription.setAlpha8Color(mColorR, mColorG, mColorB, mColorA); + } +} + void OpenGLRenderer::setupDrawBlending(SkXfermode::Mode mode, bool swapSrcDst) { + // When the blending mode is kClear_Mode, we need to use a modulate color + // argb=1,0,0,0 + accountForClear(mode); chooseBlending((mColorSet && mColorA < 1.0f) || (mShader && mShader->blend()), mode, mDescription, swapSrcDst); } void OpenGLRenderer::setupDrawBlending(bool blend, SkXfermode::Mode mode, bool swapSrcDst) { + // When the blending mode is kClear_Mode, we need to use a modulate color + // argb=1,0,0,0 + accountForClear(mode); chooseBlending(blend || (mColorSet && mColorA < 1.0f) || (mShader && mShader->blend()), mode, mDescription, swapSrcDst); } @@ -965,8 +1034,8 @@ void OpenGLRenderer::setupDrawModelViewTranslate(float left, float top, float ri } } -void OpenGLRenderer::setupDrawModelViewIdentity() { - mCaches.currentProgram->set(mOrthoMatrix, mIdentity, *mSnapshot->transform); +void OpenGLRenderer::setupDrawModelViewIdentity(bool offset) { + mCaches.currentProgram->set(mOrthoMatrix, mIdentity, *mSnapshot->transform, offset); } void OpenGLRenderer::setupDrawModelView(float left, float top, float right, float bottom, @@ -989,6 +1058,11 @@ 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 || (mShader && mSetShaderColor)) { mCaches.currentProgram->setColor(mColorR, mColorG, mColorB, mColorA); @@ -1036,6 +1110,23 @@ void OpenGLRenderer::setupDrawTexture(GLuint texture) { glEnableVertexAttribArray(mTexCoordsSlot); } +void OpenGLRenderer::setupDrawExternalTexture(GLuint texture) { + bindExternalTexture(texture); + glUniform1i(mCaches.currentProgram->getUniform("sampler"), mTextureUnit++); + + mTexCoordsSlot = mCaches.currentProgram->getAttrib("texCoords"); + glEnableVertexAttribArray(mTexCoordsSlot); +} + +void OpenGLRenderer::setupDrawTextureTransform() { + mDescription.hasTextureTransform = true; +} + +void OpenGLRenderer::setupDrawTextureTransformUniforms(mat4& transform) { + glUniformMatrix4fv(mCaches.currentProgram->getUniform("mainTextureTransform"), 1, + GL_FALSE, &transform.data[0]); +} + void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) { if (!vertices) { mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo); @@ -1049,6 +1140,41 @@ void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint v } } +void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) { + mCaches.unbindMeshBuffer(); + glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, + gVertexStride, vertices); +} + +/** + * Sets up the shader to draw an AA line. We draw AA lines with quads, where there is an + * outer boundary that fades out to 0. The variables set in the shader define the proportion of + * the width and length of the primitive occupied by the AA region. The vtxWidth and vtxLength + * attributes (one per vertex) are values from zero to one that tells the fragment + * shader where the fragment is in relation to the line width/length overall; these values are + * then used to compute the proper color, based on whether the fragment lies in the fading AA + * region of the line. + * Note that we only pass down the width values in this setup function. The length coordinates + * are set up for each individual segment. + */ +void OpenGLRenderer::setupDrawAALine(GLvoid* vertices, GLvoid* widthCoords, + GLvoid* lengthCoords, float boundaryWidthProportion) { + mCaches.unbindMeshBuffer(); + glVertexAttribPointer(mCaches.currentProgram->position, 2, GL_FLOAT, GL_FALSE, + gAAVertexStride, vertices); + int widthSlot = mCaches.currentProgram->getAttrib("vtxWidth"); + glEnableVertexAttribArray(widthSlot); + glVertexAttribPointer(widthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, widthCoords); + int lengthSlot = mCaches.currentProgram->getAttrib("vtxLength"); + glEnableVertexAttribArray(lengthSlot); + glVertexAttribPointer(lengthSlot, 1, GL_FLOAT, GL_FALSE, gAAVertexStride, lengthCoords); + int boundaryWidthSlot = mCaches.currentProgram->getUniform("boundaryWidth"); + glUniform1f(boundaryWidthSlot, boundaryWidthProportion); + // Setting the inverse value saves computations per-fragment in the shader + int inverseBoundaryWidthSlot = mCaches.currentProgram->getUniform("inverseBoundaryWidth"); + glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion)); +} + void OpenGLRenderer::finishDrawTexture() { glDisableVertexAttribArray(mTexCoordsSlot); } @@ -1072,6 +1198,50 @@ bool OpenGLRenderer::drawDisplayList(DisplayList* displayList, uint32_t width, u return false; } +void OpenGLRenderer::outputDisplayList(DisplayList* displayList, uint32_t level) { + if (displayList) { + displayList->output(*this, level); + } +} + +void OpenGLRenderer::drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint) { + int alpha; + SkXfermode::Mode mode; + getAlphaAndMode(paint, &alpha, &mode); + + setTextureWrapModes(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); + + float x = left; + float y = top; + + bool ignoreTransform = false; + if (mSnapshot->transform->isPureTranslate()) { + x = (int) floorf(left + mSnapshot->transform->getTranslateX() + 0.5f); + y = (int) floorf(top + mSnapshot->transform->getTranslateY() + 0.5f); + ignoreTransform = true; + } + + setupDraw(); + setupDrawWithTexture(true); + if (paint) { + setupDrawAlpha8Color(paint->getColor(), alpha); + } + setupDrawColorFilter(); + setupDrawShader(); + setupDrawBlending(true, mode); + setupDrawProgram(); + setupDrawModelView(x, y, x + texture->width, y + texture->height, ignoreTransform); + setupDrawTexture(texture->id); + setupDrawPureColorUniforms(); + setupDrawColorFilterUniforms(); + setupDrawShaderUniforms(); + setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); + + finishDrawTexture(); +} + void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) { const float right = left + bitmap->width(); const float bottom = top + bitmap->height(); @@ -1085,7 +1255,11 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint if (!texture) return; const AutoTexture autoCleanup(texture); - drawTextureRect(left, top, right, bottom, texture, paint); + if (bitmap->getConfig() == SkBitmap::kA8_Config) { + drawAlphaBitmap(texture, left, top, paint); + } else { + drawTextureRect(left, top, right, bottom, texture, paint); + } } void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) { @@ -1209,10 +1383,10 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, const float width = texture->width; const float height = texture->height; - const float u1 = srcLeft / width; - const float v1 = srcTop / height; - const float u2 = srcRight / width; - const float v2 = srcBottom / height; + const float u1 = (srcLeft + 0.5f) / width; + const float v1 = (srcTop + 0.5f) / height; + const float u2 = (srcRight - 0.5f) / width; + const float v2 = (srcBottom - 0.5f) / height; mCaches.unbindMeshBuffer(); resetDrawTextureTexCoords(u1, v1, u2, v2); @@ -1297,117 +1471,375 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int } } +/** + * This function uses a similar approach to that of AA lines in the drawLines() function. + * We expand the rectangle by a half pixel in screen space on all sides, and use a fragment + * shader to compute the translucency of the color, determined by whether a given pixel is + * within that boundary region and how far into the region it is. + */ +void OpenGLRenderer::drawAARect(float left, float top, float right, float bottom, + int color, SkXfermode::Mode mode) { + float inverseScaleX = 1.0f; + float inverseScaleY = 1.0f; + // The quad that we use needs to account for scaling. + if (!mSnapshot->transform->isPureTranslate()) { + Matrix4 *mat = mSnapshot->transform; + float m00 = mat->data[Matrix4::kScaleX]; + float m01 = mat->data[Matrix4::kSkewY]; + float m02 = mat->data[2]; + float m10 = mat->data[Matrix4::kSkewX]; + float m11 = mat->data[Matrix4::kScaleX]; + float m12 = mat->data[6]; + float scaleX = sqrt(m00 * m00 + m01 * m01); + float scaleY = sqrt(m10 * m10 + m11 * m11); + inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0; + inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0; + } + + setupDraw(); + setupDrawAALine(); + setupDrawColor(color); + setupDrawColorFilter(); + setupDrawShader(); + setupDrawBlending(true, mode); + setupDrawProgram(); + setupDrawModelViewIdentity(true); + setupDrawColorUniforms(); + setupDrawColorFilterUniforms(); + setupDrawShaderIdentityUniforms(); + + AAVertex rects[4]; + AAVertex* aaVertices = &rects[0]; + void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset; + void* lengthCoords = ((GLbyte*) aaVertices) + gVertexAALengthOffset; + + float boundarySizeX = .5 * inverseScaleX; + float boundarySizeY = .5 * inverseScaleY; + + // Adjust the rect by the AA boundary padding + left -= boundarySizeX; + right += boundarySizeX; + top -= boundarySizeY; + bottom += boundarySizeY; + + float width = right - left; + float height = bottom - top; + + float boundaryWidthProportion = (width != 0) ? (2 * boundarySizeX) / width : 0; + float boundaryHeightProportion = (height != 0) ? (2 * boundarySizeY) / height : 0; + setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords, boundaryWidthProportion); + int boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength"); + int inverseBoundaryLengthSlot = mCaches.currentProgram->getUniform("inverseBoundaryLength"); + glUniform1f(boundaryLengthSlot, boundaryHeightProportion); + glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryHeightProportion)); + + if (!quickReject(left, top, right, bottom)) { + AAVertex::set(aaVertices++, left, bottom, 1, 1); + AAVertex::set(aaVertices++, left, top, 1, 0); + AAVertex::set(aaVertices++, right, bottom, 0, 1); + AAVertex::set(aaVertices++, right, top, 0, 0); + dirtyLayer(left, top, right, bottom, *mSnapshot->transform); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } +} + +/** + * We draw lines as quads (tristrips). Using GL_LINES can be difficult because the rasterization + * rules for those lines produces some unexpected results, and may vary between hardware devices. + * The basics of lines-as-quads is easy; we simply find the normal to the line and position the + * corners of the quads on either side of each line endpoint, separated by the strokeWidth + * of the line. Hairlines are more involved because we need to account for transform scaling + * to end up with a one-pixel-wide line in screen space.. + * Anti-aliased lines add another factor to the approach. We use a specialized fragment shader + * in combination with values that we calculate and pass down in this method. The basic approach + * is that the quad we create contains both the core line area plus a bounding area in which + * the translucent/AA pixels are drawn. The values we calculate tell the shader what + * proportion of the width and the length of a given segment is represented by the boundary + * region. The quad ends up being exactly .5 pixel larger in all directions than the non-AA quad. + * The bounding region is actually 1 pixel wide on all sides (half pixel on the outside, half pixel + * on the inside). This ends up giving the result we want, with pixels that are completely + * 'inside' the line area being filled opaquely and the other pixels being filled according to + * how far into the boundary region they are, which is determined by shader interpolation. + */ void OpenGLRenderer::drawLines(float* points, int count, SkPaint* paint) { if (mSnapshot->isIgnored()) return; const bool isAA = paint->isAntiAlias(); - const float strokeWidth = paint->getStrokeWidth() * 0.5f; - // A stroke width of 0 has a special meaningin Skia: - // it draws an unscaled 1px wide line - const bool isHairLine = paint->getStrokeWidth() == 0.0f; - + // We use half the stroke width here because we're going to position the quad + // corner vertices half of the width away from the line endpoints + float halfStrokeWidth = paint->getStrokeWidth() * 0.5f; + // A stroke width of 0 has a special meaning in Skia: + // it draws a line 1 px wide regardless of current transform + bool isHairLine = paint->getStrokeWidth() == 0.0f; + float inverseScaleX = 1.0f; + float inverseScaleY = 1.0f; + bool scaled = false; int alpha; SkXfermode::Mode mode; - getAlphaAndMode(paint, &alpha, &mode); - - int verticesCount = count >> 2; int generatedVerticesCount = 0; - if (!isHairLine) { - // TODO: AA needs more vertices - verticesCount *= 6; - } else { - // TODO: AA will be different - verticesCount *= 2; + int verticesCount = count; + if (count > 4) { + // Polyline: account for extra vertices needed for continuous tri-strip + verticesCount += (count - 4); + } + + if (isHairLine || isAA) { + // The quad that we use for AA and hairlines needs to account for scaling. For hairlines + // the line on the screen should always be one pixel wide regardless of scale. For + // AA lines, we only want one pixel of translucent boundary around the quad. + if (!mSnapshot->transform->isPureTranslate()) { + Matrix4 *mat = mSnapshot->transform; + float m00 = mat->data[Matrix4::kScaleX]; + float m01 = mat->data[Matrix4::kSkewY]; + float m02 = mat->data[2]; + float m10 = mat->data[Matrix4::kSkewX]; + float m11 = mat->data[Matrix4::kScaleX]; + float m12 = mat->data[6]; + float scaleX = sqrt(m00*m00 + m01*m01); + float scaleY = sqrt(m10*m10 + m11*m11); + inverseScaleX = (scaleX != 0) ? (inverseScaleX / scaleX) : 0; + inverseScaleY = (scaleY != 0) ? (inverseScaleY / scaleY) : 0; + if (inverseScaleX != 1.0f || inverseScaleY != 1.0f) { + scaled = true; + } + } } - TextureVertex lines[verticesCount]; - TextureVertex* vertex = &lines[0]; - + getAlphaAndMode(paint, &alpha, &mode); setupDraw(); + if (isAA) { + setupDrawAALine(); + } setupDrawColor(paint->getColor(), alpha); setupDrawColorFilter(); setupDrawShader(); - setupDrawBlending(mode); + if (isAA) { + setupDrawBlending(true, mode); + } else { + setupDrawBlending(mode); + } setupDrawProgram(); - setupDrawModelViewIdentity(); + setupDrawModelViewIdentity(true); setupDrawColorUniforms(); setupDrawColorFilterUniforms(); setupDrawShaderIdentityUniforms(); - setupDrawMesh(vertex); - if (!isHairLine) { - // TODO: Handle the AA case - for (int i = 0; i < count; i += 4) { - // a = start point, b = end point - vec2 a(points[i], points[i + 1]); - vec2 b(points[i + 2], points[i + 3]); - - // Bias to snap to the same pixels as Skia - a += 0.375; - b += 0.375; - - // Find the normal to the line - vec2 n = (b - a).copyNormalized() * strokeWidth; - float x = n.x; - n.x = -n.y; - n.y = x; - - // Four corners of the rectangle defining a thick line - vec2 p1 = a - n; - vec2 p2 = a + n; - vec2 p3 = b + n; - vec2 p4 = b - n; - - const float left = fmin(p1.x, fmin(p2.x, fmin(p3.x, p4.x))); - const float right = fmax(p1.x, fmax(p2.x, fmax(p3.x, p4.x))); - const float top = fmin(p1.y, fmin(p2.y, fmin(p3.y, p4.y))); - const float bottom = fmax(p1.y, fmax(p2.y, fmax(p3.y, p4.y))); - - if (!quickReject(left, top, right, bottom)) { - // Draw the line as 2 triangles, could be optimized - // by using only 4 vertices and the correct indices - // Also we should probably used non textured vertices - // when line AA is disabled to save on bandwidth - TextureVertex::set(vertex++, p1.x, p1.y, 0.0f, 0.0f); - TextureVertex::set(vertex++, p2.x, p2.y, 0.0f, 0.0f); - TextureVertex::set(vertex++, p3.x, p3.y, 0.0f, 0.0f); - TextureVertex::set(vertex++, p1.x, p1.y, 0.0f, 0.0f); - TextureVertex::set(vertex++, p3.x, p3.y, 0.0f, 0.0f); - TextureVertex::set(vertex++, p4.x, p4.y, 0.0f, 0.0f); - - generatedVerticesCount += 6; - - dirtyLayer(left, top, right, bottom, *mSnapshot->transform); + if (isHairLine) { + // Set a real stroke width to be used in quad construction + halfStrokeWidth = isAA? 1 : .5; + } else if (isAA && !scaled) { + // Expand boundary to enable AA calculations on the quad border + halfStrokeWidth += .5f; + } + Vertex lines[verticesCount]; + Vertex* vertices = &lines[0]; + AAVertex wLines[verticesCount]; + AAVertex* aaVertices = &wLines[0]; + if (!isAA) { + setupDrawVertices(vertices); + } else { + void* widthCoords = ((GLbyte*) aaVertices) + gVertexAAWidthOffset; + void* lengthCoords = ((GLbyte*) aaVertices) + gVertexAALengthOffset; + // innerProportion is the ratio of the inner (non-AA) part of the line to the total + // AA stroke width (the base stroke width expanded by a half pixel on either side). + // This value is used in the fragment shader to determine how to fill fragments. + // We will need to calculate the actual width proportion on each segment for + // scaled non-hairlines, since the boundary proportion may differ per-axis when scaled. + float boundaryWidthProportion = 1 / (2 * halfStrokeWidth); + setupDrawAALine((void*) aaVertices, widthCoords, lengthCoords, boundaryWidthProportion); + } + + AAVertex* prevAAVertex = NULL; + Vertex* prevVertex = NULL; + + int boundaryLengthSlot = -1; + int inverseBoundaryLengthSlot = -1; + int boundaryWidthSlot = -1; + int inverseBoundaryWidthSlot = -1; + for (int i = 0; i < count; i += 4) { + // a = start point, b = end point + vec2 a(points[i], points[i + 1]); + vec2 b(points[i + 2], points[i + 3]); + float length = 0; + float boundaryLengthProportion = 0; + float boundaryWidthProportion = 0; + + // Find the normal to the line + vec2 n = (b - a).copyNormalized() * halfStrokeWidth; + if (isHairLine) { + if (isAA) { + float wideningFactor; + if (fabs(n.x) >= fabs(n.y)) { + wideningFactor = fabs(1.0f / n.x); + } else { + wideningFactor = fabs(1.0f / n.y); + } + n *= wideningFactor; + } + if (scaled) { + n.x *= inverseScaleX; + n.y *= inverseScaleY; + } + } else if (scaled) { + // Extend n by .5 pixel on each side, post-transform + vec2 extendedN = n.copyNormalized(); + extendedN /= 2; + extendedN.x *= inverseScaleX; + extendedN.y *= inverseScaleY; + float extendedNLength = extendedN.length(); + // We need to set this value on the shader prior to drawing + boundaryWidthProportion = extendedNLength / (halfStrokeWidth + extendedNLength); + n += extendedN; + } + float x = n.x; + n.x = -n.y; + n.y = x; + + // aa lines expand the endpoint vertices to encompass the AA boundary + if (isAA) { + vec2 abVector = (b - a); + length = abVector.length(); + abVector.normalize(); + if (scaled) { + abVector.x *= inverseScaleX; + abVector.y *= inverseScaleY; + float abLength = abVector.length(); + boundaryLengthProportion = abLength / (length + abLength); + } else { + boundaryLengthProportion = .5 / (length + 1); } + abVector /= 2; + a -= abVector; + b += abVector; } - if (generatedVerticesCount > 0) { - // GL_LINE does not give the result we want to match Skia - glDrawArrays(GL_TRIANGLES, 0, generatedVerticesCount); + // Four corners of the rectangle defining a thick line + vec2 p1 = a - n; + vec2 p2 = a + n; + vec2 p3 = b + n; + vec2 p4 = b - n; + + + const float left = fmin(p1.x, fmin(p2.x, fmin(p3.x, p4.x))); + const float right = fmax(p1.x, fmax(p2.x, fmax(p3.x, p4.x))); + const float top = fmin(p1.y, fmin(p2.y, fmin(p3.y, p4.y))); + const float bottom = fmax(p1.y, fmax(p2.y, fmax(p3.y, p4.y))); + + if (!quickReject(left, top, right, bottom)) { + if (!isAA) { + if (prevVertex != NULL) { + // Issue two repeat vertices to create degenerate triangles to bridge + // between the previous line and the new one. This is necessary because + // we are creating a single triangle_strip which will contain + // potentially discontinuous line segments. + Vertex::set(vertices++, prevVertex->position[0], prevVertex->position[1]); + Vertex::set(vertices++, p1.x, p1.y); + generatedVerticesCount += 2; + } + Vertex::set(vertices++, p1.x, p1.y); + Vertex::set(vertices++, p2.x, p2.y); + Vertex::set(vertices++, p4.x, p4.y); + Vertex::set(vertices++, p3.x, p3.y); + prevVertex = vertices - 1; + generatedVerticesCount += 4; + } else { + if (!isHairLine && scaled) { + // Must set width proportions per-segment for scaled non-hairlines to use the + // correct AA boundary dimensions + if (boundaryWidthSlot < 0) { + boundaryWidthSlot = + mCaches.currentProgram->getUniform("boundaryWidth"); + inverseBoundaryWidthSlot = + mCaches.currentProgram->getUniform("inverseBoundaryWidth"); + } + glUniform1f(boundaryWidthSlot, boundaryWidthProportion); + glUniform1f(inverseBoundaryWidthSlot, (1 / boundaryWidthProportion)); + } + if (boundaryLengthSlot < 0) { + boundaryLengthSlot = mCaches.currentProgram->getUniform("boundaryLength"); + inverseBoundaryLengthSlot = + mCaches.currentProgram->getUniform("inverseBoundaryLength"); + } + glUniform1f(boundaryLengthSlot, boundaryLengthProportion); + glUniform1f(inverseBoundaryLengthSlot, (1 / boundaryLengthProportion)); + + if (prevAAVertex != NULL) { + // Issue two repeat vertices to create degenerate triangles to bridge + // between the previous line and the new one. This is necessary because + // we are creating a single triangle_strip which will contain + // potentially discontinuous line segments. + AAVertex::set(aaVertices++,prevAAVertex->position[0], + prevAAVertex->position[1], prevAAVertex->width, prevAAVertex->length); + AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1); + generatedVerticesCount += 2; + } + AAVertex::set(aaVertices++, p4.x, p4.y, 1, 1); + AAVertex::set(aaVertices++, p1.x, p1.y, 1, 0); + AAVertex::set(aaVertices++, p3.x, p3.y, 0, 1); + AAVertex::set(aaVertices++, p2.x, p2.y, 0, 0); + prevAAVertex = aaVertices - 1; + generatedVerticesCount += 4; + } + dirtyLayer(a.x == b.x ? left - 1 : left, a.y == b.y ? top - 1 : top, + a.x == b.x ? right: right, a.y == b.y ? bottom: bottom, + *mSnapshot->transform); } - } else { - // TODO: Handle the AA case - for (int i = 0; i < count; i += 4) { - const float left = fmin(points[i], points[i + 1]); - const float right = fmax(points[i], points[i + 1]); - const float top = fmin(points[i + 2], points[i + 3]); - const float bottom = fmax(points[i + 2], points[i + 3]); + } + if (generatedVerticesCount > 0) { + glDrawArrays(GL_TRIANGLE_STRIP, 0, generatedVerticesCount); + } +} + +void OpenGLRenderer::drawPoints(float* points, int count, SkPaint* paint) { + if (mSnapshot->isIgnored()) return; - if (!quickReject(left, top, right, bottom)) { - TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f); - TextureVertex::set(vertex++, points[i + 2], points[i + 3], 0.0f, 0.0f); + // TODO: The paint's cap style defines whether the points are square or circular + // TODO: Handle AA for round points - generatedVerticesCount += 2; + // 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); - dirtyLayer(left, top, right, bottom, *mSnapshot->transform); - } - } + int verticesCount = count >> 1; + int generatedVerticesCount = 0; - if (generatedVerticesCount > 0) { - glLineWidth(1.0f); - glDrawArrays(GL_LINES, 0, generatedVerticesCount); - } + TextureVertex pointsData[verticesCount]; + TextureVertex* vertex = &pointsData[0]; + + setupDraw(); + setupDrawPoint(strokeWidth); + setupDrawColor(paint->getColor(), alpha); + setupDrawColorFilter(); + setupDrawShader(); + setupDrawBlending(mode); + setupDrawProgram(); + setupDrawModelViewIdentity(true); + setupDrawColorUniforms(); + setupDrawColorFilterUniforms(); + setupDrawPointUniforms(); + setupDrawShaderIdentityUniforms(); + setupDrawMesh(vertex); + + for (int i = 0; i < count; i += 2) { + TextureVertex::set(vertex++, points[i], points[i + 1], 0.0f, 0.0f); + generatedVerticesCount++; + float left = points[i] - halfWidth; + float right = points[i] + halfWidth; + float top = points[i + 1] - halfWidth; + float bottom = points [i + 1] + halfWidth; + dirtyLayer(left, top, right, bottom, *mSnapshot->transform); } + + glDrawArrays(GL_POINTS, 0, generatedVerticesCount); } void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { @@ -1502,7 +1934,11 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, } int color = p->getColor(); - drawColorRect(left, top, right, bottom, color, mode); + if (p->isAntiAlias() && !mSnapshot->transform->isSimple()) { + drawAARect(left, top, right, bottom, color, mode); + } else { + drawColorRect(left, top, right, bottom, color, mode); + } } void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, @@ -1512,7 +1948,16 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, } if (mSnapshot->isIgnored()) return; + // TODO: We should probably make a copy of the paint instead of modifying + // it; modifying the paint will change its generationID the first + // time, which might impact caches. More investigation needed to + // see if it matters. + // If we make a copy, then drawTextDecorations() should *not* make + // its own copy as it does right now. paint->setAntiAlias(true); +#if RENDER_TEXT_AS_GLYPHS + paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding); +#endif float length = -1.0f; switch (paint->getTextAlign()) { @@ -1546,27 +1991,36 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, if (mHasShadow) { mCaches.dropShadowCache.setFontRenderer(fontRenderer); - const ShadowTexture* shadow = mCaches.dropShadowCache.get(paint, text, bytesCount, - count, mShadowRadius); + const ShadowTexture* shadow = mCaches.dropShadowCache.get( + paint, text, bytesCount, count, mShadowRadius); const AutoTexture autoCleanup(shadow); - const float sx = x - shadow->left + mShadowDx; - const float sy = y - shadow->top + mShadowDy; + const float sx = oldX - shadow->left + mShadowDx; + const float sy = oldY - shadow->top + mShadowDy; const int shadowAlpha = ((mShadowColor >> 24) & 0xFF); + int shadowColor = mShadowColor; + if (mShader) { + shadowColor = 0xffffffff; + } glActiveTexture(gTextureUnits[0]); setupDraw(); setupDrawWithTexture(true); - setupDrawAlpha8Color(mShadowColor, shadowAlpha < 255 ? shadowAlpha : alpha); + setupDrawAlpha8Color(shadowColor, shadowAlpha < 255 ? shadowAlpha : alpha); + setupDrawColorFilter(); + setupDrawShader(); setupDrawBlending(true, mode); setupDrawProgram(); - setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height, pureTranslate); + setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height); setupDrawTexture(shadow->id); setupDrawPureColorUniforms(); + setupDrawColorFilterUniforms(); + setupDrawShaderUniforms(); setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset); glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); + finishDrawTexture(); } @@ -1658,14 +2112,9 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) { #if RENDER_LAYERS_AS_REGIONS if (!layer->region.isEmpty()) { -#if RENDER_LAYERS_RECT_AS_RECT if (layer->region.isRect()) { - const Rect r(x, y, x + layer->layer.getWidth(), y + layer->layer.getHeight()); - composeLayerRect(layer, r); + composeLayerRect(layer, layer->regionRect); } else if (layer->mesh) { -#else - if (layer->mesh) { -#endif const float a = alpha / 255.0f; const Rect& rect = layer->layer; @@ -1675,13 +2124,11 @@ void OpenGLRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* paint) { setupDrawColorFilter(); setupDrawBlending(layer->blend || layer->alpha < 255, layer->mode, false); setupDrawProgram(); + setupDrawModelViewTranslate(x, y, + x + layer->layer.getWidth(), y + layer->layer.getHeight()); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); setupDrawTexture(layer->texture); - // TODO: The current layer, if any, will be dirtied with the bounding box - // of the layer we are drawing. Since the layer we are drawing has - // a mesh, we know the dirty region, we should use it instead - setupDrawModelViewTranslate(rect.left, rect.top, rect.right, rect.bottom); setupDrawMesh(&layer->mesh[0].position[0], &layer->mesh[0].texture[0]); glDrawElements(GL_TRIANGLES, layer->meshElementCount, @@ -1786,14 +2233,15 @@ void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float // 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 = paint->measureText(text, bytesCount); + underlineWidth = paintCopy.measureText(text, bytesCount); } float offsetX = 0; - switch (paint->getTextAlign()) { + switch (paintCopy.getTextAlign()) { case SkPaint::kCenter_Align: offsetX = underlineWidth * 0.5f; break; @@ -1805,8 +2253,7 @@ void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float } if (underlineWidth > 0.0f) { - const float textSize = paint->getTextSize(); - // TODO: Support stroke width < 1.0f when we have AA lines + const float textSize = paintCopy.getTextSize(); const float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); const float left = x - offsetX; @@ -1836,10 +2283,9 @@ void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float points[currentPoint++] = top; } - SkPaint linesPaint(*paint); - linesPaint.setStrokeWidth(strokeWidth); + paintCopy.setStrokeWidth(strokeWidth); - drawLines(&points[0], pointsCount, &linesPaint); + drawLines(&points[0], pointsCount, &paintCopy); } } } @@ -2004,12 +2450,11 @@ void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mod } SkXfermode::Mode OpenGLRenderer::getXfermode(SkXfermode* mode) { - // In the future we should look at unifying the Porter-Duff modes and - // SkXferModes so that we can use SkXfermode::IsMode(xfer, &mode). - if (mode == NULL) { - return SkXfermode::kSrcOver_Mode; + SkXfermode::Mode resultMode; + if (!SkXfermode::AsMode(mode, &resultMode)) { + resultMode = SkXfermode::kSrcOver_Mode; } - return mode->fMode; + return resultMode; } void OpenGLRenderer::setTextureWrapModes(Texture* texture, GLenum wrapS, GLenum wrapT) { diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 7362473..549d6e9 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -98,6 +98,7 @@ public: virtual bool drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height, Rect& dirty, uint32_t level = 0); + virtual void outputDisplayList(DisplayList* displayList, uint32_t level = 0); virtual void drawLayer(Layer* layer, float x, float y, SkPaint* paint); virtual void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint); virtual void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint); @@ -119,6 +120,7 @@ public: float startAngle, float sweepAngle, bool useCenter, SkPaint* paint); virtual void drawPath(SkPath* path, SkPaint* paint); virtual void drawLines(float* points, int count, SkPaint* paint); + virtual void drawPoints(float* points, int count, SkPaint* paint); virtual void drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint); @@ -247,6 +249,8 @@ private: */ void composeLayerRect(Layer* layer, const Rect& rect, bool swap = false); + void drawTextureLayer(Layer* layer, const Rect& rect); + /** * Mark the layer as dirty at the specified coordinates. The coordinates * are transformed with the supplied matrix. @@ -279,6 +283,11 @@ private: void drawShape(float left, float top, const PathTexture* texture, SkPaint* paint); void drawRectAsShape(float left, float top, float right, float bottom, SkPaint* p); + void drawAlphaBitmap(Texture* texture, float left, float top, SkPaint* paint); + + void drawAARect(float left, float top, float right, float bottom, + int color, SkXfermode::Mode mode); + /** * Draws a textured rectangle with the specified texture. The specified coordinates * are transformed by the current snapshot's transform matrix. @@ -384,6 +393,14 @@ private: } /** + * Binds the specified EGLImage texture. The texture unit must have been selected + * prior to calling this method. + */ + inline void bindExternalTexture(GLuint texture) { + glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture); + } + + /** * Sets the wrap modes for the specified texture. The wrap modes are modified * only when needed. */ @@ -422,6 +439,9 @@ private: * Various methods to setup OpenGL rendering. */ void setupDrawWithTexture(bool isAlpha8 = false); + void setupDrawWithExternalTexture(); + void setupDrawAALine(); + void setupDrawPoint(float pointSize); void setupDrawColor(int color); void setupDrawColor(int color, int alpha); void setupDrawColor(float r, float g, float b, float a); @@ -435,11 +455,12 @@ private: bool swapSrcDst = false); void setupDrawProgram(); void setupDrawDirtyRegionsDisabled(); - void setupDrawModelViewIdentity(); + void setupDrawModelViewIdentity(bool offset = false); void setupDrawModelView(float left, float top, float right, float bottom, 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(); @@ -447,8 +468,15 @@ private: void setupDrawColorFilterUniforms(); void setupDrawSimpleMesh(); void setupDrawTexture(GLuint texture); + void setupDrawExternalTexture(GLuint texture); + void setupDrawTextureTransform(); + void setupDrawTextureTransformUniforms(mat4& transform); void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0); + void setupDrawVertices(GLvoid* vertices); + void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, GLvoid* lengthCoords, + float strokeWidth); void finishDrawTexture(); + void accountForClear(SkXfermode::Mode mode); void drawRegionRects(const Region& region); diff --git a/libs/hwui/Patch.cpp b/libs/hwui/Patch.cpp index 11eb953..f7dacae 100644 --- a/libs/hwui/Patch.cpp +++ b/libs/hwui/Patch.cpp @@ -152,12 +152,12 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, float previousStepY = 0.0f; float y1 = 0.0f; + float y2 = 0.0f; float v1 = 0.0f; for (uint32_t i = 0; i < mYCount; i++) { float stepY = mYDivs[i]; - float y2 = 0.0f; if (i & 1) { const float segment = stepY - previousStepY; y2 = y1 + floorf(segment * stretchY + 0.5f); @@ -167,8 +167,15 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, float v2 = fmax(0.0f, stepY - 0.5f) / 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, right - left, bitmapWidth, quadCount); +#if DEBUG_EXPLODE_PATCHES + y2 -= i * EXPLODE_GAP; +#endif } y1 = y2; @@ -178,7 +185,12 @@ void Patch::updateVertices(const float bitmapWidth, const float bitmapHeight, } if (previousStepY != bitmapHeight) { - generateRow(vertex, y1, bottom - top, v1, 1.0f, stretchX, right - left, + y2 = bottom - top; +#if DEBUG_EXPLODE_PATCHES + y1 += mYCount * EXPLODE_GAP; + y2 += mYCount * EXPLODE_GAP; +#endif + generateRow(vertex, y1, y2, v1, 1.0f, stretchX, right - left, bitmapWidth, quadCount); } @@ -202,13 +214,13 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl float previousStepX = 0.0f; float x1 = 0.0f; + float x2 = 0.0f; float u1 = 0.0f; // Generate the row quad by quad for (uint32_t i = 0; i < mXCount; i++) { float stepX = mXDivs[i]; - float x2 = 0.0f; if (i & 1) { const float segment = stepX - previousStepX; x2 = x1 + floorf(segment * stretchX + 0.5f); @@ -218,7 +230,14 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl float u2 = fmax(0.0f, stepX - 0.5f) / 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; @@ -228,7 +247,12 @@ void Patch::generateRow(TextureVertex*& vertex, float y1, float y2, float v1, fl } if (previousStepX != bitmapWidth) { - generateQuad(vertex, x1, y1, width, y2, u1, v1, 1.0f, v2, quadCount); + 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); } } diff --git a/libs/hwui/Patch.h b/libs/hwui/Patch.h index 0f0ffa2..28c9048 100644 --- a/libs/hwui/Patch.h +++ b/libs/hwui/Patch.h @@ -31,6 +31,12 @@ namespace android { namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +#define EXPLODE_GAP 4 + +/////////////////////////////////////////////////////////////////////////////// // 9-patch structures /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index dc67e16..7ff8b74 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -41,8 +41,7 @@ struct PathCacheEntry: public ShapeCacheEntry { path = NULL; } - PathCacheEntry(const PathCacheEntry& entry): - ShapeCacheEntry(entry) { + PathCacheEntry(const PathCacheEntry& entry): ShapeCacheEntry(entry) { path = entry.path; } @@ -55,6 +54,7 @@ struct PathCacheEntry: public ShapeCacheEntry { } SkPath* path; + }; // PathCacheEntry /** diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp index 2187f24..972dd87 100644 --- a/libs/hwui/Program.cpp +++ b/libs/hwui/Program.cpp @@ -124,8 +124,15 @@ GLuint Program::buildShader(const char* source, GLenum type) { } void Program::set(const mat4& projectionMatrix, const mat4& modelViewMatrix, - const mat4& transformMatrix) { + const mat4& transformMatrix, bool offset) { mat4 t(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. + t.translate(.375, .375, 0); + } t.multiply(transformMatrix); t.multiply(modelViewMatrix); diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index afc6f3d..764cb05 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -81,7 +81,7 @@ public: * transform matrices. */ void set(const mat4& projectionMatrix, const mat4& modelViewMatrix, - const mat4& transformMatrix); + const mat4& transformMatrix, bool offset = false); /** * Sets the color associated with this shader. diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 0b6c7b5..d419e3e 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -39,8 +39,15 @@ const char* gVS_Header_Attributes = "attribute vec4 position;\n"; const char* gVS_Header_Attributes_TexCoords = "attribute vec2 texCoords;\n"; +const char* gVS_Header_Attributes_AAParameters = + "attribute float vtxWidth;\n" + "attribute float vtxLength;\n"; +const char* gVS_Header_Uniforms_TextureTransform = + "uniform mat4 mainTextureTransform;\n"; const char* gVS_Header_Uniforms = "uniform mat4 transform;\n"; +const char* gVS_Header_Uniforms_IsPoint = + "uniform mediump float pointSize;\n"; const char* gVS_Header_Uniforms_HasGradient[3] = { // Linear "uniform mat4 screenSpace;\n", @@ -51,11 +58,16 @@ const char* gVS_Header_Uniforms_HasGradient[3] = { }; const char* gVS_Header_Uniforms_HasBitmap = "uniform mat4 textureTransform;\n" - "uniform vec2 textureDimension;\n"; + "uniform mediump vec2 textureDimension;\n"; const char* gVS_Header_Varyings_HasTexture = "varying vec2 outTexCoords;\n"; +const char* gVS_Header_Varyings_IsAA = + "varying float widthProportion;\n" + "varying float lengthProportion;\n"; const char* gVS_Header_Varyings_HasBitmap = "varying vec2 outBitmapTexCoords;\n"; +const char* gVS_Header_Varyings_PointHasBitmap = + "varying vec2 outPointBitmapTexCoords;\n"; const char* gVS_Header_Varyings_HasGradient[3] = { // Linear "varying vec2 linear;\n", @@ -68,6 +80,8 @@ const char* gVS_Main = "\nvoid main(void) {\n"; const char* gVS_Main_OutTexCoords = " outTexCoords = texCoords;\n"; +const char* gVS_Main_OutTransformedTexCoords = + " outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n"; const char* gVS_Main_OutGradient[3] = { // Linear " linear = vec2((screenSpace * position).x, 0.5);\n", @@ -78,8 +92,15 @@ const char* gVS_Main_OutGradient[3] = { }; 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 = transform * position;\n"; +const char* gVS_Main_PointSize = + " gl_PointSize = pointSize;\n"; +const char* gVS_Main_AA = + " widthProportion = vtxWidth;\n" + " lengthProportion = vtxLength;\n"; const char* gVS_Footer = "}\n\n"; @@ -89,12 +110,24 @@ const char* gVS_Footer = const char* gFS_Header_Extension_FramebufferFetch = "#extension GL_NV_shader_framebuffer_fetch : enable\n\n"; +const char* gFS_Header_Extension_ExternalTexture = + "#extension GL_OES_EGL_image_external : require\n\n"; const char* gFS_Header = "precision mediump float;\n\n"; const char* gFS_Uniforms_Color = "uniform vec4 color;\n"; +const char* gFS_Uniforms_AA = + "uniform float boundaryWidth;\n" + "uniform float inverseBoundaryWidth;\n" + "uniform float boundaryLength;\n" + "uniform float inverseBoundaryLength;\n"; +const char* gFS_Header_Uniforms_PointHasBitmap = + "uniform vec2 textureDimension;\n" + "uniform float pointSize;\n"; const char* gFS_Uniforms_TextureSampler = "uniform sampler2D sampler;\n"; +const char* gFS_Uniforms_ExternalTextureSampler = + "uniform samplerExternalOES sampler;\n"; const char* gFS_Uniforms_GradientSampler[3] = { // Linear "uniform sampler2D gradientSampler;\n", @@ -121,6 +154,10 @@ const char* gFS_Main = "\nvoid main(void) {\n" " lowp vec4 fragColor;\n"; +const char* gFS_Main_PointBitmapTexCoords = + " vec2 outBitmapTexCoords = outPointBitmapTexCoords + " + "((gl_PointCoord - vec2(0.5, 0.5)) * textureDimension * vec2(pointSize, pointSize));\n"; + // Fast cases const char* gFS_Fast_SingleColor = "\nvoid main(void) {\n" @@ -154,6 +191,19 @@ const char* gFS_Fast_SingleModulateGradient = // General case const char* gFS_Main_FetchColor = " fragColor = color;\n"; +const char* gFS_Main_ModulateColor = + " fragColor *= color.a;\n"; +const char* gFS_Main_AccountForAA = + " if (widthProportion < boundaryWidth) {\n" + " fragColor *= (widthProportion * inverseBoundaryWidth);\n" + " } else if (widthProportion > (1.0 - boundaryWidth)) {\n" + " fragColor *= ((1.0 - widthProportion) * inverseBoundaryWidth);\n" + " }\n" + " if (lengthProportion < boundaryLength) {\n" + " fragColor *= (lengthProportion * inverseBoundaryLength);\n" + " } else if (lengthProportion > (1.0 - boundaryLength)) {\n" + " fragColor *= ((1.0 - lengthProportion) * inverseBoundaryLength);\n" + " }\n"; const char* gFS_Main_FetchTexture[2] = { // Don't modulate " fragColor = texture2D(sampler, outTexCoords);\n", @@ -336,38 +386,62 @@ Program* ProgramCache::generateProgram(const ProgramDescription& description, pr String8 ProgramCache::generateVertexShader(const ProgramDescription& description) { // Add attributes String8 shader(gVS_Header_Attributes); - if (description.hasTexture) { + if (description.hasTexture || description.hasExternalTexture) { shader.append(gVS_Header_Attributes_TexCoords); } + if (description.isAA) { + shader.append(gVS_Header_Attributes_AAParameters); + } // Uniforms shader.append(gVS_Header_Uniforms); + if (description.hasTextureTransform) { + shader.append(gVS_Header_Uniforms_TextureTransform); + } if (description.hasGradient) { shader.append(gVS_Header_Uniforms_HasGradient[description.gradientType]); } if (description.hasBitmap) { shader.append(gVS_Header_Uniforms_HasBitmap); } + if (description.isPoint) { + shader.append(gVS_Header_Uniforms_IsPoint); + } // Varyings - if (description.hasTexture) { + if (description.hasTexture || description.hasExternalTexture) { shader.append(gVS_Header_Varyings_HasTexture); } + if (description.isAA) { + shader.append(gVS_Header_Varyings_IsAA); + } if (description.hasGradient) { shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]); } if (description.hasBitmap) { - shader.append(gVS_Header_Varyings_HasBitmap); + shader.append(description.isPoint ? + gVS_Header_Varyings_PointHasBitmap : + gVS_Header_Varyings_HasBitmap); } // Begin the shader shader.append(gVS_Main); { - if (description.hasTexture) { + if (description.hasTextureTransform) { + shader.append(gVS_Main_OutTransformedTexCoords); + } else if (description.hasTexture || description.hasExternalTexture) { shader.append(gVS_Main_OutTexCoords); } + if (description.isAA) { + shader.append(gVS_Main_AA); + } if (description.hasGradient) { shader.append(gVS_Main_OutGradient[description.gradientType]); } if (description.hasBitmap) { - shader.append(gVS_Main_OutBitmapTexCoords); + shader.append(description.isPoint ? + gVS_Main_OutPointBitmapTexCoords : + gVS_Main_OutBitmapTexCoords); + } + if (description.isPoint) { + shader.append(gVS_Main_PointSize); } // Output transformed position shader.append(gVS_Main_Position); @@ -388,23 +462,31 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (blendFramebuffer) { shader.append(gFS_Header_Extension_FramebufferFetch); } + if (description.hasExternalTexture) { + shader.append(gFS_Header_Extension_ExternalTexture); + } shader.append(gFS_Header); // Varyings - if (description.hasTexture) { + if (description.hasTexture || description.hasExternalTexture) { shader.append(gVS_Header_Varyings_HasTexture); } + if (description.isAA) { + shader.append(gVS_Header_Varyings_IsAA); + } if (description.hasGradient) { shader.append(gVS_Header_Varyings_HasGradient[description.gradientType]); } if (description.hasBitmap) { - shader.append(gVS_Header_Varyings_HasBitmap); + shader.append(description.isPoint ? + gVS_Header_Varyings_PointHasBitmap : + gVS_Header_Varyings_HasBitmap); } // Uniforms int modulateOp = MODULATE_OP_NO_MODULATE; - const bool singleColor = !description.hasTexture && + const bool singleColor = !description.hasTexture && !description.hasExternalTexture && !description.hasGradient && !description.hasBitmap; if (description.modulate || singleColor) { @@ -413,21 +495,30 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti } if (description.hasTexture) { shader.append(gFS_Uniforms_TextureSampler); + } else if (description.hasExternalTexture) { + shader.append(gFS_Uniforms_ExternalTextureSampler); + } + if (description.isAA) { + shader.append(gFS_Uniforms_AA); } if (description.hasGradient) { shader.append(gFS_Uniforms_GradientSampler[description.gradientType]); } + if (description.hasBitmap && description.isPoint) { + shader.append(gFS_Header_Uniforms_PointHasBitmap); + } // Optimization for common cases - if (!blendFramebuffer && description.colorOp == ProgramDescription::kColorNone) { + if (!description.isAA && !blendFramebuffer && + description.colorOp == ProgramDescription::kColorNone && !description.isPoint) { bool fast = false; const bool noShader = !description.hasGradient && !description.hasBitmap; - const bool singleTexture = description.hasTexture && + const bool singleTexture = (description.hasTexture || description.hasExternalTexture) && !description.hasAlpha8Texture && noShader; const bool singleA8Texture = description.hasTexture && description.hasAlpha8Texture && noShader; - const bool singleGradient = !description.hasTexture && + const bool singleGradient = !description.hasTexture && !description.hasExternalTexture && description.hasGradient && !description.hasBitmap && description.gradientType == ProgramDescription::kGradientLinear; @@ -490,7 +581,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti // Begin the shader shader.append(gFS_Main); { // Stores the result in fragColor directly - if (description.hasTexture) { + if (description.hasTexture || description.hasExternalTexture) { if (description.hasAlpha8Texture) { if (!description.hasGradient && !description.hasBitmap) { shader.append(gFS_Main_FetchA8Texture[modulateOp]); @@ -503,16 +594,23 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gFS_Main_FetchColor); } } + if (description.isAA) { + shader.append(gFS_Main_AccountForAA); + } if (description.hasGradient) { shader.append(gFS_Main_FetchGradient[description.gradientType]); } if (description.hasBitmap) { + if (description.isPoint) { + shader.append(gFS_Main_PointBitmapTexCoords); + } if (!description.isBitmapNpot) { shader.append(gFS_Main_FetchBitmap); } else { shader.append(gFS_Main_FetchBitmapNpot); } } + bool applyModulate = false; // Case when we have two shaders set if (description.hasGradient && description.hasBitmap) { int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp; @@ -522,15 +620,21 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gFS_Main_BlendShadersGB); } shader.append(gFS_Main_BlendShaders_Modulate[op]); + applyModulate = true; } else { if (description.hasGradient) { int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp; shader.append(gFS_Main_GradientShader_Modulate[op]); + applyModulate = true; } else if (description.hasBitmap) { int op = description.hasAlpha8Texture ? MODULATE_OP_MODULATE_A8 : modulateOp; shader.append(gFS_Main_BitmapShader_Modulate[op]); + applyModulate = true; } } + if (description.modulate && applyModulate) { + shader.append(gFS_Main_ModulateColor); + } // Apply the color op if needed shader.append(gFS_Main_ApplyColorOp[description.colorOp]); // Output the fragment diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index ead5b92..5c7197b 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -71,7 +71,14 @@ namespace uirenderer { #define PROGRAM_BITMAP_WRAPT_SHIFT 11 #define PROGRAM_GRADIENT_TYPE_SHIFT 33 -#define PROGRAM_MODULATE 35 +#define PROGRAM_MODULATE_SHIFT 35 + +#define PROGRAM_IS_POINT_SHIFT 36 + +#define PROGRAM_HAS_AA_SHIFT 37 + +#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38 +#define PROGRAM_HAS_TEXTURE_TRANSFORM_SHIFT 39 /////////////////////////////////////////////////////////////////////////////// // Types @@ -109,6 +116,8 @@ struct ProgramDescription { // Texturing bool hasTexture; bool hasAlpha8Texture; + bool hasExternalTexture; + bool hasTextureTransform; // Modulate, this should only be set when setColor() return true bool modulate; @@ -117,6 +126,8 @@ struct ProgramDescription { bool hasBitmap; bool isBitmapNpot; + bool isAA; + bool hasGradient; Gradient gradientType; @@ -135,6 +146,9 @@ struct ProgramDescription { SkXfermode::Mode framebufferMode; bool swapSrcDst; + bool isPoint; + float pointSize; + /** * Resets this description. All fields are reset back to the default * values they hold after building a new instance. @@ -142,6 +156,10 @@ struct ProgramDescription { void reset() { hasTexture = false; hasAlpha8Texture = false; + hasExternalTexture = false; + hasTextureTransform = false; + + isAA = false; modulate = false; @@ -162,6 +180,9 @@ struct ProgramDescription { framebufferMode = SkXfermode::kClear_Mode; swapSrcDst = false; + + isPoint = false; + pointSize = 0.0f; } /** @@ -223,7 +244,11 @@ 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; + 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; return key; } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 1aef99b..7c10518 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -27,8 +27,9 @@ // If turned on, layers drawn inside FBOs are optimized with regions #define RENDER_LAYERS_AS_REGIONS 1 -// If turned on, layers that map to a single rect are drawn as a rect -#define RENDER_LAYERS_RECT_AS_RECT 0 + +// If turned on, text is interpreted as glyphs instead of UTF-16 +#define RENDER_TEXT_AS_GLYPHS 1 /** * Debug level for app developers. diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h index 4c626dd..b5cc29c 100644 --- a/libs/hwui/ShapeCache.h +++ b/libs/hwui/ShapeCache.h @@ -89,14 +89,17 @@ struct ShapeCacheEntry { join = SkPaint::kDefault_Join; cap = SkPaint::kDefault_Cap; style = SkPaint::kFill_Style; - miter = 4.0f; - strokeWidth = 1.0f; + float v = 4.0f; + miter = *(uint32_t*) &v; + v = 1.0f; + strokeWidth = *(uint32_t*) &v; + pathEffect = NULL; } ShapeCacheEntry(const ShapeCacheEntry& entry): shapeType(entry.shapeType), join(entry.join), cap(entry.cap), style(entry.style), miter(entry.miter), - strokeWidth(entry.strokeWidth) { + strokeWidth(entry.strokeWidth), pathEffect(entry.pathEffect) { } ShapeCacheEntry(ShapeType type, SkPaint* paint) { @@ -108,18 +111,19 @@ struct ShapeCacheEntry { v = paint->getStrokeWidth(); strokeWidth = *(uint32_t*) &v; style = paint->getStyle(); + pathEffect = paint->getPathEffect(); } virtual ~ShapeCacheEntry() { } - // shapeType must be checked in subclasses operator< ShapeType shapeType; SkPaint::Join join; SkPaint::Cap cap; SkPaint::Style style; uint32_t miter; uint32_t strokeWidth; + SkPathEffect* pathEffect; bool operator<(const ShapeCacheEntry& rhs) const { LTE_INT(shapeType) { @@ -128,7 +132,9 @@ struct ShapeCacheEntry { LTE_INT(style) { LTE_INT(miter) { LTE_INT(strokeWidth) { - return lessThan(rhs); + LTE_INT(pathEffect) { + return lessThan(rhs); + } } } } diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h index d46686d..28dba13 100644 --- a/libs/hwui/TextDropShadowCache.h +++ b/libs/hwui/TextDropShadowCache.h @@ -73,7 +73,6 @@ struct ShadowText { text = str.string(); } - // TODO: Should take into account fake bold and text skew bool operator<(const ShadowText& rhs) const { LTE_INT(len) { LTE_INT(radius) { diff --git a/libs/hwui/Vertex.h b/libs/hwui/Vertex.h index bbf4d4a..38455dc 100644 --- a/libs/hwui/Vertex.h +++ b/libs/hwui/Vertex.h @@ -23,6 +23,18 @@ namespace uirenderer { /** * Simple structure to describe a vertex with a position and a texture. */ +struct Vertex { + float position[2]; + + static inline void set(Vertex* vertex, float x, float y) { + vertex[0].position[0] = x; + vertex[0].position[1] = y; + } +}; // struct Vertex + +/** + * Simple structure to describe a vertex with a position and a texture. + */ struct TextureVertex { float position[2]; float texture[2]; @@ -40,6 +52,41 @@ struct TextureVertex { } }; // struct TextureVertex +/** + * Simple structure to describe a vertex with a position and an alpha value. + */ +struct AlphaVertex : Vertex { + float alpha; + + static inline void set(AlphaVertex* vertex, float x, float y, float alpha) { + Vertex::set(vertex, x, y); + vertex[0].alpha = alpha; + } + + static inline void setColor(AlphaVertex* vertex, float alpha) { + vertex[0].alpha = alpha; + } +}; // struct AlphaVertex + +/** + * Simple structure to describe a vertex with a position and an alpha value. + */ +struct AAVertex : Vertex { + float width; + float length; + + static inline void set(AAVertex* vertex, float x, float y, float width, float length) { + Vertex::set(vertex, x, y); + vertex[0].width = width; + vertex[0].length = length; + } + + static inline void setColor(AAVertex* vertex, float width, float length) { + vertex[0].width = width; + vertex[0].length = length; + } +}; // struct AlphaVertex + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/utils/GenerationCache.h b/libs/hwui/utils/GenerationCache.h deleted file mode 100644 index 42e6d9b..0000000 --- a/libs/hwui/utils/GenerationCache.h +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2010 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_GENERATION_CACHE_H -#define ANDROID_HWUI_GENERATION_CACHE_H - -#include <utils/KeyedVector.h> -#include <utils/RefBase.h> - -namespace android { -namespace uirenderer { - -template<typename EntryKey, typename EntryValue> -class OnEntryRemoved { -public: - virtual ~OnEntryRemoved() { }; - virtual void operator()(EntryKey& key, EntryValue& value) = 0; -}; // class OnEntryRemoved - -template<typename EntryKey, typename EntryValue> -struct Entry: public LightRefBase<Entry<EntryKey, EntryValue> > { - Entry() { } - Entry(const Entry<EntryKey, EntryValue>& e): - key(e.key), value(e.value), parent(e.parent), child(e.child) { } - Entry(sp<Entry<EntryKey, EntryValue> > e): - key(e->key), value(e->value), parent(e->parent), child(e->child) { } - - EntryKey key; - EntryValue value; - - sp<Entry<EntryKey, EntryValue> > parent; - sp<Entry<EntryKey, EntryValue> > child; -}; // struct Entry - -template<typename K, typename V> -class GenerationCache { -public: - GenerationCache(uint32_t maxCapacity); - virtual ~GenerationCache(); - - enum Capacity { - kUnlimitedCapacity, - }; - - void setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener); - - void clear(); - - bool contains(K key) const; - V get(K key); - K getKeyAt(uint32_t index) const; - bool put(K key, V value); - V remove(K key); - V removeOldest(); - V getValueAt(uint32_t index) const; - - uint32_t size() const; - - void addToCache(sp<Entry<K, V> > entry, K key, V value); - void attachToCache(sp<Entry<K, V> > entry); - void detachFromCache(sp<Entry<K, V> > entry); - - V removeAt(ssize_t index); - - KeyedVector<K, sp<Entry<K, V> > > mCache; - uint32_t mMaxCapacity; - - OnEntryRemoved<K, V>* mListener; - - sp<Entry<K, V> > mOldest; - sp<Entry<K, V> > mYoungest; -}; // class GenerationCache - -template<typename K, typename V> -GenerationCache<K, V>::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity), mListener(NULL) { -}; - -template<typename K, typename V> -GenerationCache<K, V>::~GenerationCache() { - clear(); -}; - -template<typename K, typename V> -uint32_t GenerationCache<K, V>::size() const { - return mCache.size(); -} - -template<typename K, typename V> -void GenerationCache<K, V>::setOnEntryRemovedListener(OnEntryRemoved<K, V>* listener) { - mListener = listener; -} - -template<typename K, typename V> -void GenerationCache<K, V>::clear() { - if (mListener) { - for (uint32_t i = 0; i < mCache.size(); i++) { - sp<Entry<K, V> > entry = mCache.valueAt(i); - if (mListener) { - (*mListener)(entry->key, entry->value); - } - } - } - mCache.clear(); - mYoungest.clear(); - mOldest.clear(); -} - -template<typename K, typename V> -bool GenerationCache<K, V>::contains(K key) const { - return mCache.indexOfKey(key) >= 0; -} - -template<typename K, typename V> -K GenerationCache<K, V>::getKeyAt(uint32_t index) const { - return mCache.keyAt(index); -} - -template<typename K, typename V> -V GenerationCache<K, V>::getValueAt(uint32_t index) const { - return mCache.valueAt(index)->value; -} - -template<typename K, typename V> -V GenerationCache<K, V>::get(K key) { - ssize_t index = mCache.indexOfKey(key); - if (index >= 0) { - sp<Entry<K, V> > entry = mCache.valueAt(index); - if (entry.get()) { - detachFromCache(entry); - attachToCache(entry); - return entry->value; - } - } - - return NULL; -} - -template<typename K, typename V> -bool GenerationCache<K, V>::put(K key, V value) { - if (mMaxCapacity != kUnlimitedCapacity && mCache.size() >= mMaxCapacity) { - removeOldest(); - } - - ssize_t index = mCache.indexOfKey(key); - if (index < 0) { - sp<Entry<K, V> > entry = new Entry<K, V>; - addToCache(entry, key, value); - return true; - } - - return false; -} - -template<typename K, typename V> -void GenerationCache<K, V>::addToCache(sp<Entry<K, V> > entry, K key, V value) { - entry->key = key; - entry->value = value; - mCache.add(key, entry); - attachToCache(entry); -} - -template<typename K, typename V> -V GenerationCache<K, V>::remove(K key) { - ssize_t index = mCache.indexOfKey(key); - if (index >= 0) { - return removeAt(index); - } - - return NULL; -} - -template<typename K, typename V> -V GenerationCache<K, V>::removeAt(ssize_t index) { - sp<Entry<K, V> > entry = mCache.valueAt(index); - if (mListener) { - (*mListener)(entry->key, entry->value); - } - mCache.removeItemsAt(index, 1); - detachFromCache(entry); - - return entry->value; -} - -template<typename K, typename V> -V GenerationCache<K, V>::removeOldest() { - if (mOldest.get()) { - ssize_t index = mCache.indexOfKey(mOldest->key); - if (index >= 0) { - return removeAt(index); - } - } - - return NULL; -} - -template<typename K, typename V> -void GenerationCache<K, V>::attachToCache(sp<Entry<K, V> > entry) { - if (!mYoungest.get()) { - mYoungest = mOldest = entry; - } else { - entry->parent = mYoungest; - mYoungest->child = entry; - mYoungest = entry; - } -} - -template<typename K, typename V> -void GenerationCache<K, V>::detachFromCache(sp<Entry<K, V> > entry) { - if (entry->parent.get()) { - entry->parent->child = entry->child; - } - - if (entry->child.get()) { - entry->child->parent = entry->parent; - } - - if (mOldest == entry) { - mOldest = entry->child; - } - - if (mYoungest == entry) { - mYoungest = entry->parent; - } - - entry->parent.clear(); - entry->child.clear(); -} - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_GENERATION_CACHE_H |