From 5a4690bf26932c0d6940e4af8516d920e09ae81a Mon Sep 17 00:00:00 2001 From: Chris Craik Date: Tue, 14 Jul 2015 12:13:03 -0700 Subject: Clean up unncessary defines LOG_TAG and TRACE_TAG are already defined in the makefile Change-Id: I9e53e3dacbe018441edd74cb7c8c90846defee74 --- libs/hwui/TextDropShadowCache.cpp | 2 -- 1 file changed, 2 deletions(-) (limited to 'libs/hwui/TextDropShadowCache.cpp') diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp index 8b1d4cb2196c..b7a76baadff5 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -14,8 +14,6 @@ * limitations under the License. */ -#define LOG_TAG "OpenGLRenderer" - #include #include "Caches.h" -- cgit v1.2.3-59-g8ed1b From a1717271caac5e8ea3808c331d4141ac01a42134 Mon Sep 17 00:00:00 2001 From: Chris Craik Date: Thu, 19 Nov 2015 13:02:43 -0800 Subject: Initial text support in new reorderer/renderer Removes obsolete drawPosText codepath, and unifies text decoration behavior. Change-Id: I9c563249ab688a3394445a0e7fe1b9d0661f6f7c --- core/jni/android_graphics_Canvas.cpp | 45 +--------- libs/hwui/Android.mk | 1 + libs/hwui/BakedOpRenderer.cpp | 92 +++++++++++++++++++ libs/hwui/Canvas.cpp | 56 ++++++++++++ libs/hwui/Canvas.h | 9 +- libs/hwui/DisplayListCanvas.cpp | 13 +-- libs/hwui/DisplayListCanvas.h | 2 - libs/hwui/DisplayListOp.h | 18 ---- libs/hwui/FontRenderer.cpp | 40 ++++++--- libs/hwui/FontRenderer.h | 36 ++++++-- libs/hwui/OpReorderer.cpp | 18 ++++ libs/hwui/OpenGLRenderer.cpp | 125 ++++---------------------- libs/hwui/OpenGLRenderer.h | 17 +--- libs/hwui/RecordedOp.h | 37 ++++++++ libs/hwui/RecordingCanvas.cpp | 49 +++++++--- libs/hwui/RecordingCanvas.h | 18 ++-- libs/hwui/Rect.h | 7 -- libs/hwui/SkiaCanvas.cpp | 20 +---- libs/hwui/TextDropShadowCache.cpp | 23 +++-- libs/hwui/TextDropShadowCache.h | 15 ++-- libs/hwui/font/Font.cpp | 23 +++-- libs/hwui/font/Font.h | 8 +- libs/hwui/unit_tests/OpReordererTests.cpp | 38 ++++++-- libs/hwui/unit_tests/RecordingCanvasTests.cpp | 107 ++++++++++++++++++++-- libs/hwui/utils/TestUtils.cpp | 41 +++++++++ libs/hwui/utils/TestUtils.h | 3 + 26 files changed, 544 insertions(+), 317 deletions(-) create mode 100644 libs/hwui/Canvas.cpp (limited to 'libs/hwui/TextDropShadowCache.cpp') diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp index 3d96fab9709d..e4e73a4b4c8a 100644 --- a/core/jni/android_graphics_Canvas.cpp +++ b/core/jni/android_graphics_Canvas.cpp @@ -544,39 +544,6 @@ private: float totalAdvance; }; -// Same values used by Skia -#define kStdStrikeThru_Offset (-6.0f / 21.0f) -#define kStdUnderline_Offset (1.0f / 9.0f) -#define kStdUnderline_Thickness (1.0f / 18.0f) - -void drawTextDecorations(Canvas* canvas, float x, float y, float length, const SkPaint& paint) { - uint32_t flags; - SkDrawFilter* drawFilter = canvas->getDrawFilter(); - if (drawFilter) { - SkPaint paintCopy(paint); - drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type); - flags = paintCopy.getFlags(); - } else { - flags = paint.getFlags(); - } - if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { - SkScalar left = x; - SkScalar right = x + length; - float textSize = paint.getTextSize(); - float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); - if (flags & SkPaint::kUnderlineText_Flag) { - SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth; - SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth; - canvas->drawRect(left, top, right, bottom, paint); - } - if (flags & SkPaint::kStrikeThruText_Flag) { - SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth; - SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth; - canvas->drawRect(left, top, right, bottom, paint); - } - } -} - void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int contextCount, float x, float y, int bidiFlags, const Paint& origPaint, TypefaceImpl* typeface) { // minikin may modify the original paint @@ -586,8 +553,8 @@ void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int co MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount); size_t nGlyphs = layout.nGlyphs(); - uint16_t* glyphs = new uint16_t[nGlyphs]; - float* pos = new float[nGlyphs * 2]; + std::unique_ptr glyphs(new uint16_t[nGlyphs]); + std::unique_ptr pos(new float[nGlyphs * 2]); x += MinikinUtils::xOffsetForTextAlign(&paint, layout); @@ -597,13 +564,9 @@ void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int co bounds.offset(x, y); } - DrawTextFunctor f(layout, canvas, glyphs, pos, paint, x, y, bounds, layout.getAdvance()); + DrawTextFunctor f(layout, canvas, glyphs.get(), pos.get(), + paint, x, y, bounds, layout.getAdvance()); MinikinUtils::forFontRun(layout, &paint, f); - - drawTextDecorations(canvas, x, y, layout.getAdvance(), paint); - - delete[] glyphs; - delete[] pos; } static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 0a57d509d129..cc68fb292a8f 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -37,6 +37,7 @@ hwui_src_files := \ AnimatorManager.cpp \ AssetAtlas.cpp \ Caches.cpp \ + Canvas.cpp \ CanvasState.cpp \ ClipArea.cpp \ DamageAccumulator.cpp \ diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp index d2d3285b71f8..d13d7ef81e5b 100644 --- a/libs/hwui/BakedOpRenderer.cpp +++ b/libs/hwui/BakedOpRenderer.cpp @@ -24,6 +24,9 @@ #include "utils/GLUtils.h" #include "VertexBuffer.h" +#include +#include + namespace android { namespace uirenderer { @@ -183,6 +186,10 @@ void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op renderer.renderGlop(state, glop); } +void BakedOpDispatcher::onLinesOp(BakedOpRenderer& renderer, const LinesOp& op, const BakedOpState& state) { + LOG_ALWAYS_FATAL("todo"); +} + void BakedOpDispatcher::onRectOp(BakedOpRenderer& renderer, const RectOp& op, const BakedOpState& state) { Glop glop; GlopBuilder(renderer.renderState(), renderer.caches(), &glop) @@ -270,6 +277,91 @@ void BakedOpDispatcher::onSimpleRectsOp(BakedOpRenderer& renderer, const SimpleR renderer.renderGlop(state, glop); } +static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRenderer, + const TextOp& op, const BakedOpState& state) { + renderer.caches().textureState().activateTexture(0); + + PaintUtils::TextShadow textShadow; + if (!PaintUtils::getTextShadow(op.paint, &textShadow)) { + LOG_ALWAYS_FATAL("failed to query shadow attributes"); + } + + renderer.caches().dropShadowCache.setFontRenderer(fontRenderer); + ShadowTexture* texture = renderer.caches().dropShadowCache.get( + op.paint, (const char*) op.glyphs, + op.glyphCount, textShadow.radius, op.positions); + // If the drop shadow exceeds the max texture size or couldn't be + // allocated, skip drawing + if (!texture) return; + const AutoTexture autoCleanup(texture); + + const float sx = op.x - texture->left + textShadow.dx; + const float sy = op.y - texture->top + textShadow.dy; + + Glop glop; + GlopBuilder(renderer.renderState(), renderer.caches(), &glop) + .setRoundRectClipState(state.roundRectClipState) + .setMeshTexturedUnitQuad(nullptr) + .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha) + .setTransform(state.computedState.transform, TransformFlags::None) + .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height)) + .build(); + renderer.renderGlop(state, glop); +} + +void BakedOpDispatcher::onTextOp(BakedOpRenderer& renderer, const TextOp& op, const BakedOpState& state) { + FontRenderer& fontRenderer = renderer.caches().fontRenderer.getFontRenderer(); + + if (CC_UNLIKELY(PaintUtils::hasTextShadow(op.paint))) { + fontRenderer.setFont(op.paint, SkMatrix::I()); + renderTextShadow(renderer, fontRenderer, op, state); + } + + float x = op.x; + float y = op.y; + const Matrix4& transform = state.computedState.transform; + const bool pureTranslate = transform.isPureTranslate(); + if (CC_LIKELY(pureTranslate)) { + x = floorf(x + transform.getTranslateX() + 0.5f); + y = floorf(y + transform.getTranslateY() + 0.5f); + fontRenderer.setFont(op.paint, SkMatrix::I()); + fontRenderer.setTextureFiltering(false); + } else if (CC_UNLIKELY(transform.isPerspective())) { + fontRenderer.setFont(op.paint, SkMatrix::I()); + fontRenderer.setTextureFiltering(true); + } else { + // We only pass a partial transform to the font renderer. That partial + // matrix defines how glyphs are rasterized. Typically we want glyphs + // to be rasterized at their final size on screen, which means the partial + // matrix needs to take the scale factor into account. + // When a partial matrix is used to transform glyphs during rasterization, + // the mesh is generated with the inverse transform (in the case of scale, + // the mesh is generated at 1.0 / scale for instance.) This allows us to + // apply the full transform matrix at draw time in the vertex shader. + // Applying the full matrix in the shader is the easiest way to handle + // rotation and perspective and allows us to always generated quads in the + // font renderer which greatly simplifies the code, clipping in particular. + float sx, sy; + transform.decomposeScale(sx, sy); + fontRenderer.setFont(op.paint, SkMatrix::MakeScale( + roundf(std::max(1.0f, sx)), + roundf(std::max(1.0f, sy)))); + fontRenderer.setTextureFiltering(true); + } + + // TODO: Implement better clipping for scaled/rotated text + const Rect* clip = !pureTranslate ? nullptr : &state.computedState.clipRect; + Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); + + int alpha = PaintUtils::getAlphaDirect(op.paint) * state.alpha; + SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(op.paint); + TextDrawFunctor functor(&renderer, &state, x, y, pureTranslate, alpha, mode, op.paint); + + bool hasActiveLayer = false; // TODO + fontRenderer.renderPosText(op.paint, clip, (const char*) op.glyphs, op.glyphCount, x, y, + op.positions, hasActiveLayer ? &layerBounds : nullptr, &functor, true); // TODO: merging +} + void BakedOpDispatcher::onLayerOp(BakedOpRenderer& renderer, const LayerOp& op, const BakedOpState& state) { OffscreenBuffer* buffer = *op.layerHandle; diff --git a/libs/hwui/Canvas.cpp b/libs/hwui/Canvas.cpp new file mode 100644 index 000000000000..bc88c817ffc8 --- /dev/null +++ b/libs/hwui/Canvas.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 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 "Canvas.h" + +#include + +namespace android { + +void Canvas::drawTextDecorations(float x, float y, float length, const SkPaint& paint) { + uint32_t flags; + SkDrawFilter* drawFilter = getDrawFilter(); + if (drawFilter) { + SkPaint paintCopy(paint); + drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type); + flags = paintCopy.getFlags(); + } else { + flags = paint.getFlags(); + } + if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { + // Same values used by Skia + const float kStdStrikeThru_Offset = (-6.0f / 21.0f); + const float kStdUnderline_Offset = (1.0f / 9.0f); + const float kStdUnderline_Thickness = (1.0f / 18.0f); + + SkScalar left = x; + SkScalar right = x + length; + float textSize = paint.getTextSize(); + float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f); + if (flags & SkPaint::kUnderlineText_Flag) { + SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth; + SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth; + drawRect(left, top, right, bottom, paint); + } + if (flags & SkPaint::kStrikeThruText_Flag) { + SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth; + SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth; + drawRect(left, top, right, bottom, paint); + } + } +} + +} // namespace android diff --git a/libs/hwui/Canvas.h b/libs/hwui/Canvas.h index 4bd4ac8d4c37..b585a2799cad 100644 --- a/libs/hwui/Canvas.h +++ b/libs/hwui/Canvas.h @@ -149,16 +149,12 @@ public: // Text /** * drawText: count is of glyphs - * totalAdvance is ignored in software renderering, used by hardware renderer for - * text decorations (underlines, strikethroughs). + * totalAdvance: used to define width of text decorations (underlines, strikethroughs). */ virtual void drawText(const uint16_t* glyphs, const float* positions, int count, const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, float totalAdvance) = 0; - /** drawPosText: count is of UTF16 characters, posCount is floats (2 * glyphs) */ - virtual void drawPosText(const uint16_t* text, const float* positions, int count, - int posCount, const SkPaint& paint) = 0; /** drawTextOnPath: count is of glyphs */ virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) = 0; @@ -171,6 +167,9 @@ public: * to be added to each glyph's position to get its absolute position. */ virtual bool drawTextAbsolutePos() const = 0; + +protected: + void drawTextDecorations(float x, float y, float length, const SkPaint& paint); }; }; // namespace android diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp index f5e57355138c..759c12a3f214 100644 --- a/libs/hwui/DisplayListCanvas.cpp +++ b/libs/hwui/DisplayListCanvas.cpp @@ -423,18 +423,6 @@ void DisplayListCanvas::drawTextOnPath(const uint16_t* glyphs, int count, addDrawOp(op); } -void DisplayListCanvas::drawPosText(const uint16_t* text, const float* positions, - int count, int posCount, const SkPaint& paint) { - if (!text || count <= 0) return; - - int bytesCount = 2 * count; - positions = refBuffer(positions, count * 2); - - DrawOp* op = new (alloc()) DrawPosTextOp(refText((const char*) text, bytesCount), - bytesCount, count, positions, refPaint(&paint)); - addDrawOp(op); -} - void DisplayListCanvas::drawText(const uint16_t* glyphs, const float* positions, int count, const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, @@ -450,6 +438,7 @@ void DisplayListCanvas::drawText(const uint16_t* glyphs, const float* positions, DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count, x, y, positions, refPaint(&paint), totalAdvance, bounds); addDrawOp(op); + drawTextDecorations(x, y, totalAdvance, paint); } void DisplayListCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h index 609103b89644..bf98f79d4d5a 100644 --- a/libs/hwui/DisplayListCanvas.h +++ b/libs/hwui/DisplayListCanvas.h @@ -212,8 +212,6 @@ public: virtual void drawText(const uint16_t* glyphs, const float* positions, int count, const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, float totalAdvance) override; - virtual void drawPosText(const uint16_t* text, const float* positions, int count, - int posCount, const SkPaint& paint) override; virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) override; virtual bool drawTextAbsolutePos() const override { return false; } diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 772aa72b7fdc..977b53c31f46 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -1278,24 +1278,6 @@ private: float mVOffset; }; -class DrawPosTextOp : public DrawSomeTextOp { -public: - DrawPosTextOp(const char* text, int bytesCount, int count, - const float* positions, const SkPaint* paint) - : DrawSomeTextOp(text, bytesCount, count, paint), mPositions(positions) { - /* TODO: inherit from DrawBounded and init mLocalBounds */ - } - - virtual void applyDraw(OpenGLRenderer& renderer, Rect& dirty) override { - renderer.drawPosText(mText, mBytesCount, mCount, mPositions, mPaint); - } - - virtual const char* name() override { return "DrawPosText"; } - -private: - const float* mPositions; -}; - class DrawTextOp : public DrawStrokableOp { public: DrawTextOp(const char* text, int bytesCount, int count, float x, float y, diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index ccf0b48cd4be..5f33cae2f91a 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -21,13 +21,20 @@ #include "Extensions.h" #include "Glop.h" #include "GlopBuilder.h" -#include "OpenGLRenderer.h" #include "PixelBuffer.h" #include "Rect.h" #include "renderstate/RenderState.h" #include "utils/Blur.h" #include "utils/Timing.h" + +#if HWUI_NEW_OPS +#include "BakedOpState.h" +#include "BakedOpRenderer.h" +#else +#include "OpenGLRenderer.h" +#endif + #include #include #include @@ -59,14 +66,25 @@ void TextDrawFunctor::draw(CacheTexture& texture, bool linearFiltering) { int transformFlags = pureTranslate ? TransformFlags::MeshIgnoresCanvasTransform : TransformFlags::None; Glop glop; +#if HWUI_NEW_OPS + GlopBuilder(renderer->renderState(), renderer->caches(), &glop) + .setRoundRectClipState(bakedState->roundRectClipState) + .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount()) + .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, bakedState->alpha) + .setTransform(bakedState->computedState.transform, transformFlags) + .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0)) + .build(); + renderer->renderGlop(*bakedState, glop); +#else GlopBuilder(renderer->mRenderState, renderer->mCaches, &glop) + .setRoundRectClipState(renderer->currentSnapshot()->roundRectClipState) .setMeshTexturedIndexedQuads(texture.mesh(), texture.meshElementCount()) .setFillTexturePaint(texture.getTexture(), textureFillFlags, paint, renderer->currentSnapshot()->alpha) .setTransform(*(renderer->currentSnapshot()), transformFlags) .setModelViewOffsetRect(0, 0, Rect(0, 0, 0, 0)) - .setRoundRectClipState(renderer->currentSnapshot()->roundRectClipState) .build(); renderer->renderGlop(glop); +#endif } /////////////////////////////////////////////////////////////////////////////// @@ -539,7 +557,7 @@ void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) { } FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text, - uint32_t startIndex, uint32_t len, int numGlyphs, float radius, const float* positions) { + int numGlyphs, float radius, const float* positions) { checkInit(); DropShadow image; @@ -558,7 +576,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co mBounds = nullptr; Rect bounds; - mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); + mCurrentFont->measure(paint, text, numGlyphs, &bounds, positions); uint32_t intRadius = Blur::convertRadiusToInt(radius); uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius; @@ -590,7 +608,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co // text has non-whitespace, so draw and blur to create the shadow // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted // TODO: don't draw pure whitespace in the first place, and avoid needing this check - mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY, + mCurrentFont->render(paint, text, numGlyphs, penX, penY, Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions); // Unbind any PBO we might have used @@ -635,15 +653,15 @@ void FontRenderer::endPrecaching() { } bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text, - uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, - const float* positions, Rect* bounds, TextDrawFunctor* functor, bool forceFinish) { + int numGlyphs, int x, int y, const float* positions, + Rect* bounds, TextDrawFunctor* functor, bool forceFinish) { if (!mCurrentFont) { ALOGE("No font set"); return false; } initRender(clip, bounds, functor); - mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions); + mCurrentFont->render(paint, text, numGlyphs, x, y, positions); if (forceFinish) { finishRender(); @@ -653,15 +671,15 @@ bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const c } bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text, - uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path, - float hOffset, float vOffset, Rect* bounds, TextDrawFunctor* functor) { + int numGlyphs, const SkPath* path, float hOffset, float vOffset, + Rect* bounds, TextDrawFunctor* functor) { if (!mCurrentFont) { ALOGE("No font set"); return false; } initRender(clip, bounds, functor); - mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); + mCurrentFont->render(paint, text, numGlyphs, path, hOffset, vOffset); finishRender(); return mDrawn; diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index 8172312e9a43..87cfe7ff98e0 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -44,13 +44,28 @@ namespace RSC { namespace android { namespace uirenderer { +#if HWUI_NEW_OPS +class BakedOpState; +class BakedOpRenderer; +#else class OpenGLRenderer; +#endif class TextDrawFunctor { public: - TextDrawFunctor(OpenGLRenderer* renderer, float x, float y, bool pureTranslate, + TextDrawFunctor( +#if HWUI_NEW_OPS + BakedOpRenderer* renderer, + const BakedOpState* bakedState, +#else + OpenGLRenderer* renderer, +#endif + float x, float y, bool pureTranslate, int alpha, SkXfermode::Mode mode, const SkPaint* paint) : renderer(renderer) +#if HWUI_NEW_OPS + , bakedState(bakedState) +#endif , x(x) , y(y) , pureTranslate(pureTranslate) @@ -61,7 +76,12 @@ public: void draw(CacheTexture& texture, bool linearFiltering); +#if HWUI_NEW_OPS + BakedOpRenderer* renderer; + const BakedOpState* bakedState; +#else OpenGLRenderer* renderer; +#endif float x; float y; bool pureTranslate; @@ -83,15 +103,13 @@ public: void precache(const SkPaint* paint, const char* text, int numGlyphs, const SkMatrix& matrix); void endPrecaching(); - // bounds is an out parameter bool renderPosText(const SkPaint* paint, const Rect* clip, const char *text, - uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, const float* positions, - Rect* bounds, TextDrawFunctor* functor, bool forceFinish = true); + int numGlyphs, int x, int y, const float* positions, + Rect* outBounds, TextDrawFunctor* functor, bool forceFinish = true); - // bounds is an out parameter bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text, - uint32_t startIndex, uint32_t len, int numGlyphs, const SkPath* path, - float hOffset, float vOffset, Rect* bounds, TextDrawFunctor* functor); + int numGlyphs, const SkPath* path, + float hOffset, float vOffset, Rect* outBounds, TextDrawFunctor* functor); struct DropShadow { uint32_t width; @@ -103,8 +121,8 @@ public: // After renderDropShadow returns, the called owns the memory in DropShadow.image // and is responsible for releasing it when it's done with it - DropShadow renderDropShadow(const SkPaint* paint, const char *text, uint32_t startIndex, - uint32_t len, int numGlyphs, float radius, const float* positions); + DropShadow renderDropShadow(const SkPaint* paint, const char *text, int numGlyphs, + float radius, const float* positions); void setTextureFiltering(bool linearFiltering) { mLinearFiltering = linearFiltering; diff --git a/libs/hwui/OpReorderer.cpp b/libs/hwui/OpReorderer.cpp index 96cac7eedaf0..5e954ae6e971 100644 --- a/libs/hwui/OpReorderer.cpp +++ b/libs/hwui/OpReorderer.cpp @@ -671,6 +671,13 @@ void OpReorderer::onBitmapOp(const BitmapOp& op) { currentLayer().deferMergeableOp(mAllocator, bakedStateOp, OpBatchType::Bitmap, mergeId); } +void OpReorderer::onLinesOp(const LinesOp& op) { + BakedOpState* bakedStateOp = tryBakeOpState(op); + if (!bakedStateOp) return; // quick rejected + currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, OpBatchType::Vertices); + +} + void OpReorderer::onRectOp(const RectOp& op) { BakedOpState* bakedStateOp = tryBakeOpState(op); if (!bakedStateOp) return; // quick rejected @@ -683,6 +690,17 @@ void OpReorderer::onSimpleRectsOp(const SimpleRectsOp& op) { currentLayer().deferUnmergeableOp(mAllocator, bakedStateOp, OpBatchType::Vertices); } +void OpReorderer::onTextOp(const TextOp& op) { + BakedOpState* bakedStateOp = tryBakeOpState(op); + if (!bakedStateOp) return; // quick rejected + + // TODO: better handling of shader (since we won't care about color then) + batchid_t batchId = op.paint->getColor() == SK_ColorBLACK + ? OpBatchType::Text : OpBatchType::ColorText; + mergeid_t mergeId = reinterpret_cast(op.paint->getColor()); + currentLayer().deferMergeableOp(mAllocator, bakedStateOp, batchId, mergeId); +} + void OpReorderer::saveForLayer(uint32_t layerWidth, uint32_t layerHeight, float contentTranslateX, float contentTranslateY, const Rect& repaintRect, diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 12c4607130ca..e386b1cacf74 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -1950,7 +1950,7 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, } void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, - int bytesCount, int count, const float* positions, + int count, const float* positions, FontRenderer& fontRenderer, int alpha, float x, float y) { mCaches.textureState().activateTexture(0); @@ -1963,7 +1963,7 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, // if shader-based correction is enabled mCaches.dropShadowCache.setFontRenderer(fontRenderer); ShadowTexture* texture = mCaches.dropShadowCache.get( - paint, text, bytesCount, count, textShadow.radius, positions); + paint, text, count, textShadow.radius, positions); // If the drop shadow exceeds the max texture size or couldn't be // allocated, skip drawing if (!texture) return; @@ -1991,57 +1991,6 @@ bool OpenGLRenderer::canSkipText(const SkPaint* paint) const { && PaintUtils::getXfermode(paint->getXfermode()) == SkXfermode::kSrcOver_Mode; } -void OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count, - const float* positions, const SkPaint* paint) { - if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) { - return; - } - - // NOTE: Skia does not support perspective transform on drawPosText yet - if (!currentTransform()->isSimple()) { - return; - } - - mRenderState.scissor().setEnabled(true); - - float x = 0.0f; - float y = 0.0f; - const bool pureTranslate = currentTransform()->isPureTranslate(); - if (pureTranslate) { - x = floorf(x + currentTransform()->getTranslateX() + 0.5f); - y = floorf(y + currentTransform()->getTranslateY() + 0.5f); - } - - FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(); - fontRenderer.setFont(paint, SkMatrix::I()); - - int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha; - SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint); - - if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) { - drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, - alpha, 0.0f, 0.0f); - } - - // Pick the appropriate texture filtering - bool linearFilter = currentTransform()->changesBounds(); - if (pureTranslate && !linearFilter) { - linearFilter = fabs(y - (int) y) > 0.0f || fabs(x - (int) x) > 0.0f; - } - fontRenderer.setTextureFiltering(linearFilter); - - const Rect& clip(pureTranslate ? writableSnapshot()->getRenderTargetClip() : writableSnapshot()->getLocalClip()); - Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); - - TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint); - if (fontRenderer.renderPosText(paint, &clip, text, 0, bytesCount, count, x, y, - positions, hasLayer() ? &bounds : nullptr, &functor)) { - dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform()); - mDirty = true; - } - -} - bool OpenGLRenderer::findBestFontTransform(const mat4& transform, SkMatrix* outMatrix) const { if (CC_LIKELY(transform.isPureTranslate())) { outMatrix->setIdentity(); @@ -2166,7 +2115,7 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) { fontRenderer.setFont(paint, SkMatrix::I()); - drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, + drawTextShadow(paint, text, count, positions, fontRenderer, alpha, oldX, oldY); } @@ -2195,17 +2144,22 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float Rect layerBounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); bool status; +#if HWUI_NEW_OPS + LOG_ALWAYS_FATAL("unsupported"); + TextDrawFunctor functor(nullptr, nullptr, x, y, pureTranslate, alpha, mode, paint); +#else TextDrawFunctor functor(this, x, y, pureTranslate, alpha, mode, paint); +#endif // don't call issuedrawcommand, do it at end of batch bool forceFinish = (drawOpMode != DrawOpMode::kDefer); if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) { SkPaint paintCopy(*paint); paintCopy.setTextAlign(SkPaint::kLeft_Align); - status = fontRenderer.renderPosText(&paintCopy, clip, text, 0, bytesCount, count, x, y, + status = fontRenderer.renderPosText(&paintCopy, clip, text, count, x, y, positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish); } else { - status = fontRenderer.renderPosText(paint, clip, text, 0, bytesCount, count, x, y, + status = fontRenderer.renderPosText(paint, clip, text, count, x, y, positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish); } @@ -2216,8 +2170,6 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float dirtyLayerUnchecked(layerBounds, getRegion()); } - drawTextDecorations(totalAdvance, oldX, oldY, paint); - mDirty = true; } @@ -2236,12 +2188,17 @@ void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, int alpha = PaintUtils::getAlphaDirect(paint) * currentSnapshot()->alpha; SkXfermode::Mode mode = PaintUtils::getXfermodeDirect(paint); +#if HWUI_NEW_OPS + LOG_ALWAYS_FATAL("unsupported"); + TextDrawFunctor functor(nullptr, nullptr, 0.0f, 0.0f, false, alpha, mode, paint); +#else TextDrawFunctor functor(this, 0.0f, 0.0f, false, alpha, mode, paint); +#endif const Rect* clip = &writableSnapshot()->getLocalClip(); Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); - if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path, + if (fontRenderer.renderTextOnPath(paint, clip, text, count, path, hOffset, vOffset, hasLayer() ? &bounds : nullptr, &functor)) { dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform()); mDirty = true; @@ -2375,56 +2332,6 @@ void OpenGLRenderer::drawPathTexture(PathTexture* texture, float x, float y, renderGlop(glop); } -// Same values used by Skia -#define kStdStrikeThru_Offset (-6.0f / 21.0f) -#define kStdUnderline_Offset (1.0f / 9.0f) -#define kStdUnderline_Thickness (1.0f / 18.0f) - -void OpenGLRenderer::drawTextDecorations(float underlineWidth, float x, float y, - const SkPaint* paint) { - // Handle underline and strike-through - uint32_t flags = paint->getFlags(); - if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) { - SkPaint paintCopy(*paint); - - if (CC_LIKELY(underlineWidth > 0.0f)) { - const float textSize = paintCopy.getTextSize(); - const float strokeWidth = std::max(textSize * kStdUnderline_Thickness, 1.0f); - - const float left = x; - float top = 0.0f; - - int linesCount = 0; - if (flags & SkPaint::kUnderlineText_Flag) linesCount++; - if (flags & SkPaint::kStrikeThruText_Flag) linesCount++; - - const int pointsCount = 4 * linesCount; - float points[pointsCount]; - int currentPoint = 0; - - if (flags & SkPaint::kUnderlineText_Flag) { - top = y + textSize * kStdUnderline_Offset; - points[currentPoint++] = left; - points[currentPoint++] = top; - points[currentPoint++] = left + underlineWidth; - points[currentPoint++] = top; - } - - if (flags & SkPaint::kStrikeThruText_Flag) { - top = y + textSize * kStdStrikeThru_Offset; - points[currentPoint++] = left; - points[currentPoint++] = top; - points[currentPoint++] = left + underlineWidth; - points[currentPoint++] = top; - } - - paintCopy.setStrokeWidth(strokeWidth); - - drawLines(&points[0], pointsCount, &paintCopy); - } - } -} - void OpenGLRenderer::drawRects(const float* rects, int count, const SkPaint* paint) { if (mState.currentlyIgnored()) { return; diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 400c225b53a0..84bc9b059d33 100755 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -193,8 +193,6 @@ public: void drawPoints(const float* points, int count, const SkPaint* paint); void drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path, float hOffset, float vOffset, const SkPaint* paint); - void drawPosText(const char* text, int bytesCount, int count, - const float* positions, const SkPaint* paint); void drawText(const char* text, int bytesCount, int count, float x, float y, const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode = DrawOpMode::kImmediate); @@ -637,24 +635,11 @@ private: */ void drawConvexPath(const SkPath& path, const SkPaint* paint); - /** - * Draws text underline and strike-through if needed. - * - * @param text The text to decor - * @param bytesCount The number of bytes in the text - * @param totalAdvance The total advance in pixels, defines underline/strikethrough length - * @param x The x coordinate where the text will be drawn - * @param y The y coordinate where the text will be drawn - * @param paint The paint to draw the text with - */ - void drawTextDecorations(float totalAdvance, float x, float y, const SkPaint* paint); - /** * Draws shadow layer on text (with optional positions). * * @param paint The paint to draw the shadow with * @param text The text to draw - * @param bytesCount The number of bytes in the text * @param count The number of glyphs in the text * @param positions The x, y positions of individual glyphs (or NULL) * @param fontRenderer The font renderer object @@ -662,7 +647,7 @@ private: * @param x The x coordinate where the shadow will be drawn * @param y The y coordinate where the shadow will be drawn */ - void drawTextShadow(const SkPaint* paint, const char* text, int bytesCount, int count, + void drawTextShadow(const SkPaint* paint, const char* text, int count, const float* positions, FontRenderer& fontRenderer, int alpha, float x, float y); diff --git a/libs/hwui/RecordedOp.h b/libs/hwui/RecordedOp.h index ef0536761a11..127dca5be440 100644 --- a/libs/hwui/RecordedOp.h +++ b/libs/hwui/RecordedOp.h @@ -17,6 +17,7 @@ #ifndef ANDROID_HWUI_RECORDED_OP_H #define ANDROID_HWUI_RECORDED_OP_H +#include "font/FontUtil.h" #include "Matrix.h" #include "Rect.h" #include "RenderNode.h" @@ -42,10 +43,12 @@ struct Vertex; */ #define MAP_OPS(OP_FN) \ OP_FN(BitmapOp) \ + OP_FN(LinesOp) \ OP_FN(RectOp) \ OP_FN(RenderNodeOp) \ OP_FN(ShadowOp) \ OP_FN(SimpleRectsOp) \ + OP_FN(TextOp) \ OP_FN(BeginLayerOp) \ OP_FN(EndLayerOp) \ OP_FN(LayerOp) @@ -98,6 +101,10 @@ struct RenderNodeOp : RecordedOp { bool skipInOrderDraw = false; }; +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Standard Ops +//////////////////////////////////////////////////////////////////////////////////////////////////// + struct BitmapOp : RecordedOp { BitmapOp(BASE_PARAMS, const SkBitmap* bitmap) : SUPER(BitmapOp) @@ -106,6 +113,15 @@ struct BitmapOp : RecordedOp { // TODO: asset atlas/texture id lookup? }; +struct LinesOp : RecordedOp { + LinesOp(BASE_PARAMS, const float* points, const int floatCount) + : SUPER(LinesOp) + , points(points) + , floatCount(floatCount) {} + const float* points; + const int floatCount; +}; + struct RectOp : RecordedOp { RectOp(BASE_PARAMS) : SUPER(RectOp) {} @@ -148,6 +164,27 @@ struct SimpleRectsOp : RecordedOp { // Filled, no AA (TODO: better name?) const size_t vertexCount; }; +struct TextOp : RecordedOp { + TextOp(BASE_PARAMS, const glyph_t* glyphs, const float* positions, int glyphCount, + float x, float y) + : SUPER(TextOp) + , glyphs(glyphs) + , positions(positions) + , glyphCount(glyphCount) + , x(x) + , y(y) {} + const glyph_t* glyphs; + const float* positions; + const int glyphCount; + const float x; + const float y; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Layers +//////////////////////////////////////////////////////////////////////////////////////////////////// + + /** * Stateful operation! denotes the creation of an off-screen layer, * and that commands following will render into it. diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 6ab253c2491f..61fa3844df27 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -230,12 +230,9 @@ void RecordingCanvas::drawColor(int color, SkXfermode::Mode mode) { void RecordingCanvas::drawPaint(const SkPaint& paint) { // TODO: more efficient recording? - Matrix4 identity; - identity.loadIdentity(); - addOp(new (alloc()) RectOp( mState.getRenderTargetClipBounds(), - identity, + Matrix4::identity(), mState.getRenderTargetClipBounds(), refPaint(&paint))); } @@ -244,9 +241,30 @@ void RecordingCanvas::drawPaint(const SkPaint& paint) { void RecordingCanvas::drawPoints(const float* points, int count, const SkPaint& paint) { LOG_ALWAYS_FATAL("TODO!"); } -void RecordingCanvas::drawLines(const float* points, int count, const SkPaint& paint) { - LOG_ALWAYS_FATAL("TODO!"); + +void RecordingCanvas::drawLines(const float* points, int floatCount, const SkPaint& paint) { + if (floatCount < 4) return; + floatCount &= ~0x3; // round down to nearest four + + Rect unmappedBounds(points[0], points[1], points[0], points[1]); + for (int i = 2; i < floatCount; i += 2) { + unmappedBounds.left = std::min(unmappedBounds.left, points[i]); + unmappedBounds.right = std::max(unmappedBounds.right, points[i]); + unmappedBounds.top = std::min(unmappedBounds.top, points[i + 1]); + unmappedBounds.bottom = std::max(unmappedBounds.bottom, points[i + 1]); + } + + // since anything AA stroke with less than 1.0 pixel width is drawn with an alpha-reduced + // 1.0 stroke, treat 1.0 as minimum. + unmappedBounds.outset(std::max(paint.getStrokeWidth(), 1.0f) * 0.5f); + + addOp(new (alloc()) LinesOp( + unmappedBounds, + *mState.currentSnapshot()->transform, + mState.getRenderTargetClipBounds(), + refPaint(&paint), refBuffer(points, floatCount), floatCount)); } + void RecordingCanvas::drawRect(float left, float top, float right, float bottom, const SkPaint& paint) { addOp(new (alloc()) RectOp( Rect(left, top, right, bottom), @@ -388,17 +406,24 @@ void RecordingCanvas::drawNinePatch(const SkBitmap& bitmap, const android::Res_p } // Text -void RecordingCanvas::drawText(const uint16_t* glyphs, const float* positions, int count, +void RecordingCanvas::drawText(const uint16_t* glyphs, const float* positions, int glyphCount, const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, float totalAdvance) { - LOG_ALWAYS_FATAL("TODO!"); -} -void RecordingCanvas::drawPosText(const uint16_t* text, const float* positions, int count, - int posCount, const SkPaint& paint) { - LOG_ALWAYS_FATAL("TODO!"); + if (!glyphs || !positions || glyphCount <= 0 || PaintUtils::paintWillNotDrawText(paint)) return; + glyphs = refBuffer(glyphs, glyphCount); + positions = refBuffer(positions, glyphCount * 2); + + addOp(new (alloc()) TextOp( + Rect(boundsLeft, boundsTop, boundsRight, boundsBottom), + *(mState.currentSnapshot()->transform), + mState.getRenderTargetClipBounds(), + refPaint(&paint), glyphs, positions, glyphCount, x, y)); + drawTextDecorations(x, y, totalAdvance, paint); } + void RecordingCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) { + // NOTE: can't use refPaint() directly, since it forces left alignment LOG_ALWAYS_FATAL("TODO!"); } diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index f26b0c827604..736cc9ecaf2a 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -178,8 +178,6 @@ public: virtual void drawText(const uint16_t* glyphs, const float* positions, int count, const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, float totalAdvance) override; - virtual void drawPosText(const uint16_t* text, const float* positions, int count, - int posCount, const SkPaint& paint) override; virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) override; virtual bool drawTextAbsolutePos() const override { return false; } @@ -221,6 +219,15 @@ private: return cachedPath; } + /** + * Returns a RenderThread-safe, const copy of the SkPaint parameter passed in (with deduping + * based on paint generation ID) + * + * Note that this forces Left_Align, since drawText glyph rendering expects left alignment, + * since alignment offsetting has been done at a higher level. This is done to essentially all + * copied paints, since the deduping can mean a paint is shared by drawText commands and other + * types (which wouldn't care about alignment). + */ inline const SkPaint* refPaint(const SkPaint* paint) { if (!paint) return nullptr; @@ -239,10 +246,11 @@ private: // In the unlikely event that 2 unique paints have the same hash we do a // object equality check to ensure we don't erroneously dedup them. if (cachedPaint == nullptr || *cachedPaint != *paint) { - cachedPaint = new SkPaint(*paint); - std::unique_ptr copy(cachedPaint); - mDisplayList->paints.push_back(std::move(copy)); + SkPaint* copy = new SkPaint(*paint); + copy->setTextAlign(SkPaint::kLeft_Align); + cachedPaint = copy; + mDisplayList->paints.emplace_back(copy); // replaceValueFor() performs an add if the entry doesn't exist mPaintMap.replaceValueFor(key, cachedPaint); refBitmapsInShader(cachedPaint->getShader()); diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h index 0736a109e572..472aad711432 100644 --- a/libs/hwui/Rect.h +++ b/libs/hwui/Rect.h @@ -260,13 +260,6 @@ public: bottom = std::max(bottom, y); } - void expandToCoverRect(float otherLeft, float otherTop, float otherRight, float otherBottom) { - left = std::min(left, otherLeft); - top = std::min(top, otherTop); - right = std::max(right, otherRight); - bottom = std::max(bottom, otherBottom); - } - SkRect toSkRect() const { return SkRect::MakeLTRB(left, top, right, bottom); } diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index 6d3dfac4ba9c..96c1a7c18db9 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -131,8 +131,6 @@ public: const SkPaint& paint, float x, float y, float boundsLeft, float boundsTop, float boundsRight, float boundsBottom, float totalAdvance) override; - virtual void drawPosText(const uint16_t* text, const float* positions, int count, - int posCount, const SkPaint& paint) override; virtual void drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path, float hOffset, float vOffset, const SkPaint& paint) override; @@ -152,7 +150,6 @@ private: void drawPoints(const float* points, int count, const SkPaint& paint, SkCanvas::PointMode mode); - void drawTextDecorations(float x, float y, float length, const SkPaint& paint); SkAutoTUnref mCanvas; std::unique_ptr mSaveStack; // lazily allocated, tracks partial saves. @@ -712,22 +709,7 @@ void SkiaCanvas::drawText(const uint16_t* text, const float* positions, int coun static_assert(sizeof(SkPoint) == sizeof(float)*2, "SkPoint is no longer two floats"); mCanvas->drawPosText(text, count << 1, reinterpret_cast(positions), paintCopy); -} - -void SkiaCanvas::drawPosText(const uint16_t* text, const float* positions, int count, int posCount, - const SkPaint& paint) { - SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL; - int indx; - for (indx = 0; indx < posCount; indx++) { - posPtr[indx].fX = positions[indx << 1]; - posPtr[indx].fY = positions[(indx << 1) + 1]; - } - - SkPaint paintCopy(paint); - paintCopy.setTextEncoding(SkPaint::kUTF16_TextEncoding); - mCanvas->drawPosText(text, count, posPtr, paintCopy); - - delete[] posPtr; + drawTextDecorations(x, y, totalAdvance, paint); } void SkiaCanvas::drawTextOnPath(const uint16_t* glyphs, int count, const SkPath& path, diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp index b7a76baadff5..996ac8ebc447 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -30,8 +30,7 @@ namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// hash_t ShadowText::hash() const { - uint32_t charCount = len / sizeof(char16_t); - uint32_t hash = JenkinsHashMix(0, len); + uint32_t hash = JenkinsHashMix(0, glyphCount); hash = JenkinsHashMix(hash, android::hash_type(radius)); hash = JenkinsHashMix(hash, android::hash_type(textSize)); hash = JenkinsHashMix(hash, android::hash_type(typeface)); @@ -40,10 +39,10 @@ hash_t ShadowText::hash() const { hash = JenkinsHashMix(hash, android::hash_type(scaleX)); if (text) { hash = JenkinsHashMixShorts( - hash, reinterpret_cast(text), charCount); + hash, reinterpret_cast(text), glyphCount); } if (positions) { - for (uint32_t i = 0; i < charCount * 2; i++) { + for (uint32_t i = 0; i < glyphCount * 2; i++) { hash = JenkinsHashMix(hash, android::hash_type(positions[i])); } } @@ -51,7 +50,7 @@ hash_t ShadowText::hash() const { } int ShadowText::compare(const ShadowText& lhs, const ShadowText& rhs) { - int deltaInt = int(lhs.len) - int(rhs.len); + int deltaInt = int(lhs.glyphCount) - int(rhs.glyphCount); if (deltaInt != 0) return deltaInt; deltaInt = lhs.flags - rhs.flags; @@ -76,7 +75,7 @@ int ShadowText::compare(const ShadowText& lhs, const ShadowText& rhs) { if (!lhs.text) return -1; if (!rhs.text) return +1; - deltaInt = memcmp(lhs.text, rhs.text, lhs.len); + deltaInt = memcmp(lhs.text, rhs.text, lhs.glyphCount * sizeof(glyph_t)); if (deltaInt != 0) return deltaInt; } @@ -84,7 +83,7 @@ int ShadowText::compare(const ShadowText& lhs, const ShadowText& rhs) { if (!lhs.positions) return -1; if (!rhs.positions) return +1; - return memcmp(lhs.positions, rhs.positions, lhs.len << 2); + return memcmp(lhs.positions, rhs.positions, lhs.glyphCount << 1); } return 0; @@ -168,16 +167,16 @@ void TextDropShadowCache::clear() { mCache.clear(); } -ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* text, uint32_t len, - int numGlyphs, float radius, const float* positions) { - ShadowText entry(paint, radius, len, text, positions); +ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* glyphs, int numGlyphs, + float radius, const float* positions) { + ShadowText entry(paint, radius, numGlyphs * 2, glyphs, positions); ShadowTexture* texture = mCache.get(entry); if (!texture) { SkPaint paintCopy(*paint); paintCopy.setTextAlign(SkPaint::kLeft_Align); - FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(&paintCopy, text, 0, - len, numGlyphs, radius, positions); + FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(&paintCopy, glyphs, numGlyphs, + radius, positions); if (!shadow.image) { return nullptr; diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h index caf089f6d2a5..c4f3c5d96786 100644 --- a/libs/hwui/TextDropShadowCache.h +++ b/libs/hwui/TextDropShadowCache.h @@ -34,14 +34,14 @@ class Caches; class FontRenderer; struct ShadowText { - ShadowText(): len(0), radius(0.0f), textSize(0.0f), typeface(nullptr), + ShadowText(): glyphCount(0), radius(0.0f), textSize(0.0f), typeface(nullptr), flags(0), italicStyle(0.0f), scaleX(0), text(nullptr), positions(nullptr) { } // len is the number of bytes in text - ShadowText(const SkPaint* paint, float radius, uint32_t len, const char* srcText, + ShadowText(const SkPaint* paint, float radius, uint32_t glyphCount, const char* srcText, const float* positions): - len(len), radius(radius), positions(positions) { + glyphCount(glyphCount), radius(radius), positions(positions) { // TODO: Propagate this through the API, we should not cast here text = (const char16_t*) srcText; @@ -73,17 +73,16 @@ struct ShadowText { } void copyTextLocally() { - uint32_t charCount = len / sizeof(char16_t); - str.setTo((const char16_t*) text, charCount); + str.setTo((const char16_t*) text, glyphCount); text = str.string(); if (positions != nullptr) { positionsCopy.clear(); - positionsCopy.appendArray(positions, charCount * 2); + positionsCopy.appendArray(positions, glyphCount * 2); positions = positionsCopy.array(); } } - uint32_t len; + uint32_t glyphCount; float radius; float textSize; SkTypeface* typeface; @@ -136,7 +135,7 @@ public: */ void operator()(ShadowText& text, ShadowTexture*& texture) override; - ShadowTexture* get(const SkPaint* paint, const char* text, uint32_t len, + ShadowTexture* get(const SkPaint* paint, const char* text, int numGlyphs, float radius, const float* positions); /** diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp index d680f990a0be..dc82041e8f89 100644 --- a/libs/hwui/font/Font.cpp +++ b/libs/hwui/font/Font.cpp @@ -291,20 +291,18 @@ CachedGlyphInfo* Font::getCachedGlyph(const SkPaint* paint, glyph_t textUnit, bo return cachedGlyph; } -void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len, +void Font::render(const SkPaint* paint, const char *text, int numGlyphs, int x, int y, const float* positions) { - render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, nullptr, + render(paint, text, numGlyphs, x, y, FRAMEBUFFER, nullptr, 0, 0, nullptr, positions); } -void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len, - int numGlyphs, const SkPath* path, float hOffset, float vOffset) { - if (numGlyphs == 0 || text == nullptr || len == 0) { +void Font::render(const SkPaint* paint, const char *text, int numGlyphs, + const SkPath* path, float hOffset, float vOffset) { + if (numGlyphs == 0 || text == nullptr) { return; } - text += start; - int glyphsCount = 0; SkFixed prevRsbDelta = 0; @@ -317,7 +315,7 @@ void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32 float pathLength = SkScalarToFloat(measure.getLength()); if (paint->getTextAlign() != SkPaint::kLeft_Align) { - float textWidth = SkScalarToFloat(paint->measureText(text, len)); + float textWidth = SkScalarToFloat(paint->measureText(text, numGlyphs * 2)); float pathOffset = pathLength; if (paint->getTextAlign() == SkPaint::kCenter_Align) { textWidth *= 0.5f; @@ -347,14 +345,14 @@ void Font::render(const SkPaint* paint, const char *text, uint32_t start, uint32 } } -void Font::measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, +void Font::measure(const SkPaint* paint, const char* text, int numGlyphs, Rect *bounds, const float* positions) { if (bounds == nullptr) { ALOGE("No return rectangle provided to measure text"); return; } bounds->set(1e6, -1e6, -1e6, 1e6); - render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions); + render(paint, text, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions); } void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) { @@ -378,10 +376,10 @@ void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) { } } -void Font::render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, +void Font::render(const SkPaint* paint, const char* text, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) { - if (numGlyphs == 0 || text == nullptr || len == 0) { + if (numGlyphs == 0 || text == nullptr) { return; } @@ -395,7 +393,6 @@ void Font::render(const SkPaint* paint, const char* text, uint32_t start, uint32 }; RenderGlyph render = gRenderGlyph[(mode << 1) + !mIdentityTransform]; - text += start; int glyphsCount = 0; while (glyphsCount < numGlyphs) { diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h index 3119d734bc2b..59518a1fb8ee 100644 --- a/libs/hwui/font/Font.h +++ b/libs/hwui/font/Font.h @@ -82,10 +82,10 @@ public: ~Font(); - void render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, + void render(const SkPaint* paint, const char* text, int numGlyphs, int x, int y, const float* positions); - void render(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, + void render(const SkPaint* paint, const char* text, int numGlyphs, const SkPath* path, float hOffset, float vOffset); const Font::FontDescription& getDescription() const { @@ -113,11 +113,11 @@ private: void precache(const SkPaint* paint, const char* text, int numGlyphs); - void render(const SkPaint* paint, const char *text, uint32_t start, uint32_t len, + void render(const SkPaint* paint, const char *text, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions); - void measure(const SkPaint* paint, const char* text, uint32_t start, uint32_t len, + void measure(const SkPaint* paint, const char* text, int numGlyphs, Rect *bounds, const float* positions); void invalidateTextureCache(CacheTexture* cacheTexture = nullptr); diff --git a/libs/hwui/unit_tests/OpReordererTests.cpp b/libs/hwui/unit_tests/OpReordererTests.cpp index ec8048dae014..d76086c9cfcd 100644 --- a/libs/hwui/unit_tests/OpReordererTests.cpp +++ b/libs/hwui/unit_tests/OpReordererTests.cpp @@ -136,14 +136,14 @@ TEST(OpReorderer, simpleRejection) { } TEST(OpReorderer, simpleBatching) { - static int SIMPLE_BATCHING_LOOPS = 5; + const int LOOPS = 5; class SimpleBatchingTestRenderer : public TestRendererBase { public: void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { - EXPECT_TRUE(mIndex++ >= SIMPLE_BATCHING_LOOPS); + EXPECT_TRUE(mIndex++ >= LOOPS) << "Bitmaps should be above all rects"; } void onRectOp(const RectOp& op, const BakedOpState& state) override { - EXPECT_TRUE(mIndex++ < SIMPLE_BATCHING_LOOPS); + EXPECT_TRUE(mIndex++ < LOOPS) << "Rects should be below all bitmaps"; } }; @@ -153,7 +153,7 @@ TEST(OpReorderer, simpleBatching) { // Alternate between drawing rects and bitmaps, with bitmaps overlapping rects. // Rects don't overlap bitmaps, so bitmaps should be brought to front as a group. canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); - for (int i = 0; i < SIMPLE_BATCHING_LOOPS; i++) { + for (int i = 0; i < LOOPS; i++) { canvas.translate(0, 10); canvas.drawRect(0, 0, 10, 10, SkPaint()); canvas.drawBitmap(bitmap, 5, 0, nullptr); @@ -164,7 +164,35 @@ TEST(OpReorderer, simpleBatching) { OpReorderer reorderer(200, 200, *dl, sLightCenter); SimpleBatchingTestRenderer renderer; reorderer.replayBakedOps(renderer); - EXPECT_EQ(2 * SIMPLE_BATCHING_LOOPS, renderer.getIndex()); // 2 x loops ops, because no merging (TODO: force no merging) + EXPECT_EQ(2 * LOOPS, renderer.getIndex()) + << "Expect number of ops = 2 * loop count"; // TODO: force no merging +} + +TEST(OpReorderer, textStrikethroughBatching) { + const int LOOPS = 5; + class TextStrikethroughTestRenderer : public TestRendererBase { + public: + void onRectOp(const RectOp& op, const BakedOpState& state) override { + EXPECT_TRUE(mIndex++ >= LOOPS) << "Strikethrough rects should be above all text"; + } + void onTextOp(const TextOp& op, const BakedOpState& state) override { + EXPECT_TRUE(mIndex++ < LOOPS) << "Text should be beneath all strikethrough rects"; + } + }; + auto dl = TestUtils::createDisplayList(200, 2000, [](RecordingCanvas& canvas) { + SkPaint textPaint; + textPaint.setAntiAlias(true); + textPaint.setTextSize(20); + textPaint.setStrikeThruText(true); + for (int i = 0; i < LOOPS; i++) { + TestUtils::drawTextToCanvas(&canvas, "test text", textPaint, 10, 100 * (i + 1)); + } + }); + OpReorderer reorderer(200, 2000, *dl, sLightCenter); + TextStrikethroughTestRenderer renderer; + reorderer.replayBakedOps(renderer); + EXPECT_EQ(2 * LOOPS, renderer.getIndex()) + << "Expect number of ops = 2 * loop count"; // TODO: force no merging } TEST(OpReorderer, renderNode) { diff --git a/libs/hwui/unit_tests/RecordingCanvasTests.cpp b/libs/hwui/unit_tests/RecordingCanvasTests.cpp index 22190f50cbe5..c23d47e3fab4 100644 --- a/libs/hwui/unit_tests/RecordingCanvasTests.cpp +++ b/libs/hwui/unit_tests/RecordingCanvasTests.cpp @@ -41,21 +41,110 @@ TEST(RecordingCanvas, emptyPlayback) { playbackOps(*dl, [](const RecordedOp& op) { ADD_FAILURE(); }); } -TEST(RecordingCanvas, testSimpleRectRecord) { +TEST(RecordingCanvas, drawLines) { + auto dl = TestUtils::createDisplayList(100, 200, [](RecordingCanvas& canvas) { + SkPaint paint; + paint.setStrokeWidth(20); + float points[] = { 0, 0, 20, 10, 30, 40, 90 }; // NB: only 1 valid line + canvas.drawLines(&points[0], 7, paint); + }); + + ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op"; + auto op = dl->getOps()[0]; + ASSERT_EQ(RecordedOpId::LinesOp, op->opId); + EXPECT_EQ(4, ((LinesOp*)op)->floatCount) + << "float count must be rounded down to closest multiple of 4"; + EXPECT_EQ(Rect(-10, -10, 30, 20), op->unmappedBounds) + << "unmapped bounds must be size of line, outset by 1/2 stroke width"; +} + +TEST(RecordingCanvas, drawRect) { auto dl = TestUtils::createDisplayList(100, 200, [](RecordingCanvas& canvas) { canvas.drawRect(10, 20, 90, 180, SkPaint()); }); + ASSERT_EQ(1u, dl->getOps().size()) << "Must be exactly one op"; + auto op = *(dl->getOps()[0]); + ASSERT_EQ(RecordedOpId::RectOp, op.opId); + EXPECT_EQ(Rect(0, 0, 100, 200), op.localClipRect); + EXPECT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds); +} + +TEST(RecordingCanvas, drawText) { + auto dl = TestUtils::createDisplayList(200, 200, [](RecordingCanvas& canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setTextSize(20); + TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25); + }); + int count = 0; playbackOps(*dl, [&count](const RecordedOp& op) { count++; - ASSERT_EQ(RecordedOpId::RectOp, op.opId); - ASSERT_EQ(Rect(0, 0, 100, 200), op.localClipRect); - ASSERT_EQ(Rect(10, 20, 90, 180), op.unmappedBounds); + ASSERT_EQ(RecordedOpId::TextOp, op.opId); + EXPECT_EQ(Rect(0, 0, 200, 200), op.localClipRect); + EXPECT_TRUE(op.localMatrix.isIdentity()); + EXPECT_TRUE(op.unmappedBounds.contains(25, 15, 50, 25)) + << "Op expected to be 25+ pixels wide, 10+ pixels tall"; }); ASSERT_EQ(1, count); } +TEST(RecordingCanvas, drawText_strikeThruAndUnderline) { + auto dl = TestUtils::createDisplayList(200, 200, [](RecordingCanvas& canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setTextSize(20); + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + paint.setUnderlineText(i != 0); + paint.setStrikeThruText(j != 0); + TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25); + } + } + }); + + auto ops = dl->getOps(); + ASSERT_EQ(8u, ops.size()); + + int index = 0; + EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); // no underline or strikethrough + + EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); + EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough only + + EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); + EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline only + + EXPECT_EQ(RecordedOpId::TextOp, ops[index++]->opId); + EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // underline + EXPECT_EQ(RecordedOpId::RectOp, ops[index++]->opId); // strikethrough +} + +TEST(RecordingCanvas, drawText_forceAlignLeft) { + auto dl = TestUtils::createDisplayList(200, 200, [](RecordingCanvas& canvas) { + SkPaint paint; + paint.setAntiAlias(true); + paint.setTextSize(20); + paint.setTextAlign(SkPaint::kLeft_Align); + TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25); + paint.setTextAlign(SkPaint::kCenter_Align); + TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25); + paint.setTextAlign(SkPaint::kRight_Align); + TestUtils::drawTextToCanvas(&canvas, "test text", paint, 25, 25); + }); + + int count = 0; + playbackOps(*dl, [&count](const RecordedOp& op) { + count++; + ASSERT_EQ(RecordedOpId::TextOp, op.opId); + EXPECT_EQ(SkPaint::kLeft_Align, op.paint->getTextAlign()) + << "recorded drawText commands must force kLeft_Align on their paint"; + EXPECT_EQ(SkPaint::kGlyphID_TextEncoding, op.paint->getTextEncoding()); // verify TestUtils + }); + ASSERT_EQ(3, count); +} + TEST(RecordingCanvas, backgroundAndImage) { auto dl = TestUtils::createDisplayList(100, 200, [](RecordingCanvas& canvas) { SkBitmap bitmap; @@ -109,7 +198,7 @@ TEST(RecordingCanvas, backgroundAndImage) { ASSERT_EQ(2, count); } -TEST(RecordingCanvas, saveLayerSimple) { +TEST(RecordingCanvas, saveLayer_simple) { auto dl = TestUtils::createDisplayList(200, 200, [](RecordingCanvas& canvas) { canvas.saveLayerAlpha(10, 20, 190, 180, 128, SkCanvas::kARGB_ClipLayer_SaveFlag); canvas.drawRect(10, 20, 190, 180, SkPaint()); @@ -143,7 +232,7 @@ TEST(RecordingCanvas, saveLayerSimple) { EXPECT_EQ(3, count); } -TEST(RecordingCanvas, saveLayerViewportCrop) { +TEST(RecordingCanvas, saveLayer_viewportCrop) { auto dl = TestUtils::createDisplayList(200, 200, [](RecordingCanvas& canvas) { // shouldn't matter, since saveLayer will clip to its bounds canvas.clipRect(-1000, -1000, 1000, 1000, SkRegion::kReplace_Op); @@ -167,7 +256,7 @@ TEST(RecordingCanvas, saveLayerViewportCrop) { EXPECT_EQ(3, count); } -TEST(RecordingCanvas, saveLayerRotateUnclipped) { +TEST(RecordingCanvas, saveLayer_rotateUnclipped) { auto dl = TestUtils::createDisplayList(200, 200, [](RecordingCanvas& canvas) { canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); canvas.translate(100, 100); @@ -193,7 +282,7 @@ TEST(RecordingCanvas, saveLayerRotateUnclipped) { EXPECT_EQ(3, count); } -TEST(RecordingCanvas, saveLayerRotateClipped) { +TEST(RecordingCanvas, saveLayer_rotateClipped) { auto dl = TestUtils::createDisplayList(200, 200, [](RecordingCanvas& canvas) { canvas.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); canvas.translate(100, 100); @@ -224,7 +313,7 @@ TEST(RecordingCanvas, saveLayerRotateClipped) { EXPECT_EQ(3, count); } -TEST(RecordingCanvas, testReorderBarrier) { +TEST(RecordingCanvas, insertReorderBarrier) { auto dl = TestUtils::createDisplayList(200, 200, [](RecordingCanvas& canvas) { canvas.drawRect(0, 0, 400, 400, SkPaint()); canvas.insertReorderBarrier(true); diff --git a/libs/hwui/utils/TestUtils.cpp b/libs/hwui/utils/TestUtils.cpp index 84230a72f1c2..dd6fc36b8042 100644 --- a/libs/hwui/utils/TestUtils.cpp +++ b/libs/hwui/utils/TestUtils.cpp @@ -36,5 +36,46 @@ SkColor TestUtils::interpolateColor(float fraction, SkColor start, SkColor end) | (int)((startB + (int)(fraction * (endB - startB)))); } +void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text, + const SkPaint& inPaint, float x, float y) { + // copy to force TextEncoding (which JNI layer would have done) + SkPaint paint(inPaint); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + + SkMatrix identity; + identity.setIdentity(); + SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry); + SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &identity); + + float totalAdvance = 0; + std::vector glyphs; + std::vector positions; + Rect bounds; + while (*text != '\0') { + SkUnichar unichar = SkUTF8_NextUnichar(&text); + glyph_t glyph = autoCache.getCache()->unicharToGlyph(unichar); + autoCache.getCache()->unicharToGlyph(unichar); + + // push glyph and its relative position + glyphs.push_back(glyph); + positions.push_back(totalAdvance); + positions.push_back(0); + + // compute bounds + SkGlyph skGlyph = autoCache.getCache()->getUnicharMetrics(unichar); + Rect glyphBounds(skGlyph.fWidth, skGlyph.fHeight); + glyphBounds.translate(totalAdvance + skGlyph.fLeft, skGlyph.fTop); + bounds.unionWith(glyphBounds); + + // advance next character + SkScalar skWidth; + paint.getTextWidths(&glyph, sizeof(glyph), &skWidth, NULL); + totalAdvance += skWidth; + } + bounds.translate(x, y); + canvas->drawText(glyphs.data(), positions.data(), glyphs.size(), paint, x, y, + bounds.left, bounds.top, bounds.right, bounds.bottom, totalAdvance); +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/utils/TestUtils.h b/libs/hwui/utils/TestUtils.h index f7f4f2d1ca10..f9fa242ec833 100644 --- a/libs/hwui/utils/TestUtils.h +++ b/libs/hwui/utils/TestUtils.h @@ -196,6 +196,9 @@ public: static SkColor interpolateColor(float fraction, SkColor start, SkColor end); + static void drawTextToCanvas(TestCanvas* canvas, const char* text, + const SkPaint& inPaint, float x, float y); + private: static void syncHierarchyPropertiesAndDisplayListImpl(RenderNode* node) { node->syncProperties(); -- cgit v1.2.3-59-g8ed1b From 086847797142a25e9e21611e9864c53abfca174f Mon Sep 17 00:00:00 2001 From: Chris Craik Date: Fri, 4 Dec 2015 09:16:58 -0800 Subject: Correct shadowtext glyph count bug:25837773 Change-Id: Id890624ddce3e5a331ecef207011709be0daf703 --- libs/hwui/TextDropShadowCache.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'libs/hwui/TextDropShadowCache.cpp') diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp index 996ac8ebc447..51f16523966b 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -83,7 +83,7 @@ int ShadowText::compare(const ShadowText& lhs, const ShadowText& rhs) { if (!lhs.positions) return -1; if (!rhs.positions) return +1; - return memcmp(lhs.positions, rhs.positions, lhs.glyphCount << 1); + return memcmp(lhs.positions, rhs.positions, lhs.glyphCount * sizeof(float) * 2); } return 0; @@ -169,7 +169,7 @@ void TextDropShadowCache::clear() { ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* glyphs, int numGlyphs, float radius, const float* positions) { - ShadowText entry(paint, radius, numGlyphs * 2, glyphs, positions); + ShadowText entry(paint, radius, numGlyphs, glyphs, positions); ShadowTexture* texture = mCache.get(entry); if (!texture) { -- cgit v1.2.3-59-g8ed1b From 38e0c32852e3b9d8ca4a9d3791577f52536419cb Mon Sep 17 00:00:00 2001 From: John Reck Date: Tue, 10 Nov 2015 12:19:17 -0800 Subject: Track texture memory globally Also mostly consolidates texture creation Change-Id: Ifea01303afda531dcec99b8fe2a0f64cf2f24420 --- libs/hwui/Android.mk | 2 + libs/hwui/AssetAtlas.cpp | 57 +++--- libs/hwui/AssetAtlas.h | 31 ++-- libs/hwui/BakedOpDispatcher.cpp | 14 +- libs/hwui/BakedOpRenderer.cpp | 6 +- libs/hwui/GpuMemoryTracker.cpp | 140 +++++++++++++++ libs/hwui/GpuMemoryTracker.h | 76 ++++++++ libs/hwui/GradientCache.cpp | 24 +-- libs/hwui/GradientCache.h | 3 +- libs/hwui/Layer.cpp | 35 ++-- libs/hwui/Layer.h | 17 +- libs/hwui/LayerCache.cpp | 1 - libs/hwui/OpenGLRenderer.cpp | 18 +- libs/hwui/PathCache.cpp | 33 +--- libs/hwui/PathCache.h | 4 +- libs/hwui/SkiaShader.cpp | 6 +- libs/hwui/TextDropShadowCache.cpp | 9 +- libs/hwui/Texture.cpp | 208 +++++++++++++++++++++- libs/hwui/Texture.h | 85 +++++++-- libs/hwui/TextureCache.cpp | 9 +- libs/hwui/TextureCache.h | 1 + libs/hwui/font/CacheTexture.cpp | 28 +-- libs/hwui/font/CacheTexture.h | 7 +- libs/hwui/renderstate/OffscreenBufferPool.cpp | 31 ++-- libs/hwui/renderstate/OffscreenBufferPool.h | 10 +- libs/hwui/renderstate/RenderState.cpp | 8 +- libs/hwui/renderstate/TextureState.cpp | 128 ------------- libs/hwui/renderstate/TextureState.h | 7 - libs/hwui/renderthread/CanvasContext.cpp | 3 + libs/hwui/tests/common/TestUtils.h | 4 + libs/hwui/tests/macrobench/main.cpp | 1 + libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp | 70 ++++++++ libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp | 12 +- libs/hwui/tests/unit/StringUtilsTests.cpp | 15 ++ libs/hwui/utils/StringUtils.h | 17 ++ 35 files changed, 743 insertions(+), 377 deletions(-) create mode 100644 libs/hwui/GpuMemoryTracker.cpp create mode 100644 libs/hwui/GpuMemoryTracker.h create mode 100644 libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp (limited to 'libs/hwui/TextDropShadowCache.cpp') diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index bbfc0221666c..cdd891fa5644 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -53,6 +53,7 @@ hwui_src_files := \ FrameInfoVisualizer.cpp \ GammaFontRenderer.cpp \ GlopBuilder.cpp \ + GpuMemoryTracker.cpp \ GradientCache.cpp \ Image.cpp \ Interpolator.cpp \ @@ -228,6 +229,7 @@ LOCAL_SRC_FILES += \ tests/unit/DamageAccumulatorTests.cpp \ tests/unit/DeviceInfoTests.cpp \ tests/unit/FatVectorTests.cpp \ + tests/unit/GpuMemoryTrackerTests.cpp \ tests/unit/LayerUpdateQueueTests.cpp \ tests/unit/LinearAllocatorTests.cpp \ tests/unit/VectorDrawableTests.cpp \ diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp index 41411a98a4bf..6afff1b7158e 100644 --- a/libs/hwui/AssetAtlas.cpp +++ b/libs/hwui/AssetAtlas.cpp @@ -39,40 +39,22 @@ void AssetAtlas::init(sp buffer, int64_t* map, int count) { if (!mTexture) { Caches& caches = Caches::getInstance(); mTexture = new Texture(caches); - mTexture->width = buffer->getWidth(); - mTexture->height = buffer->getHeight(); + mTexture->wrap(mImage->getTexture(), + buffer->getWidth(), buffer->getHeight(), GL_RGBA); createEntries(caches, map, count); } } else { ALOGW("Could not create atlas image"); - delete mImage; - mImage = nullptr; + terminate(); } - - updateTextureId(); } void AssetAtlas::terminate() { - if (mImage) { - delete mImage; - mImage = nullptr; - updateTextureId(); - } -} - - -void AssetAtlas::updateTextureId() { - mTexture->id = mImage ? mImage->getTexture() : 0; - if (mTexture->id) { - // Texture ID changed, force-set to defaults to sync the wrapper & GL - // state objects - mTexture->setWrap(GL_CLAMP_TO_EDGE, false, true); - mTexture->setFilter(GL_NEAREST, false, true); - } - for (size_t i = 0; i < mEntries.size(); i++) { - AssetAtlas::Entry* entry = mEntries.valueAt(i); - entry->texture->id = mTexture->id; - } + delete mImage; + mImage = nullptr; + delete mTexture; + mTexture = nullptr; + mEntries.clear(); } /////////////////////////////////////////////////////////////////////////////// @@ -80,13 +62,13 @@ void AssetAtlas::updateTextureId() { /////////////////////////////////////////////////////////////////////////////// AssetAtlas::Entry* AssetAtlas::getEntry(const SkPixelRef* pixelRef) const { - ssize_t index = mEntries.indexOfKey(pixelRef); - return index >= 0 ? mEntries.valueAt(index) : nullptr; + auto result = mEntries.find(pixelRef); + return result != mEntries.end() ? result->second.get() : nullptr; } Texture* AssetAtlas::getEntryTexture(const SkPixelRef* pixelRef) const { - ssize_t index = mEntries.indexOfKey(pixelRef); - return index >= 0 ? mEntries.valueAt(index)->texture : nullptr; + auto result = mEntries.find(pixelRef); + return result != mEntries.end() ? result->second->texture : nullptr; } /** @@ -94,7 +76,8 @@ Texture* AssetAtlas::getEntryTexture(const SkPixelRef* pixelRef) const { * instead of applying the changes to the virtual textures. */ struct DelegateTexture: public Texture { - DelegateTexture(Caches& caches, Texture* delegate): Texture(caches), mDelegate(delegate) { } + DelegateTexture(Caches& caches, Texture* delegate) + : Texture(caches), mDelegate(delegate) { } virtual void setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture = false, bool force = false, GLenum renderTarget = GL_TEXTURE_2D) override { @@ -111,8 +94,8 @@ private: }; // struct DelegateTexture void AssetAtlas::createEntries(Caches& caches, int64_t* map, int count) { - const float width = float(mTexture->width); - const float height = float(mTexture->height); + const float width = float(mTexture->width()); + const float height = float(mTexture->height()); for (int i = 0; i < count; ) { SkPixelRef* pixelRef = reinterpret_cast(map[i++]); @@ -133,13 +116,13 @@ void AssetAtlas::createEntries(Caches& caches, int64_t* map, int count) { Texture* texture = new DelegateTexture(caches, mTexture); texture->blend = !SkAlphaTypeIsOpaque(pixelRef->info().alphaType()); - texture->width = pixelRef->info().width(); - texture->height = pixelRef->info().height(); + texture->wrap(mTexture->id(), pixelRef->info().width(), + pixelRef->info().height(), mTexture->format()); - Entry* entry = new Entry(pixelRef, texture, mapper, *this); + std::unique_ptr entry(new Entry(pixelRef, texture, mapper, *this)); texture->uvMapper = &entry->uvMapper; - mEntries.add(entry->pixelRef, entry); + mEntries.emplace(entry->pixelRef, std::move(entry)); } } diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h index a037725b1c6c..75400ff494c3 100644 --- a/libs/hwui/AssetAtlas.h +++ b/libs/hwui/AssetAtlas.h @@ -17,18 +17,16 @@ #ifndef ANDROID_HWUI_ASSET_ATLAS_H #define ANDROID_HWUI_ASSET_ATLAS_H -#include - -#include - -#include +#include "Texture.h" +#include "UvMapper.h" #include - +#include +#include #include -#include "Texture.h" -#include "UvMapper.h" +#include +#include namespace android { namespace uirenderer { @@ -71,6 +69,10 @@ public: return texture->blend ? &atlas.mBlendKey : &atlas.mOpaqueKey; } + ~Entry() { + delete texture; + } + private: /** * The pixel ref that generated this atlas entry. @@ -90,10 +92,6 @@ public: , atlas(atlas) { } - ~Entry() { - delete texture; - } - friend class AssetAtlas; }; @@ -127,7 +125,7 @@ public: * Can return 0 if the atlas is not initialized. */ uint32_t getWidth() const { - return mTexture ? mTexture->width : 0; + return mTexture ? mTexture->width() : 0; } /** @@ -135,7 +133,7 @@ public: * Can return 0 if the atlas is not initialized. */ uint32_t getHeight() const { - return mTexture ? mTexture->height : 0; + return mTexture ? mTexture->height() : 0; } /** @@ -143,7 +141,7 @@ public: * Can return 0 if the atlas is not initialized. */ GLuint getTexture() const { - return mTexture ? mTexture->id : 0; + return mTexture ? mTexture->id() : 0; } /** @@ -160,7 +158,6 @@ public: private: void createEntries(Caches& caches, int64_t* map, int count); - void updateTextureId(); Texture* mTexture; Image* mImage; @@ -168,7 +165,7 @@ private: const bool mBlendKey; const bool mOpaqueKey; - KeyedVector mEntries; + std::unordered_map> mEntries; }; // class AssetAtlas }; // namespace uirenderer diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp index 5b34f6b79199..6b0c1499336f 100644 --- a/libs/hwui/BakedOpDispatcher.cpp +++ b/libs/hwui/BakedOpDispatcher.cpp @@ -217,7 +217,7 @@ static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRender .setMeshTexturedUnitQuad(nullptr) .setFillShadowTexturePaint(*texture, textShadow.color, *op.paint, state.alpha) .setTransform(state.computedState.transform, TransformFlags::None) - .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height)) + .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height())) .build(); renderer.renderGlop(state, glop); } @@ -337,7 +337,7 @@ static void renderConvexPath(BakedOpRenderer& renderer, const BakedOpState& stat static void renderPathTexture(BakedOpRenderer& renderer, const BakedOpState& state, PathTexture& texture, const RecordedOp& op) { - Rect dest(texture.width, texture.height); + Rect dest(texture.width(), texture.height()); dest.translate(texture.left - texture.offset, texture.top - texture.offset); Glop glop; @@ -399,7 +399,7 @@ void BakedOpDispatcher::onBitmapOp(BakedOpRenderer& renderer, const BitmapOp& op .setMeshTexturedUnitQuad(texture->uvMapper) .setFillTexturePaint(*texture, textureFillFlags, op.paint, state.alpha) .setTransform(state.computedState.transform, TransformFlags::None) - .setModelViewMapUnitToRectSnap(Rect(texture->width, texture->height)) + .setModelViewMapUnitToRectSnap(Rect(texture->width(), texture->height())) .build(); renderer.renderGlop(state, glop); } @@ -483,10 +483,10 @@ void BakedOpDispatcher::onBitmapRectOp(BakedOpRenderer& renderer, const BitmapRe if (!texture) return; const AutoTexture autoCleanup(texture); - Rect uv(std::max(0.0f, op.src.left / texture->width), - std::max(0.0f, op.src.top / texture->height), - std::min(1.0f, op.src.right / texture->width), - std::min(1.0f, op.src.bottom / texture->height)); + Rect uv(std::max(0.0f, op.src.left / texture->width()), + std::max(0.0f, op.src.top / texture->height()), + std::min(1.0f, op.src.right / texture->width()), + std::min(1.0f, op.src.bottom / texture->height())); const int textureFillFlags = (op.bitmap->colorType() == kAlpha_8_SkColorType) ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None; diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp index 42fb66fe9845..4fbff0d107ba 100644 --- a/libs/hwui/BakedOpRenderer.cpp +++ b/libs/hwui/BakedOpRenderer.cpp @@ -48,7 +48,7 @@ void BakedOpRenderer::startRepaintLayer(OffscreenBuffer* offscreenBuffer, const // attach the texture to the FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - offscreenBuffer->texture.id, 0); + offscreenBuffer->texture.id(), 0); LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(), "startLayer FAILED"); LOG_ALWAYS_FATAL_IF(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, "framebuffer incomplete!"); @@ -84,7 +84,7 @@ OffscreenBuffer* BakedOpRenderer::copyToLayer(const Rect& area) { area.getWidth(), area.getHeight()); if (!area.isEmpty()) { mCaches.textureState().activateTexture(0); - mCaches.textureState().bindTexture(buffer->texture.id); + mCaches.textureState().bindTexture(buffer->texture.id()); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, area.left, mRenderTarget.viewportHeight - area.bottom, @@ -272,7 +272,7 @@ void BakedOpRenderer::prepareRender(const Rect* dirtyBounds, const ClipBase* cli OffscreenBuffer* layer = mRenderTarget.offscreenBuffer; mRenderTarget.stencil = mCaches.renderBufferCache.get( Stencil::getLayerStencilFormat(), - layer->texture.width, layer->texture.height); + layer->texture.width(), layer->texture.height()); // stencil is bound + allocated - associate it with current FBO glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mRenderTarget.stencil->getName()); diff --git a/libs/hwui/GpuMemoryTracker.cpp b/libs/hwui/GpuMemoryTracker.cpp new file mode 100644 index 000000000000..4fb57019264d --- /dev/null +++ b/libs/hwui/GpuMemoryTracker.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2016 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 "utils/StringUtils.h" +#include "Texture.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace android { +namespace uirenderer { + +pthread_t gGpuThread = 0; + +#define NUM_TYPES static_cast(GpuObjectType::TypeCount) + +const char* TYPE_NAMES[] = { + "Texture", + "OffscreenBuffer", + "Layer", +}; + +struct TypeStats { + int totalSize = 0; + int count = 0; +}; + +static std::array gObjectStats; +static std::unordered_set gObjectSet; + +void GpuMemoryTracker::notifySizeChanged(int newSize) { + int delta = newSize - mSize; + mSize = newSize; + gObjectStats[static_cast(mType)].totalSize += delta; +} + +void GpuMemoryTracker::startTrackingObject() { + auto result = gObjectSet.insert(this); + LOG_ALWAYS_FATAL_IF(!result.second, + "startTrackingObject() on %p failed, already being tracked!", this); + gObjectStats[static_cast(mType)].count++; +} + +void GpuMemoryTracker::stopTrackingObject() { + size_t removed = gObjectSet.erase(this); + LOG_ALWAYS_FATAL_IF(removed != 1, + "stopTrackingObject removed %zd, is %p not being tracked?", + removed, this); + gObjectStats[static_cast(mType)].count--; +} + +void GpuMemoryTracker::onGLContextCreated() { + LOG_ALWAYS_FATAL_IF(gGpuThread != 0, "We already have a GL thread? " + "current = %lu, gl thread = %lu", pthread_self(), gGpuThread); + gGpuThread = pthread_self(); +} + +void GpuMemoryTracker::onGLContextDestroyed() { + gGpuThread = 0; + if (CC_UNLIKELY(gObjectSet.size() > 0)) { + std::stringstream os; + dump(os); + ALOGE("%s", os.str().c_str()); + LOG_ALWAYS_FATAL("Leaked %zd GPU objects!", gObjectSet.size()); + } +} + +void GpuMemoryTracker::dump() { + std::stringstream strout; + dump(strout); + ALOGD("%s", strout.str().c_str()); +} + +void GpuMemoryTracker::dump(std::ostream& stream) { + for (int type = 0; type < NUM_TYPES; type++) { + const TypeStats& stats = gObjectStats[type]; + stream << TYPE_NAMES[type]; + stream << " is using " << SizePrinter{stats.totalSize}; + stream << ", count = " << stats.count; + stream << std::endl; + } +} + +int GpuMemoryTracker::getInstanceCount(GpuObjectType type) { + return gObjectStats[static_cast(type)].count; +} + +int GpuMemoryTracker::getTotalSize(GpuObjectType type) { + return gObjectStats[static_cast(type)].totalSize; +} + +void GpuMemoryTracker::onFrameCompleted() { + if (ATRACE_ENABLED()) { + char buf[128]; + for (int type = 0; type < NUM_TYPES; type++) { + snprintf(buf, 128, "hwui_%s", TYPE_NAMES[type]); + const TypeStats& stats = gObjectStats[type]; + ATRACE_INT(buf, stats.totalSize); + snprintf(buf, 128, "hwui_%s_count", TYPE_NAMES[type]); + ATRACE_INT(buf, stats.count); + } + } + + std::vector freeList; + for (const auto& obj : gObjectSet) { + if (obj->objectType() == GpuObjectType::Texture) { + const Texture* texture = static_cast(obj); + if (texture->cleanup) { + ALOGE("Leaked texture marked for cleanup! id=%u, size %ux%u", + texture->id(), texture->width(), texture->height()); + freeList.push_back(texture); + } + } + } + for (auto& texture : freeList) { + const_cast(texture)->deleteTexture(); + delete texture; + } +} + +} // namespace uirenderer +} // namespace android; diff --git a/libs/hwui/GpuMemoryTracker.h b/libs/hwui/GpuMemoryTracker.h new file mode 100644 index 000000000000..851aeae8352c --- /dev/null +++ b/libs/hwui/GpuMemoryTracker.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016 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. + */ +#pragma once + +#include +#include +#include + +namespace android { +namespace uirenderer { + +extern pthread_t gGpuThread; + +#define ASSERT_GPU_THREAD() LOG_ALWAYS_FATAL_IF( \ + !pthread_equal(gGpuThread, pthread_self()), \ + "Error, %p of type %d (size=%d) used on wrong thread! cur thread %lu " \ + "!= gpu thread %lu", this, static_cast(mType), mSize, \ + pthread_self(), gGpuThread) + +enum class GpuObjectType { + Texture = 0, + OffscreenBuffer, + Layer, + + TypeCount, +}; + +class GpuMemoryTracker { +public: + GpuObjectType objectType() { return mType; } + int objectSize() { return mSize; } + + static void onGLContextCreated(); + static void onGLContextDestroyed(); + static void dump(); + static void dump(std::ostream& stream); + static int getInstanceCount(GpuObjectType type); + static int getTotalSize(GpuObjectType type); + static void onFrameCompleted(); + +protected: + GpuMemoryTracker(GpuObjectType type) : mType(type) { + ASSERT_GPU_THREAD(); + startTrackingObject(); + } + + ~GpuMemoryTracker() { + notifySizeChanged(0); + stopTrackingObject(); + } + + void notifySizeChanged(int newSize); + +private: + void startTrackingObject(); + void stopTrackingObject(); + + int mSize = 0; + GpuObjectType mType; +}; + +} // namespace uirenderer +} // namespace android; diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index 8c4645092c97..522aa96132dd 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -110,9 +110,7 @@ void GradientCache::setMaxSize(uint32_t maxSize) { void GradientCache::operator()(GradientCacheEntry&, Texture*& texture) { if (texture) { - const uint32_t size = texture->width * texture->height * bytesPerPixel(); - mSize -= size; - + mSize -= texture->objectSize(); texture->deleteTexture(); delete texture; } @@ -167,18 +165,16 @@ Texture* GradientCache::addLinearGradient(GradientCacheEntry& gradient, getGradientInfo(colors, count, info); Texture* texture = new Texture(Caches::getInstance()); - texture->width = info.width; - texture->height = 2; texture->blend = info.hasAlpha; texture->generation = 1; // Asume the cache is always big enough - const uint32_t size = texture->width * texture->height * bytesPerPixel(); + const uint32_t size = info.width * 2 * bytesPerPixel(); while (getSize() + size > mMaxSize) { mCache.removeOldest(); } - generateTexture(colors, positions, texture); + generateTexture(colors, positions, info.width, 2, texture); mSize += size; mCache.put(gradient, texture); @@ -231,10 +227,10 @@ void GradientCache::mixFloats(GradientColor& start, GradientColor& end, float am dst += 4 * sizeof(float); } -void GradientCache::generateTexture(uint32_t* colors, float* positions, Texture* texture) { - const uint32_t width = texture->width; +void GradientCache::generateTexture(uint32_t* colors, float* positions, + const uint32_t width, const uint32_t height, Texture* texture) { const GLsizei rowBytes = width * bytesPerPixel(); - uint8_t pixels[rowBytes * texture->height]; + uint8_t pixels[rowBytes * height]; static ChannelSplitter gSplitters[] = { &android::uirenderer::GradientCache::splitToBytes, @@ -277,17 +273,13 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, Texture* memcpy(pixels + rowBytes, pixels, rowBytes); - glGenTextures(1, &texture->id); - Caches::getInstance().textureState().bindTexture(texture->id); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); if (mUseFloatTexture) { // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, texture->height, 0, - GL_RGBA, GL_FLOAT, pixels); + texture->upload(width, height, GL_RGBA16F, GL_RGBA, GL_FLOAT, pixels); } else { - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, texture->height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, pixels); + texture->upload(width, height, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, pixels); } texture->setFilter(GL_LINEAR); diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h index 7534c5d11164..b762ca7d5e5c 100644 --- a/libs/hwui/GradientCache.h +++ b/libs/hwui/GradientCache.h @@ -143,7 +143,8 @@ private: Texture* addLinearGradient(GradientCacheEntry& gradient, uint32_t* colors, float* positions, int count); - void generateTexture(uint32_t* colors, float* positions, Texture* texture); + void generateTexture(uint32_t* colors, float* positions, + const uint32_t width, const uint32_t height, Texture* texture); struct GradientInfo { uint32_t width; diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index 0fe20ad1b662..7a74b9825d80 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -36,7 +36,8 @@ namespace android { namespace uirenderer { Layer::Layer(Type layerType, RenderState& renderState, uint32_t layerWidth, uint32_t layerHeight) - : state(State::Uncached) + : GpuMemoryTracker(GpuObjectType::Layer) + , state(State::Uncached) , caches(Caches::getInstance()) , renderState(renderState) , texture(caches) @@ -45,8 +46,8 @@ Layer::Layer(Type layerType, RenderState& renderState, uint32_t layerWidth, uint // preserves the old inc/dec ref locations. This should be changed... incStrong(nullptr); renderTarget = GL_TEXTURE_2D; - texture.width = layerWidth; - texture.height = layerHeight; + texture.mWidth = layerWidth; + texture.mHeight = layerHeight; renderState.registerLayer(this); } @@ -54,10 +55,10 @@ Layer::~Layer() { renderState.unregisterLayer(this); SkSafeUnref(colorFilter); - if (stencil || fbo || texture.id) { + if (stencil || fbo || texture.mId) { renderState.requireGLContext(); removeFbo(); - deleteTexture(); + texture.deleteTexture(); } delete[] mesh; @@ -65,7 +66,7 @@ Layer::~Layer() { void Layer::onGlContextLost() { removeFbo(); - deleteTexture(); + texture.deleteTexture(); } uint32_t Layer::computeIdealWidth(uint32_t layerWidth) { @@ -179,8 +180,8 @@ void Layer::setColorFilter(SkColorFilter* filter) { } void Layer::bindTexture() const { - if (texture.id) { - caches.textureState().bindTexture(renderTarget, texture.id); + if (texture.mId) { + caches.textureState().bindTexture(renderTarget, texture.mId); } } @@ -191,28 +192,22 @@ void Layer::bindStencilRenderBuffer() const { } void Layer::generateTexture() { - if (!texture.id) { - glGenTextures(1, &texture.id); - } -} - -void Layer::deleteTexture() { - if (texture.id) { - texture.deleteTexture(); - texture.id = 0; + if (!texture.mId) { + glGenTextures(1, &texture.mId); } } void Layer::clearTexture() { - caches.textureState().unbindTexture(texture.id); - texture.id = 0; + caches.textureState().unbindTexture(texture.mId); + texture.mId = 0; } void Layer::allocateTexture() { #if DEBUG_LAYERS ALOGD(" Allocate layer: %dx%d", getWidth(), getHeight()); #endif - if (texture.id) { + if (texture.mId) { + texture.updateSize(getWidth(), getHeight(), GL_RGBA); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index e90f055b667b..e00ae66997a5 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -24,6 +24,7 @@ #include #include +#include #include @@ -54,7 +55,7 @@ struct DeferStateStruct; /** * A layer has dimensions and is backed by an OpenGL texture or FBO. */ -class Layer : public VirtualLightRefBase { +class Layer : public VirtualLightRefBase, GpuMemoryTracker { public: enum class Type { Texture, @@ -94,8 +95,8 @@ public: regionRect.set(bounds.leftTop().x, bounds.leftTop().y, bounds.rightBottom().x, bounds.rightBottom().y); - const float texX = 1.0f / float(texture.width); - const float texY = 1.0f / float(texture.height); + const float texX = 1.0f / float(texture.mWidth); + const float texY = 1.0f / float(texture.mHeight); const float height = layer.getHeight(); texCoords.set( regionRect.left * texX, (height - regionRect.top) * texY, @@ -112,11 +113,11 @@ public: void updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom); inline uint32_t getWidth() const { - return texture.width; + return texture.mWidth; } inline uint32_t getHeight() const { - return texture.height; + return texture.mHeight; } /** @@ -131,8 +132,7 @@ public: bool resize(const uint32_t width, const uint32_t height); void setSize(uint32_t width, uint32_t height) { - texture.width = width; - texture.height = height; + texture.updateSize(width, height, texture.format()); } ANDROID_API void setPaint(const SkPaint* paint); @@ -201,7 +201,7 @@ public: } inline GLuint getTextureId() const { - return texture.id; + return texture.id(); } inline Texture& getTexture() { @@ -263,7 +263,6 @@ public: void bindTexture() const; void generateTexture(); void allocateTexture(); - void deleteTexture(); /** * When the caller frees the texture itself, the caller diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index b117754347ed..f5681ce712d5 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -112,7 +112,6 @@ Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uin layer->bindTexture(); layer->setFilter(GL_NEAREST); layer->setWrap(GL_CLAMP_TO_EDGE, false); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); #if DEBUG_LAYERS dump(); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 92b758de8200..0cd763df78d1 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include "OpenGLRenderer.h" #include "DeferredDisplayList.h" @@ -200,6 +201,7 @@ bool OpenGLRenderer::finish() { #if DEBUG_MEMORY_USAGE mCaches.dumpMemoryUsage(); + GPUMemoryTracker::dump(); #else if (Properties::debugLevel & kDebugMemory) { mCaches.dumpMemoryUsage(); @@ -1497,7 +1499,7 @@ void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkPaint* paint) { .setMeshTexturedUnitQuad(texture->uvMapper) .setFillTexturePaint(*texture, textureFillFlags, paint, currentSnapshot()->alpha) .setTransform(*currentSnapshot(), TransformFlags::None) - .setModelViewMapUnitToRectSnap(Rect(texture->width, texture->height)) + .setModelViewMapUnitToRectSnap(Rect(texture->width(), texture->height())) .build(); renderGlop(glop); } @@ -1601,10 +1603,10 @@ void OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, Rect src, Rect dst, cons if (!texture) return; const AutoTexture autoCleanup(texture); - Rect uv(std::max(0.0f, src.left / texture->width), - std::max(0.0f, src.top / texture->height), - std::min(1.0f, src.right / texture->width), - std::min(1.0f, src.bottom / texture->height)); + Rect uv(std::max(0.0f, src.left / texture->width()), + std::max(0.0f, src.top / texture->height()), + std::min(1.0f, src.right / texture->width()), + std::min(1.0f, src.bottom / texture->height())); const int textureFillFlags = (bitmap->colorType() == kAlpha_8_SkColorType) ? TextureFillFlags::IsAlphaMaskTexture : TextureFillFlags::None; @@ -1977,7 +1979,7 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, .setMeshTexturedUnitQuad(nullptr) .setFillShadowTexturePaint(*texture, textShadow.color, *paint, currentSnapshot()->alpha) .setTransform(*currentSnapshot(), TransformFlags::None) - .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width, sy + texture->height)) + .setModelViewMapUnitToRect(Rect(sx, sy, sx + texture->width(), sy + texture->height())) .build(); renderGlop(glop); } @@ -2316,7 +2318,7 @@ Texture* OpenGLRenderer::getTexture(const SkBitmap* bitmap) { void OpenGLRenderer::drawPathTexture(PathTexture* texture, float x, float y, const SkPaint* paint) { - if (quickRejectSetupScissor(x, y, x + texture->width, y + texture->height)) { + if (quickRejectSetupScissor(x, y, x + texture->width(), y + texture->height())) { return; } @@ -2326,7 +2328,7 @@ void OpenGLRenderer::drawPathTexture(PathTexture* texture, float x, float y, .setMeshTexturedUnitQuad(nullptr) .setFillPathTexturePaint(*texture, *paint, currentSnapshot()->alpha) .setTransform(*currentSnapshot(), TransformFlags::None) - .setModelViewMapUnitToRect(Rect(x, y, x + texture->width, y + texture->height)) + .setModelViewMapUnitToRect(Rect(x, y, x + texture->width(), y + texture->height())) .build(); renderGlop(glop); } diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index 06ea55ad2eac..bfabc1d4d94c 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -185,7 +185,7 @@ void PathCache::operator()(PathDescription& entry, PathTexture*& texture) { void PathCache::removeTexture(PathTexture* texture) { if (texture) { - const uint32_t size = texture->width * texture->height; + const uint32_t size = texture->width() * texture->height(); // If there is a pending task we must wait for it to return // before attempting our cleanup @@ -209,9 +209,7 @@ void PathCache::removeTexture(PathTexture* texture) { ALOGD("Shape deleted, size = %d", size); } - if (texture->id) { - Caches::getInstance().textureState().deleteTexture(texture->id); - } + texture->deleteTexture(); delete texture; } } @@ -248,8 +246,7 @@ PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *p drawPath(path, paint, bitmap, left, top, offset, width, height); PathTexture* texture = new PathTexture(Caches::getInstance(), - left, top, offset, width, height, - path->getGenerationID()); + left, top, offset, path->getGenerationID()); generateTexture(entry, &bitmap, texture); return texture; @@ -262,7 +259,7 @@ void PathCache::generateTexture(const PathDescription& entry, SkBitmap* bitmap, // Note here that we upload to a texture even if it's bigger than mMaxSize. // Such an entry in mCache will only be temporary, since it will be evicted // immediately on trim, or on any other Path entering the cache. - uint32_t size = texture->width * texture->height; + uint32_t size = texture->width() * texture->height(); mSize += size; PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d", texture->id, size, mSize); @@ -280,24 +277,8 @@ void PathCache::clear() { void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) { ATRACE_NAME("Upload Path Texture"); - SkAutoLockPixels alp(bitmap); - if (!bitmap.readyToDraw()) { - ALOGE("Cannot generate texture from bitmap"); - return; - } - - glGenTextures(1, &texture->id); - - Caches::getInstance().textureState().bindTexture(texture->id); - // Textures are Alpha8 - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - texture->blend = true; - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, - GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels()); - + texture->upload(bitmap); texture->setFilter(GL_LINEAR); - texture->setWrap(GL_CLAMP_TO_EDGE); } /////////////////////////////////////////////////////////////////////////////// @@ -320,16 +301,12 @@ void PathCache::PathProcessor::onProcess(const sp >& task) { texture->left = left; texture->top = top; texture->offset = offset; - texture->width = width; - texture->height = height; if (width <= mMaxTextureSize && height <= mMaxTextureSize) { SkBitmap* bitmap = new SkBitmap(); drawPath(&t->path, &t->paint, *bitmap, left, top, offset, width, height); t->setResult(bitmap); } else { - texture->width = 0; - texture->height = 0; t->setResult(nullptr); } } diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index 302e9f856904..18f380fe3f7a 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -61,13 +61,11 @@ class Caches; */ struct PathTexture: public Texture { PathTexture(Caches& caches, float left, float top, - float offset, int width, int height, int generation) + float offset, int generation) : Texture(caches) , left(left) , top(top) , offset(offset) { - this->width = width; - this->height = height; this->generation = generation; } PathTexture(Caches& caches, int generation) diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index 83652c6de21f..6f4a6839be4e 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -57,7 +57,7 @@ static inline void bindUniformColor(int slot, FloatColor color) { } static inline void bindTexture(Caches* caches, Texture* texture, GLenum wrapS, GLenum wrapT) { - caches->textureState().bindTexture(texture->id); + caches->textureState().bindTexture(texture->id()); texture->setWrapST(wrapS, wrapT); } @@ -219,8 +219,8 @@ bool tryStoreBitmap(Caches& caches, const SkShader& shader, const Matrix4& model outData->bitmapSampler = (*textureUnit)++; - const float width = outData->bitmapTexture->width; - const float height = outData->bitmapTexture->height; + const float width = outData->bitmapTexture->width(); + const float height = outData->bitmapTexture->height(); description->hasBitmap = true; if (!caches.extensions().hasNPot() diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp index 51f16523966b..f1e28b7dfa40 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -187,13 +187,10 @@ ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* glyphs texture = new ShadowTexture(caches); texture->left = shadow.penX; texture->top = shadow.penY; - texture->width = shadow.width; - texture->height = shadow.height; texture->generation = 0; texture->blend = true; const uint32_t size = shadow.width * shadow.height; - texture->bitmapSize = size; // Don't even try to cache a bitmap that's bigger than the cache if (size < mMaxSize) { @@ -202,15 +199,11 @@ ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* glyphs } } - glGenTextures(1, &texture->id); - - caches.textureState().bindTexture(texture->id); // Textures are Alpha8 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, + texture->upload(GL_ALPHA, shadow.width, shadow.height, GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image); - texture->setFilter(GL_LINEAR); texture->setWrap(GL_CLAMP_TO_EDGE); diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp index 5195b457af2b..8a6b28d86f27 100644 --- a/libs/hwui/Texture.cpp +++ b/libs/hwui/Texture.cpp @@ -14,14 +14,29 @@ * limitations under the License. */ -#include - #include "Caches.h" #include "Texture.h" +#include "utils/TraceUtils.h" + +#include + +#include namespace android { namespace uirenderer { +static int bytesPerPixel(GLint glFormat) { + switch (glFormat) { + case GL_ALPHA: + return 1; + case GL_RGB: + return 3; + case GL_RGBA: + default: + return 4; + } +} + void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force, GLenum renderTarget) { @@ -32,7 +47,7 @@ void Texture::setWrapST(GLenum wrapS, GLenum wrapT, bool bindTexture, bool force mWrapT = wrapT; if (bindTexture) { - mCaches.textureState().bindTexture(renderTarget, id); + mCaches.textureState().bindTexture(renderTarget, mId); } glTexParameteri(renderTarget, GL_TEXTURE_WRAP_S, wrapS); @@ -50,7 +65,7 @@ void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool for mMagFilter = mag; if (bindTexture) { - mCaches.textureState().bindTexture(renderTarget, id); + mCaches.textureState().bindTexture(renderTarget, mId); } if (mipMap && min == GL_LINEAR) min = GL_LINEAR_MIPMAP_LINEAR; @@ -60,8 +75,189 @@ void Texture::setFilterMinMag(GLenum min, GLenum mag, bool bindTexture, bool for } } -void Texture::deleteTexture() const { - mCaches.textureState().deleteTexture(id); +void Texture::deleteTexture() { + mCaches.textureState().deleteTexture(mId); + mId = 0; +} + +bool Texture::updateSize(uint32_t width, uint32_t height, GLint format) { + if (mWidth == width && mHeight == height && mFormat == format) { + return false; + } + mWidth = width; + mHeight = height; + mFormat = format; + notifySizeChanged(mWidth * mHeight * bytesPerPixel(mFormat)); + return true; +} + +void Texture::upload(GLint internalformat, uint32_t width, uint32_t height, + GLenum format, GLenum type, const void* pixels) { + bool needsAlloc = updateSize(width, height, internalformat); + if (!needsAlloc && !pixels) { + return; + } + mCaches.textureState().activateTexture(0); + if (!mId) { + glGenTextures(1, &mId); + needsAlloc = true; + } + mCaches.textureState().bindTexture(GL_TEXTURE_2D, mId); + if (needsAlloc) { + glTexImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0, + format, type, pixels); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, mFormat, mWidth, mHeight, 0, + format, type, pixels); + } +} + +static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp, + GLsizei width, GLsizei height, const GLvoid * data) { + + glPixelStorei(GL_UNPACK_ALIGNMENT, bpp); + const bool useStride = stride != width + && Caches::getInstance().extensions().hasUnpackRowLength(); + if ((stride == width) || useStride) { + if (useStride) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); + } + + if (resize) { + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); + } + + if (useStride) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + } + } else { + // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer + // if the stride doesn't match the width + + GLvoid * temp = (GLvoid *) malloc(width * height * bpp); + if (!temp) return; + + uint8_t * pDst = (uint8_t *)temp; + uint8_t * pSrc = (uint8_t *)data; + for (GLsizei i = 0; i < height; i++) { + memcpy(pDst, pSrc, width * bpp); + pDst += width * bpp; + pSrc += stride * bpp; + } + + if (resize) { + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp); + } + + free(temp); + } +} + +static void uploadSkBitmapToTexture(const SkBitmap& bitmap, + bool resize, GLenum format, GLenum type) { + uploadToTexture(resize, format, type, bitmap.rowBytesAsPixels(), bitmap.bytesPerPixel(), + bitmap.width(), bitmap.height(), bitmap.getPixels()); +} + +static void colorTypeToGlFormatAndType(SkColorType colorType, + GLint* outFormat, GLint* outType) { + switch (colorType) { + case kAlpha_8_SkColorType: + *outFormat = GL_ALPHA; + *outType = GL_UNSIGNED_BYTE; + break; + case kRGB_565_SkColorType: + *outFormat = GL_RGB; + *outType = GL_UNSIGNED_SHORT_5_6_5; + break; + // ARGB_4444 and Index_8 are both upconverted to RGBA_8888 + case kARGB_4444_SkColorType: + case kIndex_8_SkColorType: + case kN32_SkColorType: + *outFormat = GL_RGBA; + *outType = GL_UNSIGNED_BYTE; + break; + default: + LOG_ALWAYS_FATAL("Unsupported bitmap colorType: %d", colorType); + break; + } +} + +void Texture::upload(const SkBitmap& bitmap) { + SkAutoLockPixels alp(bitmap); + + if (!bitmap.readyToDraw()) { + ALOGE("Cannot generate texture from bitmap"); + return; + } + + ATRACE_FORMAT("Upload %ux%u Texture", bitmap.width(), bitmap.height()); + + // We could also enable mipmapping if both bitmap dimensions are powers + // of 2 but we'd have to deal with size changes. Let's keep this simple + const bool canMipMap = mCaches.extensions().hasNPot(); + + // If the texture had mipmap enabled but not anymore, + // force a glTexImage2D to discard the mipmap levels + bool needsAlloc = canMipMap && mipMap && !bitmap.hasHardwareMipMap(); + + if (!mId) { + glGenTextures(1, &mId); + needsAlloc = true; + } + + GLint format, type; + colorTypeToGlFormatAndType(bitmap.colorType(), &format, &type); + + if (updateSize(bitmap.width(), bitmap.height(), format)) { + needsAlloc = true; + } + + blend = !bitmap.isOpaque(); + mCaches.textureState().bindTexture(mId); + + if (CC_UNLIKELY(bitmap.colorType() == kARGB_4444_SkColorType + || bitmap.colorType() == kIndex_8_SkColorType)) { + SkBitmap rgbaBitmap; + rgbaBitmap.allocPixels(SkImageInfo::MakeN32(mWidth, mHeight, + bitmap.alphaType())); + rgbaBitmap.eraseColor(0); + + SkCanvas canvas(rgbaBitmap); + canvas.drawBitmap(bitmap, 0.0f, 0.0f, nullptr); + + uploadSkBitmapToTexture(rgbaBitmap, needsAlloc, format, type); + } else { + uploadSkBitmapToTexture(bitmap, needsAlloc, format, type); + } + + if (canMipMap) { + mipMap = bitmap.hasHardwareMipMap(); + if (mipMap) { + glGenerateMipmap(GL_TEXTURE_2D); + } + } + + if (mFirstFilter) { + setFilter(GL_NEAREST); + } + + if (mFirstWrap) { + setWrap(GL_CLAMP_TO_EDGE); + } +} + +void Texture::wrap(GLuint id, uint32_t width, uint32_t height, GLint format) { + mId = id; + mWidth = width; + mHeight = height; + mFormat = format; + // We're wrapping an existing texture, so don't double count this memory + notifySizeChanged(0); } }; // namespace uirenderer diff --git a/libs/hwui/Texture.h b/libs/hwui/Texture.h index 1c544b929e64..4e8e6dcf1a77 100644 --- a/libs/hwui/Texture.h +++ b/libs/hwui/Texture.h @@ -17,20 +17,27 @@ #ifndef ANDROID_HWUI_TEXTURE_H #define ANDROID_HWUI_TEXTURE_H +#include "GpuMemoryTracker.h" + #include +#include namespace android { namespace uirenderer { class Caches; class UvMapper; +class Layer; /** * Represents an OpenGL texture. */ -class Texture { +class Texture : public GpuMemoryTracker { public: - Texture(Caches& caches) : mCaches(caches) { } + Texture(Caches& caches) + : GpuMemoryTracker(GpuObjectType::Texture) + , mCaches(caches) + { } virtual ~Texture() { } @@ -53,28 +60,63 @@ public: /** * Convenience method to call glDeleteTextures() on this texture's id. */ - void deleteTexture() const; + void deleteTexture(); /** - * Name of the texture. + * Sets the width, height, and format of the texture along with allocating + * the texture ID. Does nothing if the width, height, and format are already + * the requested values. + * + * The image data is undefined after calling this. */ - GLuint id = 0; + void resize(uint32_t width, uint32_t height, GLint format) { + upload(format, width, height, format, GL_UNSIGNED_BYTE, nullptr); + } + /** - * Generation of the backing bitmap, + * Updates this Texture with the contents of the provided SkBitmap, + * also setting the appropriate width, height, and format. It is not necessary + * to call resize() prior to this. + * + * Note this does not set the generation from the SkBitmap. */ - uint32_t generation = 0; + void upload(const SkBitmap& source); + /** - * Indicates whether the texture requires blending. + * Basically glTexImage2D/glTexSubImage2D. */ - bool blend = false; + void upload(GLint internalformat, uint32_t width, uint32_t height, + GLenum format, GLenum type, const void* pixels); + /** - * Width of the backing bitmap. + * Wraps an existing texture. */ - uint32_t width = 0; + void wrap(GLuint id, uint32_t width, uint32_t height, GLint format); + + GLuint id() const { + return mId; + } + + uint32_t width() const { + return mWidth; + } + + uint32_t height() const { + return mHeight; + } + + GLint format() const { + return mFormat; + } + /** - * Height of the backing bitmap. + * Generation of the backing bitmap, */ - uint32_t height = 0; + uint32_t generation = 0; + /** + * Indicates whether the texture requires blending. + */ + bool blend = false; /** * Indicates whether this texture should be cleaned up after use. */ @@ -100,6 +142,19 @@ public: void* isInUse = nullptr; private: + // TODO: Temporarily grant private access to Layer, remove once + // Layer can be de-tangled from being a dual-purpose render target + // and external texture wrapper + friend class Layer; + + // Returns true if the size changed, false if it was the same + bool updateSize(uint32_t width, uint32_t height, GLint format); + + GLuint mId = 0; + uint32_t mWidth = 0; + uint32_t mHeight = 0; + GLint mFormat = 0; + /** * Last wrap modes set on this texture. */ @@ -120,7 +175,7 @@ private: class AutoTexture { public: - AutoTexture(const Texture* texture) + AutoTexture(Texture* texture) : texture(texture) {} ~AutoTexture() { if (texture && texture->cleanup) { @@ -129,7 +184,7 @@ public: } } - const Texture *const texture; + Texture* const texture; }; // class AutoTexture }; // namespace uirenderer diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 21901cf4414b..31bfa3a1ada4 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -166,7 +166,8 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType a if (canCache) { texture = new Texture(Caches::getInstance()); texture->bitmapSize = size; - Caches::getInstance().textureState().generateTexture(bitmap, texture, false); + texture->generation = bitmap->getGenerationID(); + texture->upload(*bitmap); mSize += size; TEXTURE_LOGD("TextureCache::get: create texture(%p): name, size, mSize = %d, %d, %d", @@ -179,7 +180,8 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap, AtlasUsageType a } else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) { // Texture was in the cache but is dirty, re-upload // TODO: Re-adjust the cache size if the bitmap's dimensions have changed - Caches::getInstance().textureState().generateTexture(bitmap, texture, true); + texture->upload(*bitmap); + texture->generation = bitmap->getGenerationID(); } return texture; @@ -204,7 +206,8 @@ Texture* TextureCache::get(const SkBitmap* bitmap, AtlasUsageType atlasUsageType const uint32_t size = bitmap->rowBytes() * bitmap->height(); texture = new Texture(Caches::getInstance()); texture->bitmapSize = size; - Caches::getInstance().textureState().generateTexture(bitmap, texture, false); + texture->upload(*bitmap); + texture->generation = bitmap->getGenerationID(); texture->cleanup = true; } diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index 191c8a81fec6..463450c81714 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -25,6 +25,7 @@ #include "Debug.h" #include +#include namespace android { namespace uirenderer { diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp index d2685daa1711..8ba4761c1b2e 100644 --- a/libs/hwui/font/CacheTexture.cpp +++ b/libs/hwui/font/CacheTexture.cpp @@ -111,11 +111,11 @@ CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock* blockToRemove) CacheTexture::CacheTexture(uint16_t width, uint16_t height, GLenum format, uint32_t maxQuadCount) : mTexture(Caches::getInstance()) + , mWidth(width) + , mHeight(height) , mFormat(format) , mMaxQuadCount(maxQuadCount) , mCaches(Caches::getInstance()) { - mTexture.width = width; - mTexture.height = height; mTexture.blend = true; mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE, @@ -160,10 +160,7 @@ void CacheTexture::releasePixelBuffer() { delete mPixelBuffer; mPixelBuffer = nullptr; } - if (mTexture.id) { - mCaches.textureState().deleteTexture(mTexture.id); - mTexture.id = 0; - } + mTexture.deleteTexture(); mDirty = false; mCurrentQuad = 0; } @@ -183,22 +180,9 @@ void CacheTexture::allocatePixelBuffer() { mPixelBuffer = PixelBuffer::create(mFormat, getWidth(), getHeight()); } - if (!mTexture.id) { - glGenTextures(1, &mTexture.id); - - mCaches.textureState().bindTexture(mTexture.id); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - // Initialize texture dimensions - glTexImage2D(GL_TEXTURE_2D, 0, mFormat, getWidth(), getHeight(), 0, - mFormat, GL_UNSIGNED_BYTE, nullptr); - - const GLenum filtering = getLinearFiltering() ? GL_LINEAR : GL_NEAREST; - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering); - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } + mTexture.resize(mWidth, mHeight, mFormat); + mTexture.setFilter(getLinearFiltering() ? GL_LINEAR : GL_NEAREST); + mTexture.setWrap(GL_CLAMP_TO_EDGE); } bool CacheTexture::upload() { diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h index 6dabc768ce6b..5510666eef86 100644 --- a/libs/hwui/font/CacheTexture.h +++ b/libs/hwui/font/CacheTexture.h @@ -92,11 +92,11 @@ public: bool fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY); inline uint16_t getWidth() const { - return mTexture.width; + return mWidth; } inline uint16_t getHeight() const { - return mTexture.height; + return mHeight; } inline GLenum getFormat() const { @@ -122,7 +122,7 @@ public: GLuint getTextureId() { allocatePixelBuffer(); - return mTexture.id; + return mTexture.id(); } inline bool isDirty() const { @@ -183,6 +183,7 @@ private: PixelBuffer* mPixelBuffer = nullptr; Texture mTexture; + uint32_t mWidth, mHeight; GLenum mFormat; bool mLinearFiltering = false; bool mDirty = false; diff --git a/libs/hwui/renderstate/OffscreenBufferPool.cpp b/libs/hwui/renderstate/OffscreenBufferPool.cpp index 227b6409b893..98c94dfab1c5 100644 --- a/libs/hwui/renderstate/OffscreenBufferPool.cpp +++ b/libs/hwui/renderstate/OffscreenBufferPool.cpp @@ -34,29 +34,22 @@ namespace uirenderer { OffscreenBuffer::OffscreenBuffer(RenderState& renderState, Caches& caches, uint32_t viewportWidth, uint32_t viewportHeight) - : renderState(renderState) + : GpuMemoryTracker(GpuObjectType::OffscreenBuffer) + , renderState(renderState) , viewportWidth(viewportWidth) , viewportHeight(viewportHeight) , texture(caches) { - texture.width = computeIdealDimension(viewportWidth); - texture.height = computeIdealDimension(viewportHeight); + uint32_t width = computeIdealDimension(viewportWidth); + uint32_t height = computeIdealDimension(viewportHeight); + texture.resize(width, height, GL_RGBA); texture.blend = true; - - caches.textureState().activateTexture(0); - glGenTextures(1, &texture.id); - caches.textureState().bindTexture(GL_TEXTURE_2D, texture.id); - - texture.setWrap(GL_CLAMP_TO_EDGE, false, false, GL_TEXTURE_2D); + texture.setWrap(GL_CLAMP_TO_EDGE); // not setting filter on texture, since it's set when drawing, based on transform - - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.width, texture.height, 0, - GL_RGBA, GL_UNSIGNED_BYTE, nullptr); } Rect OffscreenBuffer::getTextureCoordinates() { - const float texX = 1.0f / float(texture.width); - const float texY = 1.0f / float(texture.height); + const float texX = 1.0f / static_cast(texture.width()); + const float texY = 1.0f / static_cast(texture.height()); return Rect(0, viewportHeight * texY, viewportWidth * texX, 0); } @@ -69,8 +62,8 @@ void OffscreenBuffer::updateMeshFromRegion() { size_t count; const android::Rect* rects = safeRegion.getArray(&count); - const float texX = 1.0f / float(texture.width); - const float texY = 1.0f / float(texture.height); + const float texX = 1.0f / float(texture.width()); + const float texY = 1.0f / float(texture.height()); FatVector meshVector(count * 4); // uses heap if more than 64 vertices needed TextureVertex* mesh = &meshVector[0]; @@ -157,8 +150,8 @@ OffscreenBuffer* OffscreenBufferPool::get(RenderState& renderState, OffscreenBuffer* OffscreenBufferPool::resize(OffscreenBuffer* layer, const uint32_t width, const uint32_t height) { RenderState& renderState = layer->renderState; - if (layer->texture.width == OffscreenBuffer::computeIdealDimension(width) - && layer->texture.height == OffscreenBuffer::computeIdealDimension(height)) { + if (layer->texture.width() == OffscreenBuffer::computeIdealDimension(width) + && layer->texture.height() == OffscreenBuffer::computeIdealDimension(height)) { // resize in place layer->viewportWidth = width; layer->viewportHeight = height; diff --git a/libs/hwui/renderstate/OffscreenBufferPool.h b/libs/hwui/renderstate/OffscreenBufferPool.h index 2d8d5295e1f8..94155efcbb0a 100644 --- a/libs/hwui/renderstate/OffscreenBufferPool.h +++ b/libs/hwui/renderstate/OffscreenBufferPool.h @@ -17,10 +17,10 @@ #ifndef ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H #define ANDROID_HWUI_OFFSCREEN_BUFFER_POOL_H +#include #include "Caches.h" #include "Texture.h" #include "utils/Macros.h" - #include #include @@ -40,7 +40,7 @@ class RenderState; * viewport bounds, since textures are always allocated with width / height as a multiple of 64, for * the purpose of improving reuse. */ -class OffscreenBuffer { +class OffscreenBuffer : GpuMemoryTracker { public: OffscreenBuffer(RenderState& renderState, Caches& caches, uint32_t viewportWidth, uint32_t viewportHeight); @@ -58,7 +58,7 @@ public: static uint32_t computeIdealDimension(uint32_t dimension); - uint32_t getSizeInBytes() { return texture.width * texture.height * 4; } + uint32_t getSizeInBytes() { return texture.objectSize(); } RenderState& renderState; @@ -124,8 +124,8 @@ private: Entry(OffscreenBuffer* layer) : layer(layer) - , width(layer->texture.width) - , height(layer->texture.height) { + , width(layer->texture.width()) + , height(layer->texture.height()) { } static int compare(const Entry& lhs, const Entry& rhs); diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index 4fa820058fb2..4a1e8fcfedf2 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -13,12 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include #include "renderstate/RenderState.h" #include "renderthread/CanvasContext.h" #include "renderthread/EglManager.h" #include "utils/GLUtils.h" - #include namespace android { @@ -40,6 +40,8 @@ RenderState::~RenderState() { void RenderState::onGLContextCreated() { LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil, "State object lifecycle not managed correctly"); + GpuMemoryTracker::onGLContextCreated(); + mBlend = new Blend(); mMeshState = new MeshState(); mScissor = new Scissor(); @@ -106,6 +108,8 @@ void RenderState::onGLContextDestroyed() { mScissor = nullptr; delete mStencil; mStencil = nullptr; + + GpuMemoryTracker::onGLContextDestroyed(); } void RenderState::flush(Caches::FlushMode mode) { @@ -310,7 +314,7 @@ void RenderState::render(const Glop& glop, const Matrix4& orthoMatrix) { texture.texture->setFilter(texture.filter, true, false, texture.target); } - mCaches->textureState().bindTexture(texture.target, texture.texture->id); + mCaches->textureState().bindTexture(texture.target, texture.texture->id()); meshState().enableTexCoordsVertexArray(); meshState().bindTexCoordsVertexPointer(force, vertices.texCoord, vertices.stride); diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp index 1f50f712c267..26ebdee06c08 100644 --- a/libs/hwui/renderstate/TextureState.cpp +++ b/libs/hwui/renderstate/TextureState.cpp @@ -34,134 +34,6 @@ const GLenum kTextureUnits[] = { GL_TEXTURE3 }; -static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp, - GLsizei width, GLsizei height, const GLvoid * data) { - - glPixelStorei(GL_UNPACK_ALIGNMENT, bpp); - const bool useStride = stride != width - && Caches::getInstance().extensions().hasUnpackRowLength(); - if ((stride == width) || useStride) { - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); - } - - if (resize) { - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); - } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); - } - - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - } - } else { - // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer - // if the stride doesn't match the width - - GLvoid * temp = (GLvoid *) malloc(width * height * bpp); - if (!temp) return; - - uint8_t * pDst = (uint8_t *)temp; - uint8_t * pSrc = (uint8_t *)data; - for (GLsizei i = 0; i < height; i++) { - memcpy(pDst, pSrc, width * bpp); - pDst += width * bpp; - pSrc += stride * bpp; - } - - if (resize) { - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp); - } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp); - } - - free(temp); - } -} - -static void uploadSkBitmapToTexture(const SkBitmap& bitmap, - bool resize, GLenum format, GLenum type) { - uploadToTexture(resize, format, type, bitmap.rowBytesAsPixels(), bitmap.bytesPerPixel(), - bitmap.width(), bitmap.height(), bitmap.getPixels()); -} - -void TextureState::generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate) { - SkAutoLockPixels alp(*bitmap); - - if (!bitmap->readyToDraw()) { - ALOGE("Cannot generate texture from bitmap"); - return; - } - - ATRACE_FORMAT("Upload %ux%u Texture", bitmap->width(), bitmap->height()); - - // We could also enable mipmapping if both bitmap dimensions are powers - // of 2 but we'd have to deal with size changes. Let's keep this simple - const bool canMipMap = Caches::getInstance().extensions().hasNPot(); - - // If the texture had mipmap enabled but not anymore, - // force a glTexImage2D to discard the mipmap levels - const bool resize = !regenerate || bitmap->width() != int(texture->width) || - bitmap->height() != int(texture->height) || - (regenerate && canMipMap && texture->mipMap && !bitmap->hasHardwareMipMap()); - - if (!regenerate) { - glGenTextures(1, &texture->id); - } - - texture->generation = bitmap->getGenerationID(); - texture->width = bitmap->width(); - texture->height = bitmap->height(); - - bindTexture(texture->id); - - switch (bitmap->colorType()) { - case kAlpha_8_SkColorType: - uploadSkBitmapToTexture(*bitmap, resize, GL_ALPHA, GL_UNSIGNED_BYTE); - texture->blend = true; - break; - case kRGB_565_SkColorType: - uploadSkBitmapToTexture(*bitmap, resize, GL_RGB, GL_UNSIGNED_SHORT_5_6_5); - texture->blend = false; - break; - case kN32_SkColorType: - uploadSkBitmapToTexture(*bitmap, resize, GL_RGBA, GL_UNSIGNED_BYTE); - // Do this after calling getPixels() to make sure Skia's deferred - // decoding happened - texture->blend = !bitmap->isOpaque(); - break; - case kARGB_4444_SkColorType: - case kIndex_8_SkColorType: { - SkBitmap rgbaBitmap; - rgbaBitmap.allocPixels(SkImageInfo::MakeN32(texture->width, texture->height, - bitmap->alphaType())); - rgbaBitmap.eraseColor(0); - - SkCanvas canvas(rgbaBitmap); - canvas.drawBitmap(*bitmap, 0.0f, 0.0f, nullptr); - - uploadSkBitmapToTexture(rgbaBitmap, resize, GL_RGBA, GL_UNSIGNED_BYTE); - texture->blend = !bitmap->isOpaque(); - break; - } - default: - ALOGW("Unsupported bitmap colorType: %d", bitmap->colorType()); - break; - } - - if (canMipMap) { - texture->mipMap = bitmap->hasHardwareMipMap(); - if (texture->mipMap) { - glGenerateMipmap(GL_TEXTURE_2D); - } - } - - if (!regenerate) { - texture->setFilter(GL_NEAREST); - texture->setWrap(GL_CLAMP_TO_EDGE); - } -} - TextureState::TextureState() : mTextureUnit(0) { glActiveTexture(kTextureUnits[0]); diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h index 3a2b85ae2886..ec94d7e9e267 100644 --- a/libs/hwui/renderstate/TextureState.h +++ b/libs/hwui/renderstate/TextureState.h @@ -76,13 +76,6 @@ public: */ void unbindTexture(GLuint texture); - /** - * Generates the texture from a bitmap into the specified texture structure. - * - * @param regenerate If true, the bitmap data is reuploaded into the texture, but - * no new texture is generated. - */ - void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate); private: // total number of texture units available for use static const int kTextureUnitsCount = 4; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 644f3565216b..968135b4fd6c 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#include #include "CanvasContext.h" #include "AnimationContext.h" @@ -497,6 +498,8 @@ void CanvasContext::draw() { mJankTracker.addFrame(*mCurrentFrameInfo); mRenderThread.jankTracker().addFrame(*mCurrentFrameInfo); + + GpuMemoryTracker::onFrameCompleted(); } // Called by choreographer to do an RT-driven animation diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index edde31e502e0..42cd0ced0cac 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -197,6 +197,10 @@ public: renderthread::RenderThread::getInstance().queueAndWait(&task); } + static bool isRenderThreadRunning() { + return renderthread::RenderThread::hasInstance(); + } + static SkColor interpolateColor(float fraction, SkColor start, SkColor end); static void drawTextToCanvas(TestCanvas* canvas, const char* text, diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp index 1616a95857d8..02a39501e647 100644 --- a/libs/hwui/tests/macrobench/main.cpp +++ b/libs/hwui/tests/macrobench/main.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include diff --git a/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp new file mode 100644 index 000000000000..aa1dcb2ea51b --- /dev/null +++ b/libs/hwui/tests/unit/GpuMemoryTrackerTests.cpp @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2016 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 +#include + +#include "renderthread/EglManager.h" +#include "renderthread/RenderThread.h" +#include "tests/common/TestUtils.h" + +#include + +using namespace android; +using namespace android::uirenderer; +using namespace android::uirenderer::renderthread; + +class TestGPUObject : public GpuMemoryTracker { +public: + TestGPUObject() : GpuMemoryTracker(GpuObjectType::Texture) {} + + void changeSize(int newSize) { + notifySizeChanged(newSize); + } +}; + +// Other tests may have created a renderthread and EGL context. +// This will destroy the EGLContext on RenderThread if it exists so that the +// current thread can spoof being a GPU thread +static void destroyEglContext() { + if (TestUtils::isRenderThreadRunning()) { + TestUtils::runOnRenderThread([](RenderThread& thread) { + thread.eglManager().destroy(); + }); + } +} + +TEST(GpuMemoryTracker, sizeCheck) { + destroyEglContext(); + + GpuMemoryTracker::onGLContextCreated(); + ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); + ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture)); + { + TestGPUObject myObj; + ASSERT_EQ(1, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture)); + myObj.changeSize(500); + ASSERT_EQ(500, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); + myObj.changeSize(1000); + ASSERT_EQ(1000, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); + myObj.changeSize(300); + ASSERT_EQ(300, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); + } + ASSERT_EQ(0, GpuMemoryTracker::getTotalSize(GpuObjectType::Texture)); + ASSERT_EQ(0, GpuMemoryTracker::getInstanceCount(GpuObjectType::Texture)); + GpuMemoryTracker::onGLContextDestroyed(); +} diff --git a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp index e96e9ba55361..5278730a3c9e 100644 --- a/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp +++ b/libs/hwui/tests/unit/OffscreenBufferPoolTests.cpp @@ -36,8 +36,8 @@ TEST(OffscreenBuffer, construct) { EXPECT_EQ(49u, layer.viewportWidth); EXPECT_EQ(149u, layer.viewportHeight); - EXPECT_EQ(64u, layer.texture.width); - EXPECT_EQ(192u, layer.texture.height); + EXPECT_EQ(64u, layer.texture.width()); + EXPECT_EQ(192u, layer.texture.height()); EXPECT_EQ(64u * 192u * 4u, layer.getSizeInBytes()); }); @@ -100,8 +100,8 @@ TEST(OffscreenBufferPool, resize) { ASSERT_EQ(layer, pool.resize(layer, 60u, 55u)); EXPECT_EQ(60u, layer->viewportWidth); EXPECT_EQ(55u, layer->viewportHeight); - EXPECT_EQ(64u, layer->texture.width); - EXPECT_EQ(64u, layer->texture.height); + EXPECT_EQ(64u, layer->texture.width()); + EXPECT_EQ(64u, layer->texture.height()); // resized to use different object in pool auto layer2 = pool.get(thread.renderState(), 128u, 128u); @@ -110,8 +110,8 @@ TEST(OffscreenBufferPool, resize) { ASSERT_EQ(layer2, pool.resize(layer, 120u, 125u)); EXPECT_EQ(120u, layer2->viewportWidth); EXPECT_EQ(125u, layer2->viewportHeight); - EXPECT_EQ(128u, layer2->texture.width); - EXPECT_EQ(128u, layer2->texture.height); + EXPECT_EQ(128u, layer2->texture.width()); + EXPECT_EQ(128u, layer2->texture.height()); // original allocation now only thing in pool EXPECT_EQ(1u, pool.getCount()); diff --git a/libs/hwui/tests/unit/StringUtilsTests.cpp b/libs/hwui/tests/unit/StringUtilsTests.cpp index 6b2e265a61ff..b60e96c756ec 100644 --- a/libs/hwui/tests/unit/StringUtilsTests.cpp +++ b/libs/hwui/tests/unit/StringUtilsTests.cpp @@ -36,3 +36,18 @@ TEST(StringUtils, advancedBuildSet) { EXPECT_TRUE(collection.has("GL_ext1")); EXPECT_FALSE(collection.has("GL_ext")); // string present, but not in list } + +TEST(StringUtils, sizePrinter) { + std::stringstream os; + os << SizePrinter{500}; + EXPECT_EQ("500.00B", os.str()); + os.str(""); + os << SizePrinter{46080}; + EXPECT_EQ("45.00KiB", os.str()); + os.str(""); + os << SizePrinter{5 * 1024 * 1024 + 520 * 1024}; + EXPECT_EQ("5.51MiB", os.str()); + os.str(""); + os << SizePrinter{2147483647}; + EXPECT_EQ("2048.00MiB", os.str()); +} diff --git a/libs/hwui/utils/StringUtils.h b/libs/hwui/utils/StringUtils.h index 055869f73c26..05a3d5931e5d 100644 --- a/libs/hwui/utils/StringUtils.h +++ b/libs/hwui/utils/StringUtils.h @@ -18,6 +18,8 @@ #include #include +#include +#include namespace android { namespace uirenderer { @@ -34,6 +36,21 @@ public: static unordered_string_set split(const char* spacedList); }; +struct SizePrinter { + int bytes; + friend std::ostream& operator<<(std::ostream& stream, const SizePrinter& d) { + static const char* SUFFIXES[] = {"B", "KiB", "MiB"}; + size_t suffix = 0; + double temp = d.bytes; + while (temp > 1000 && suffix < 2) { + temp /= 1024.0; + suffix++; + } + stream << std::fixed << std::setprecision(2) << temp << SUFFIXES[suffix]; + return stream; + } +}; + } /* namespace uirenderer */ } /* namespace android */ -- cgit v1.2.3-59-g8ed1b From 2de7771740ee08fcaff638ec6b2e460bb72fff04 Mon Sep 17 00:00:00 2001 From: John Reck Date: Wed, 20 Jan 2016 11:09:53 -0800 Subject: Normalize GL_UNPACK_ALIGNMENT Several places were setting GL_UNPACK_ALIGNMENT unneccessarily, whereas other places were assuming an unpack alignment of 1. Since we never actually do explicit row-alignment, set GL_UNPACK_ALIGNMENT to 1 at context creation time and never change it Bug: 26584230 Also turns on aggressive glGetError checking to better catch potential problem zones Change-Id: I190c8f0f0494a7f046d5ed769405c75d363be59a --- libs/hwui/BakedOpRenderer.cpp | 4 +--- libs/hwui/Dither.cpp | 2 -- libs/hwui/FontRenderer.cpp | 1 - libs/hwui/GradientCache.cpp | 2 -- libs/hwui/Layer.cpp | 1 - libs/hwui/LayerRenderer.cpp | 18 +++++------------- libs/hwui/OpenGLRenderer.cpp | 4 +--- libs/hwui/PixelBuffer.cpp | 11 ++++------- libs/hwui/TextDropShadowCache.cpp | 2 -- libs/hwui/Texture.cpp | 1 - libs/hwui/renderstate/TextureState.cpp | 1 + libs/hwui/utils/GLUtils.h | 4 +++- 12 files changed, 15 insertions(+), 36 deletions(-) (limited to 'libs/hwui/TextDropShadowCache.cpp') diff --git a/libs/hwui/BakedOpRenderer.cpp b/libs/hwui/BakedOpRenderer.cpp index 09312825234e..e65746eea98a 100644 --- a/libs/hwui/BakedOpRenderer.cpp +++ b/libs/hwui/BakedOpRenderer.cpp @@ -139,9 +139,7 @@ void BakedOpRenderer::endFrame(const Rect& repaintRect) { mCaches.pathCache.trim(); mCaches.tessellationCache.trim(); -#if DEBUG_OPENGL - GLUtils::dumpGLErrors(); -#endif + GL_CHECKPOINT(); #if DEBUG_MEMORY_USAGE mCaches.dumpMemoryUsage(); diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp index 1ba651174c8a..ec2013e27401 100644 --- a/libs/hwui/Dither.cpp +++ b/libs/hwui/Dither.cpp @@ -54,7 +54,6 @@ void Dither::bindDitherTexture() { 15 * dither, 7 * dither, 13 * dither, 5 * dither }; - glPixelStorei(GL_UNPACK_ALIGNMENT, sizeof(GLfloat)); glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0, GL_RED, GL_FLOAT, &pattern); } else { @@ -65,7 +64,6 @@ void Dither::bindDitherTexture() { 15, 7, 13, 5 }; - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, DITHER_KERNEL_SIZE, DITHER_KERNEL_SIZE, 0, GL_ALPHA, GL_UNSIGNED_BYTE, &pattern); } diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index ed31a2c27121..68bae6dc47f6 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -458,7 +458,6 @@ void FontRenderer::checkTextureUpdate() { GLuint lastTextureId = 0; bool resetPixelStore = false; - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // Iterate over all the cache textures and see which ones need to be updated checkTextureUpdateForCache(caches, mACacheTextures, resetPixelStore, lastTextureId); diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index 1473bc88c060..e899ac71ff36 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -273,8 +273,6 @@ void GradientCache::generateTexture(uint32_t* colors, float* positions, memcpy(pixels + rowBytes, pixels, rowBytes); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); - if (mUseFloatTexture) { // We have to use GL_RGBA16F because GL_RGBA32F does not support filtering texture->upload(GL_RGBA16F, width, height, GL_RGBA, GL_FLOAT, pixels); diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index 83692664d76f..114347d94357 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -207,7 +207,6 @@ void Layer::allocateTexture() { #endif if (texture.mId) { texture.updateSize(getWidth(), getHeight(), GL_RGBA); - glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glTexImage2D(renderTarget, 0, GL_RGBA, getWidth(), getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); } diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index 0cf643fb686e..0f219e4792f6 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -373,7 +373,6 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* GLenum format; GLenum type; - GLenum error = GL_NO_ERROR; bool status = false; switch (bitmap->colorType()) { @@ -408,7 +407,7 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* renderState.bindFramebuffer(fbo); glGenTextures(1, &texture); - if ((error = glGetError()) != GL_NO_ERROR) goto error; + GL_CHECKPOINT(); caches.textureState().activateTexture(0); caches.textureState().bindTexture(texture); @@ -423,11 +422,11 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* glTexImage2D(GL_TEXTURE_2D, 0, format, bitmap->width(), bitmap->height(), 0, format, type, nullptr); - if ((error = glGetError()) != GL_NO_ERROR) goto error; + GL_CHECKPOINT(); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); - if ((error = glGetError()) != GL_NO_ERROR) goto error; + GL_CHECKPOINT(); { LayerRenderer renderer(renderState, layer); @@ -438,7 +437,7 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* renderer.translate(0.0f, bitmap->height()); renderer.scale(1.0f, -1.0f); - if ((error = glGetError()) != GL_NO_ERROR) goto error; + GL_CHECKPOINT(); { Rect bounds; @@ -448,19 +447,12 @@ bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* glReadPixels(0, 0, bitmap->width(), bitmap->height(), format, type, bitmap->getPixels()); - if ((error = glGetError()) != GL_NO_ERROR) goto error; + GL_CHECKPOINT(); } status = true; } -error: -#if DEBUG_OPENGL - if (error != GL_NO_ERROR) { - ALOGD("GL error while copying layer into bitmap = 0x%x", error); - } -#endif - renderState.bindFramebuffer(previousFbo); layer->setAlpha(alpha, mode); layer->setFbo(previousLayerFbo); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 0cd763df78d1..1bfa3084d266 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -195,9 +195,7 @@ bool OpenGLRenderer::finish() { } if (!suppressErrorChecks()) { -#if DEBUG_OPENGL - GLUtils::dumpGLErrors(); -#endif + GL_CHECKPOINT(); #if DEBUG_MEMORY_USAGE mCaches.dumpMemoryUsage(); diff --git a/libs/hwui/PixelBuffer.cpp b/libs/hwui/PixelBuffer.cpp index 96247260f0f4..6df994c623f2 100644 --- a/libs/hwui/PixelBuffer.cpp +++ b/libs/hwui/PixelBuffer.cpp @@ -20,6 +20,7 @@ #include "Extensions.h" #include "Properties.h" #include "renderstate/RenderState.h" +#include "utils/GLUtils.h" #include @@ -112,14 +113,10 @@ uint8_t* GpuPixelBuffer::map(AccessMode mode) { if (mAccessMode == kAccessMode_None) { mCaches.pixelBufferState().bind(mBuffer); mMappedPointer = (uint8_t*) glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, getSize(), mode); -#if DEBUG_OPENGL - if (!mMappedPointer) { - GLenum status = GL_NO_ERROR; - while ((status = glGetError()) != GL_NO_ERROR) { - ALOGE("Could not map GPU pixel buffer: 0x%x", status); - } + if (CC_UNLIKELY(!mMappedPointer)) { + GLUtils::dumpGLErrors(); + LOG_ALWAYS_FATAL("Failed to map PBO"); } -#endif mAccessMode = mode; } diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp index f1e28b7dfa40..62a20fcfa699 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -200,8 +200,6 @@ ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* glyphs } // Textures are Alpha8 - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - texture->upload(GL_ALPHA, shadow.width, shadow.height, GL_ALPHA, GL_UNSIGNED_BYTE, shadow.image); texture->setFilter(GL_LINEAR); diff --git a/libs/hwui/Texture.cpp b/libs/hwui/Texture.cpp index 771d004cb91d..9fc0c199815d 100644 --- a/libs/hwui/Texture.cpp +++ b/libs/hwui/Texture.cpp @@ -122,7 +122,6 @@ void Texture::upload(GLint internalformat, uint32_t width, uint32_t height, static void uploadToTexture(bool resize, GLenum format, GLenum type, GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, const GLvoid * data) { - glPixelStorei(GL_UNPACK_ALIGNMENT, bpp); const bool useStride = stride != width && Caches::getInstance().extensions().hasUnpackRowLength(); if ((stride == width) || useStride) { diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp index 26ebdee06c08..78b8edae2eed 100644 --- a/libs/hwui/renderstate/TextureState.cpp +++ b/libs/hwui/renderstate/TextureState.cpp @@ -43,6 +43,7 @@ TextureState::TextureState() glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount, "At least %d texture units are required!", kTextureUnitsCount); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); } void TextureState::activateTexture(GLuint textureUnit) { diff --git a/libs/hwui/utils/GLUtils.h b/libs/hwui/utils/GLUtils.h index 6c521e4955a0..85a10f94dc4c 100644 --- a/libs/hwui/utils/GLUtils.h +++ b/libs/hwui/utils/GLUtils.h @@ -16,12 +16,14 @@ #ifndef GLUTILS_H #define GLUTILS_H +#include "Debug.h" + #include namespace android { namespace uirenderer { -#if 0 +#if DEBUG_OPENGL #define GL_CHECKPOINT() LOG_ALWAYS_FATAL_IF(GLUtils::dumpGLErrors(),\ "GL errors! %s:%d", __FILE__, __LINE__) #else -- cgit v1.2.3-59-g8ed1b From c3127a78b996a540cd002e5a87861e8a2adeb336 Mon Sep 17 00:00:00 2001 From: John Reck Date: Fri, 29 Jan 2016 15:54:10 -0800 Subject: Fix TextDropShadowCache infinite loop Bug: 26862239 Switch TextDropCacheShadow to use the tracked objectSize() instead of the optional bitmapSize. A mismatch here results in ::get() infinite looping trying to free space in the cache since the LRU removal callback would always decrement mSize by 0 since bitmapSize was not being set. Also prevent the infinite loop in the future by crashing if this scenario happens again. Change-Id: Ib4e9fbe1c8327af2335ad650fd694a1627d9824f --- libs/hwui/Android.mk | 3 +- libs/hwui/TextDropShadowCache.cpp | 8 ++-- libs/hwui/tests/unit/TextDropShadowCacheTests.cpp | 53 +++++++++++++++++++++++ 3 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 libs/hwui/tests/unit/TextDropShadowCacheTests.cpp (limited to 'libs/hwui/TextDropShadowCache.cpp') diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index fc405541484f..587a366730c2 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -248,7 +248,8 @@ LOCAL_SRC_FILES += \ tests/unit/VectorDrawableTests.cpp \ tests/unit/OffscreenBufferPoolTests.cpp \ tests/unit/StringUtilsTests.cpp \ - tests/unit/BufferPoolTests.cpp + tests/unit/BufferPoolTests.cpp \ + tests/unit/TextDropShadowCacheTests.cpp ifeq (true, $(HWUI_NEW_OPS)) LOCAL_SRC_FILES += \ diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp index 62a20fcfa699..1707468f169d 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -148,7 +148,7 @@ void TextDropShadowCache::setMaxSize(uint32_t maxSize) { void TextDropShadowCache::operator()(ShadowText&, ShadowTexture*& texture) { if (texture) { - mSize -= texture->bitmapSize; + mSize -= texture->objectSize(); if (mDebugEnabled) { ALOGD("Shadow texture deleted, size = %d", texture->bitmapSize); @@ -195,7 +195,9 @@ ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* glyphs // Don't even try to cache a bitmap that's bigger than the cache if (size < mMaxSize) { while (mSize + size > mMaxSize) { - mCache.removeOldest(); + LOG_ALWAYS_FATAL_IF(!mCache.removeOldest(), + "Failed to remove oldest from cache. mSize = %" + PRIu32 ", mCache.size() = %zu", mSize, mCache.size()); } } @@ -212,7 +214,7 @@ ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* glyphs entry.copyTextLocally(); - mSize += size; + mSize += texture->objectSize(); mCache.put(entry, texture); } else { texture->cleanup = true; diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp new file mode 100644 index 000000000000..c54f2c365ee2 --- /dev/null +++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 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 + +#include "GammaFontRenderer.h" +#include "TextDropShadowCache.h" +#include "utils/Blur.h" +#include "tests/common/TestUtils.h" + +#include +#include + +using namespace android; +using namespace android::uirenderer; + +RENDERTHREAD_TEST(TextDropShadowCache, addRemove) { + GammaFontRenderer gammaFontRenderer; + FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer(); + TextDropShadowCache cache(5000); + cache.setFontRenderer(fontRenderer); + + SkPaint paint; + paint.setLooper(SkBlurDrawLooper::Create((SkColor)0xFFFFFFFF, + Blur::convertRadiusToSigma(10), 10, 10))->unref(); + std::string msg("This is a test"); + std::unique_ptr positions(new float[msg.length()]); + for (size_t i = 0; i < msg.length(); i++) { + positions[i] = i * 10.0f; + } + fontRenderer.setFont(&paint, SkMatrix::I()); + ShadowTexture* texture = cache.get(&paint, msg.c_str(), msg.length(), + 10.0f, positions.get()); + ASSERT_TRUE(texture); + ASSERT_FALSE(texture->cleanup); + ASSERT_EQ((uint32_t) texture->objectSize(), cache.getSize()); + ASSERT_TRUE(cache.getSize()); + cache.clear(); + ASSERT_EQ(cache.getSize(), 0u); +} -- cgit v1.2.3-59-g8ed1b From 48a8f431fa52ae2ee25ffba9d20676f03bb710ff Mon Sep 17 00:00:00 2001 From: Chris Craik Date: Fri, 5 Feb 2016 15:59:29 -0800 Subject: Move several property queries to Properties class bug:17478770 This removes a lot of redundant property query code, and puts the queries all in one place, so defining them automatically will be simpler in the future. Change-Id: I0428550e6081f07bc6554ffdf73b22284325abb8 --- libs/hwui/FboCache.cpp | 11 ++--------- libs/hwui/GradientCache.cpp | 17 +---------------- libs/hwui/GradientCache.h | 6 +----- libs/hwui/PatchCache.cpp | 12 ++---------- libs/hwui/PatchCache.h | 2 +- libs/hwui/PathCache.cpp | 15 ++++----------- libs/hwui/PathCache.h | 2 +- libs/hwui/Properties.cpp | 25 +++++++++++++++++++++++-- libs/hwui/Properties.h | 9 +++++++++ libs/hwui/RenderBufferCache.cpp | 18 +++--------------- libs/hwui/RenderBufferCache.h | 4 ---- libs/hwui/TessellationCache.cpp | 18 +----------------- libs/hwui/TessellationCache.h | 8 +------- libs/hwui/TextDropShadowCache.cpp | 38 ++++++++------------------------------ libs/hwui/TextDropShadowCache.h | 10 ++-------- libs/hwui/TextureCache.cpp | 32 ++------------------------------ libs/hwui/TextureCache.h | 13 ++----------- 17 files changed, 63 insertions(+), 177 deletions(-) (limited to 'libs/hwui/TextDropShadowCache.cpp') diff --git a/libs/hwui/FboCache.cpp b/libs/hwui/FboCache.cpp index cca3cb7a98f1..b2181b60054f 100644 --- a/libs/hwui/FboCache.cpp +++ b/libs/hwui/FboCache.cpp @@ -27,15 +27,8 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -FboCache::FboCache(): mMaxSize(DEFAULT_FBO_CACHE_SIZE) { - char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_FBO_CACHE_SIZE, property, nullptr) > 0) { - INIT_LOGD(" Setting fbo cache size to %s", property); - mMaxSize = atoi(property); - } else { - INIT_LOGD(" Using default fbo cache size of %d", DEFAULT_FBO_CACHE_SIZE); - } -} +FboCache::FboCache() + : mMaxSize(Properties::fboCacheSize) {} FboCache::~FboCache() { clear(); diff --git a/libs/hwui/GradientCache.cpp b/libs/hwui/GradientCache.cpp index e899ac71ff36..044c7cb65718 100644 --- a/libs/hwui/GradientCache.cpp +++ b/libs/hwui/GradientCache.cpp @@ -65,17 +65,9 @@ int GradientCacheEntry::compare(const GradientCacheEntry& lhs, const GradientCac GradientCache::GradientCache(Extensions& extensions) : mCache(LruCache::kUnlimitedCapacity) , mSize(0) - , mMaxSize(MB(DEFAULT_GRADIENT_CACHE_SIZE)) + , mMaxSize(Properties::gradientCacheSize) , mUseFloatTexture(extensions.hasFloatTextures()) , mHasNpot(extensions.hasNPot()){ - char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_GRADIENT_CACHE_SIZE, property, nullptr) > 0) { - INIT_LOGD(" Setting gradient cache size to %sMB", property); - setMaxSize(MB(atof(property))); - } else { - INIT_LOGD(" Using default gradient cache size of %.2fMB", DEFAULT_GRADIENT_CACHE_SIZE); - } - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); mCache.setOnEntryRemovedListener(this); @@ -97,13 +89,6 @@ uint32_t GradientCache::getMaxSize() { return mMaxSize; } -void GradientCache::setMaxSize(uint32_t maxSize) { - mMaxSize = maxSize; - while (mSize > mMaxSize) { - mCache.removeOldest(); - } -} - /////////////////////////////////////////////////////////////////////////////// // Callbacks /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/GradientCache.h b/libs/hwui/GradientCache.h index b762ca7d5e5c..dccd45072522 100644 --- a/libs/hwui/GradientCache.h +++ b/libs/hwui/GradientCache.h @@ -122,10 +122,6 @@ public: */ void clear(); - /** - * Sets the maximum size of the cache in bytes. - */ - void setMaxSize(uint32_t maxSize); /** * Returns the maximum size of the cache in bytes. */ @@ -177,7 +173,7 @@ private: LruCache mCache; uint32_t mSize; - uint32_t mMaxSize; + const uint32_t mMaxSize; GLint mMaxTextureSize; bool mUseFloatTexture; diff --git a/libs/hwui/PatchCache.cpp b/libs/hwui/PatchCache.cpp index 98812805259e..bd6feb9fc762 100644 --- a/libs/hwui/PatchCache.cpp +++ b/libs/hwui/PatchCache.cpp @@ -32,20 +32,12 @@ namespace uirenderer { PatchCache::PatchCache(RenderState& renderState) : mRenderState(renderState) + , mMaxSize(Properties::patchCacheSize) , mSize(0) , mCache(LruCache::kUnlimitedCapacity) , mMeshBuffer(0) , mFreeBlocks(nullptr) - , mGenerationId(0) { - char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_PATCH_CACHE_SIZE, property, nullptr) > 0) { - INIT_LOGD(" Setting patch cache size to %skB", property); - mMaxSize = KB(atoi(property)); - } else { - INIT_LOGD(" Using default patch cache size of %.2fkB", DEFAULT_PATCH_CACHE_SIZE); - mMaxSize = KB(DEFAULT_PATCH_CACHE_SIZE); - } -} + , mGenerationId(0) {} PatchCache::~PatchCache() { clear(); diff --git a/libs/hwui/PatchCache.h b/libs/hwui/PatchCache.h index 387f79acf0ec..66ef6a0279ba 100644 --- a/libs/hwui/PatchCache.h +++ b/libs/hwui/PatchCache.h @@ -169,7 +169,7 @@ private: #endif RenderState& mRenderState; - uint32_t mMaxSize; + const uint32_t mMaxSize; uint32_t mSize; LruCache mCache; diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index bfabc1d4d94c..8f914acc5c76 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -135,17 +135,10 @@ static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap, // Cache constructor/destructor /////////////////////////////////////////////////////////////////////////////// -PathCache::PathCache(): - mCache(LruCache::kUnlimitedCapacity), - mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) { - char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_PATH_CACHE_SIZE, property, nullptr) > 0) { - INIT_LOGD(" Setting path cache size to %sMB", property); - mMaxSize = MB(atof(property)); - } else { - INIT_LOGD(" Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE); - } - +PathCache::PathCache() + : mCache(LruCache::kUnlimitedCapacity) + , mSize(0) + , mMaxSize(Properties::pathCacheSize) { mCache.setOnEntryRemovedListener(this); GLint maxTextureSize; diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index 18f380fe3f7a..d2633aa427c5 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -300,7 +300,7 @@ private: LruCache mCache; uint32_t mSize; - uint32_t mMaxSize; + const uint32_t mMaxSize; GLuint mMaxTextureSize; bool mDebugEnabled; diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 083aeb7ed585..bbd8c7226ab2 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -37,7 +37,18 @@ bool Properties::useBufferAge = true; bool Properties::enablePartialUpdates = true; float Properties::textGamma = DEFAULT_TEXT_GAMMA; -int Properties::layerPoolSize = DEFAULT_LAYER_CACHE_SIZE; + +int Properties::fboCacheSize = DEFAULT_FBO_CACHE_SIZE; +int Properties::gradientCacheSize = MB(DEFAULT_GRADIENT_CACHE_SIZE); +int Properties::layerPoolSize = MB(DEFAULT_LAYER_CACHE_SIZE); +int Properties::patchCacheSize = KB(DEFAULT_PATCH_CACHE_SIZE); +int Properties::pathCacheSize = MB(DEFAULT_PATH_CACHE_SIZE); +int Properties::renderBufferCacheSize = MB(DEFAULT_RENDER_BUFFER_CACHE_SIZE); +int Properties::tessellationCacheSize = MB(DEFAULT_VERTEX_CACHE_SIZE); +int Properties::textDropShadowCacheSize = MB(DEFAULT_DROP_SHADOW_CACHE_SIZE); +int Properties::textureCacheSize = MB(DEFAULT_TEXTURE_CACHE_SIZE); + +float Properties::textureCacheFlushRate = DEFAULT_TEXTURE_CACHE_FLUSH_RATE; DebugLevel Properties::debugLevel = kDebugDisabled; OverdrawColorSet Properties::overdrawColorSet = OverdrawColorSet::Default; @@ -79,7 +90,6 @@ bool Properties::load() { bool prevDebugOverdraw = debugOverdraw; StencilClipDebug prevDebugStencilClip = debugStencilClip; - debugOverdraw = false; if (property_get(PROPERTY_DEBUG_OVERDRAW, property, nullptr) > 0) { INIT_LOGD(" Overdraw debug enabled: %s", property); @@ -133,7 +143,18 @@ bool Properties::load() { enablePartialUpdates = property_get_bool(PROPERTY_ENABLE_PARTIAL_UPDATES, true); textGamma = property_get_float(PROPERTY_TEXT_GAMMA, DEFAULT_TEXT_GAMMA); + + fboCacheSize = property_get_int(PROPERTY_FBO_CACHE_SIZE, DEFAULT_FBO_CACHE_SIZE); + gradientCacheSize = MB(property_get_float(PROPERTY_GRADIENT_CACHE_SIZE, DEFAULT_GRADIENT_CACHE_SIZE)); layerPoolSize = MB(property_get_float(PROPERTY_LAYER_CACHE_SIZE, DEFAULT_LAYER_CACHE_SIZE)); + patchCacheSize = KB(property_get_float(PROPERTY_PATCH_CACHE_SIZE, DEFAULT_PATCH_CACHE_SIZE)); + pathCacheSize = MB(property_get_float(PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE)); + renderBufferCacheSize = MB(property_get_float(PROPERTY_RENDER_BUFFER_CACHE_SIZE, DEFAULT_RENDER_BUFFER_CACHE_SIZE)); + tessellationCacheSize = MB(property_get_float(PROPERTY_VERTEX_CACHE_SIZE, DEFAULT_VERTEX_CACHE_SIZE)); + textDropShadowCacheSize = MB(property_get_float(PROPERTY_DROP_SHADOW_CACHE_SIZE, DEFAULT_DROP_SHADOW_CACHE_SIZE)); + textureCacheSize = MB(property_get_float(PROPERTY_TEXTURE_CACHE_SIZE, DEFAULT_TEXTURE_CACHE_SIZE)); + textureCacheFlushRate = std::max(0.0f, std::min(1.0f, + property_get_float(PROPERTY_TEXTURE_CACHE_FLUSH_RATE, DEFAULT_TEXTURE_CACHE_FLUSH_RATE))); return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw) diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 88f1dbc98926..3e111516ac63 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -267,7 +267,16 @@ public: static float textGamma; + static int fboCacheSize; + static int gradientCacheSize; static int layerPoolSize; + static int patchCacheSize; + static int pathCacheSize; + static int renderBufferCacheSize; + static int tessellationCacheSize; + static int textDropShadowCacheSize; + static int textureCacheSize; + static float textureCacheFlushRate; static DebugLevel debugLevel; static OverdrawColorSet overdrawColorSet; diff --git a/libs/hwui/RenderBufferCache.cpp b/libs/hwui/RenderBufferCache.cpp index 11d7a6af3a6a..1ac57cdbac0c 100644 --- a/libs/hwui/RenderBufferCache.cpp +++ b/libs/hwui/RenderBufferCache.cpp @@ -40,16 +40,9 @@ namespace uirenderer { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -RenderBufferCache::RenderBufferCache(): mSize(0), mMaxSize(MB(DEFAULT_RENDER_BUFFER_CACHE_SIZE)) { - char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_RENDER_BUFFER_CACHE_SIZE, property, nullptr) > 0) { - INIT_LOGD(" Setting render buffer cache size to %sMB", property); - setMaxSize(MB(atof(property))); - } else { - INIT_LOGD(" Using default render buffer cache size of %.2fMB", - DEFAULT_RENDER_BUFFER_CACHE_SIZE); - } -} +RenderBufferCache::RenderBufferCache() + : mSize(0) + , mMaxSize(Properties::renderBufferCacheSize) {} RenderBufferCache::~RenderBufferCache() { clear(); @@ -67,11 +60,6 @@ uint32_t RenderBufferCache::getMaxSize() { return mMaxSize; } -void RenderBufferCache::setMaxSize(uint32_t maxSize) { - clear(); - mMaxSize = maxSize; -} - /////////////////////////////////////////////////////////////////////////////// // Caching /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/RenderBufferCache.h b/libs/hwui/RenderBufferCache.h index 7f59ec1c48b1..f77f4c95b5ba 100644 --- a/libs/hwui/RenderBufferCache.h +++ b/libs/hwui/RenderBufferCache.h @@ -63,10 +63,6 @@ public: */ void clear(); - /** - * Sets the maximum size of the cache in bytes. - */ - void setMaxSize(uint32_t maxSize); /** * Returns the maximum size of the cache in bytes. */ diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp index fd9fb852171c..14c8f3926e31 100644 --- a/libs/hwui/TessellationCache.cpp +++ b/libs/hwui/TessellationCache.cpp @@ -265,18 +265,9 @@ public: /////////////////////////////////////////////////////////////////////////////// TessellationCache::TessellationCache() - : mSize(0) - , mMaxSize(MB(DEFAULT_VERTEX_CACHE_SIZE)) + : mMaxSize(Properties::tessellationCacheSize) , mCache(LruCache::kUnlimitedCapacity) , mShadowCache(LruCache*>::kUnlimitedCapacity) { - char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_VERTEX_CACHE_SIZE, property, nullptr) > 0) { - INIT_LOGD(" Setting tessellation cache size to %sMB", property); - setMaxSize(MB(atof(property))); - } else { - INIT_LOGD(" Using default tessellation cache size of %.2fMB", DEFAULT_VERTEX_CACHE_SIZE); - } - mCache.setOnEntryRemovedListener(&mBufferRemovedListener); mShadowCache.setOnEntryRemovedListener(&mBufferPairRemovedListener); mDebugEnabled = Properties::debugLevel & kDebugCaches; @@ -303,13 +294,6 @@ uint32_t TessellationCache::getMaxSize() { return mMaxSize; } -void TessellationCache::setMaxSize(uint32_t maxSize) { - mMaxSize = maxSize; - while (mSize > mMaxSize) { - mCache.removeOldest(); - } -} - /////////////////////////////////////////////////////////////////////////////// // Caching /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h index 6dcc8120cf48..0bd6365db60f 100644 --- a/libs/hwui/TessellationCache.h +++ b/libs/hwui/TessellationCache.h @@ -131,11 +131,6 @@ public: * Clears the cache. This causes all TessellationBuffers to be deleted. */ void clear(); - - /** - * Sets the maximum size of the cache in bytes. - */ - void setMaxSize(uint32_t maxSize); /** * Returns the maximum size of the cache in bytes. */ @@ -198,8 +193,7 @@ private: Buffer* getOrCreateBuffer(const Description& entry, Tessellator tessellator); - uint32_t mSize; - uint32_t mMaxSize; + const uint32_t mMaxSize; bool mDebugEnabled; diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp index 1707468f169d..fe4b3d7507b2 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -93,36 +93,21 @@ int ShadowText::compare(const ShadowText& lhs, const ShadowText& rhs) { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -TextDropShadowCache::TextDropShadowCache(): - mCache(LruCache::kUnlimitedCapacity), - mSize(0), mMaxSize(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) { - char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, nullptr) > 0) { - INIT_LOGD(" Setting drop shadow cache size to %sMB", property); - setMaxSize(MB(atof(property))); - } else { - INIT_LOGD(" Using default drop shadow cache size of %.2fMB", - DEFAULT_DROP_SHADOW_CACHE_SIZE); - } - - init(); -} +TextDropShadowCache::TextDropShadowCache() + : TextDropShadowCache(Properties::textDropShadowCacheSize) {} -TextDropShadowCache::TextDropShadowCache(uint32_t maxByteSize): - mCache(LruCache::kUnlimitedCapacity), - mSize(0), mMaxSize(maxByteSize) { - init(); +TextDropShadowCache::TextDropShadowCache(uint32_t maxByteSize) + : mCache(LruCache::kUnlimitedCapacity) + , mSize(0) + , mMaxSize(maxByteSize) { + mCache.setOnEntryRemovedListener(this); + mDebugEnabled = Properties::debugLevel & kDebugMoreCaches; } TextDropShadowCache::~TextDropShadowCache() { mCache.clear(); } -void TextDropShadowCache::init() { - mCache.setOnEntryRemovedListener(this); - mDebugEnabled = Properties::debugLevel & kDebugMoreCaches; -} - /////////////////////////////////////////////////////////////////////////////// // Size management /////////////////////////////////////////////////////////////////////////////// @@ -135,13 +120,6 @@ uint32_t TextDropShadowCache::getMaxSize() { return mMaxSize; } -void TextDropShadowCache::setMaxSize(uint32_t maxSize) { - mMaxSize = maxSize; - while (mSize > mMaxSize) { - mCache.removeOldest(); - } -} - /////////////////////////////////////////////////////////////////////////////// // Callbacks /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h index c4f3c5d96786..cf647882e5a7 100644 --- a/libs/hwui/TextDropShadowCache.h +++ b/libs/hwui/TextDropShadowCache.h @@ -147,10 +147,6 @@ public: mRenderer = &fontRenderer; } - /** - * Sets the maximum size of the cache in bytes. - */ - void setMaxSize(uint32_t maxSize); /** * Returns the maximum size of the cache in bytes. */ @@ -161,13 +157,11 @@ public: uint32_t getSize(); private: - void init(); - LruCache mCache; uint32_t mSize; - uint32_t mMaxSize; - FontRenderer* mRenderer; + const uint32_t mMaxSize; + FontRenderer* mRenderer = nullptr; bool mDebugEnabled; }; // class TextDropShadowCache diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 31bfa3a1ada4..ade8600ab78b 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -35,26 +35,9 @@ namespace uirenderer { TextureCache::TextureCache() : mCache(LruCache::kUnlimitedCapacity) , mSize(0) - , mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)) - , mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) + , mMaxSize(Properties::textureCacheSize) + , mFlushRate(Properties::textureCacheFlushRate) , mAssetAtlas(nullptr) { - char property[PROPERTY_VALUE_MAX]; - if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, nullptr) > 0) { - INIT_LOGD(" Setting texture cache size to %sMB", property); - setMaxSize(MB(atof(property))); - } else { - INIT_LOGD(" Using default texture cache size of %.2fMB", DEFAULT_TEXTURE_CACHE_SIZE); - } - - if (property_get(PROPERTY_TEXTURE_CACHE_FLUSH_RATE, property, nullptr) > 0) { - float flushRate = atof(property); - INIT_LOGD(" Setting texture cache flush rate to %.2f%%", flushRate * 100.0f); - setFlushRate(flushRate); - } else { - INIT_LOGD(" Using default texture cache flush rate of %.2f%%", - DEFAULT_TEXTURE_CACHE_FLUSH_RATE * 100.0f); - } - mCache.setOnEntryRemovedListener(this); glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); @@ -79,17 +62,6 @@ uint32_t TextureCache::getMaxSize() { return mMaxSize; } -void TextureCache::setMaxSize(uint32_t maxSize) { - mMaxSize = maxSize; - while (mSize > mMaxSize) { - mCache.removeOldest(); - } -} - -void TextureCache::setFlushRate(float flushRate) { - mFlushRate = std::max(0.0f, std::min(1.0f, flushRate)); -} - /////////////////////////////////////////////////////////////////////////////// // Callbacks /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index 463450c81714..a4317cee73fd 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -108,10 +108,6 @@ public: */ void clear(); - /** - * Sets the maximum size of the cache in bytes. - */ - void setMaxSize(uint32_t maxSize); /** * Returns the maximum size of the cache in bytes. */ @@ -126,11 +122,6 @@ public: * is defined by the flush rate. */ void flush(); - /** - * Indicates the percentage of the cache to retain when a - * memory trim is requested (see Caches::flush). - */ - void setFlushRate(float flushRate); void setAssetAtlas(AssetAtlas* assetAtlas); @@ -148,10 +139,10 @@ private: LruCache mCache; uint32_t mSize; - uint32_t mMaxSize; + const uint32_t mMaxSize; GLint mMaxTextureSize; - float mFlushRate; + const float mFlushRate; bool mDebugEnabled; -- cgit v1.2.3-59-g8ed1b From e8c3c813b0e3ac98304b17a751ce6e436e252bd9 Mon Sep 17 00:00:00 2001 From: Chris Craik Date: Fri, 5 Feb 2016 20:10:50 -0800 Subject: Fix TextDropShadowCacheTests and glyph_t everywhere Change-Id: I943eae4e9408c77bdfba6304ba7ee3e862351a41 --- libs/hwui/BakedOpDispatcher.cpp | 9 ++---- libs/hwui/DisplayListCanvas.cpp | 5 ++- libs/hwui/DisplayListCanvas.h | 4 --- libs/hwui/DisplayListOp.h | 10 +++--- libs/hwui/FontRenderer.cpp | 18 +++++------ libs/hwui/FontRenderer.h | 8 ++--- libs/hwui/OpenGLRenderer.cpp | 20 ++++++------ libs/hwui/OpenGLRenderer.h | 6 ++-- libs/hwui/Properties.h | 3 -- libs/hwui/TextDropShadowCache.cpp | 14 ++++----- libs/hwui/TextDropShadowCache.h | 37 ++++++++++------------ libs/hwui/font/Font.cpp | 28 ++++++++--------- libs/hwui/font/Font.h | 10 +++--- libs/hwui/font/FontUtil.h | 23 ++------------ libs/hwui/tests/common/TestUtils.cpp | 38 +++++++++++++++-------- libs/hwui/tests/common/TestUtils.h | 4 +++ libs/hwui/tests/unit/TextDropShadowCacheTests.cpp | 28 +++++++++-------- 17 files changed, 125 insertions(+), 140 deletions(-) (limited to 'libs/hwui/TextDropShadowCache.cpp') diff --git a/libs/hwui/BakedOpDispatcher.cpp b/libs/hwui/BakedOpDispatcher.cpp index e3a5f3eeeac1..f83e1faf9c8a 100644 --- a/libs/hwui/BakedOpDispatcher.cpp +++ b/libs/hwui/BakedOpDispatcher.cpp @@ -201,8 +201,7 @@ static void renderTextShadow(BakedOpRenderer& renderer, FontRenderer& fontRender renderer.caches().dropShadowCache.setFontRenderer(fontRenderer); ShadowTexture* texture = renderer.caches().dropShadowCache.get( - op.paint, (const char*) op.glyphs, - op.glyphCount, textShadow.radius, op.positions); + op.paint, op.glyphs, op.glyphCount, textShadow.radius, op.positions); // If the drop shadow exceeds the max texture size or couldn't be // allocated, skip drawing if (!texture) return; @@ -277,8 +276,7 @@ static void renderTextOp(BakedOpRenderer& renderer, const TextOp& op, const Bake bool forceFinish = (renderType == TextRenderType::Flush); bool mustDirtyRenderTarget = renderer.offscreenRenderTarget(); const Rect* localOpClip = pureTranslate ? &state.computedState.clipRect() : nullptr; - fontRenderer.renderPosText(op.paint, localOpClip, - (const char*) op.glyphs, op.glyphCount, x, y, + fontRenderer.renderPosText(op.paint, localOpClip, op.glyphs, op.glyphCount, x, y, op.positions, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor, forceFinish); if (mustDirtyRenderTarget) { @@ -701,8 +699,7 @@ void BakedOpDispatcher::onTextOnPathOp(BakedOpRenderer& renderer, const TextOnPa bool mustDirtyRenderTarget = renderer.offscreenRenderTarget(); const Rect localSpaceClip = state.computedState.computeLocalSpaceClip(); - if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip, - reinterpret_cast(op.glyphs), op.glyphCount, + if (fontRenderer.renderTextOnPath(op.paint, &localSpaceClip, op.glyphs, op.glyphCount, op.path, op.hOffset, op.vOffset, mustDirtyRenderTarget ? &layerBounds : nullptr, &functor)) { if (mustDirtyRenderTarget) { diff --git a/libs/hwui/DisplayListCanvas.cpp b/libs/hwui/DisplayListCanvas.cpp index 3db14b55cff6..00560d7df454 100644 --- a/libs/hwui/DisplayListCanvas.cpp +++ b/libs/hwui/DisplayListCanvas.cpp @@ -428,7 +428,7 @@ void DisplayListCanvas::drawTextOnPath(const uint16_t* glyphs, int count, if (!glyphs || count <= 0) return; int bytesCount = 2 * count; - DrawOp* op = new (alloc()) DrawTextOnPathOp(refText((const char*) glyphs, bytesCount), + DrawOp* op = new (alloc()) DrawTextOnPathOp(refBuffer(glyphs, count), bytesCount, count, refPath(&path), hOffset, vOffset, refPaint(&paint)); addDrawOp(op); @@ -442,11 +442,10 @@ void DisplayListCanvas::drawText(const uint16_t* glyphs, const float* positions, if (!glyphs || count <= 0 || PaintUtils::paintWillNotDrawText(paint)) return; int bytesCount = count * 2; - const char* text = refText((const char*) glyphs, bytesCount); positions = refBuffer(positions, count * 2); Rect bounds(boundsLeft, boundsTop, boundsRight, boundsBottom); - DrawOp* op = new (alloc()) DrawTextOp(text, bytesCount, count, + DrawOp* op = new (alloc()) DrawTextOp(refBuffer(glyphs, count), bytesCount, count, x, y, positions, refPaint(&paint), totalAdvance, bounds); addDrawOp(op); drawTextDecorations(x, y, totalAdvance, paint); diff --git a/libs/hwui/DisplayListCanvas.h b/libs/hwui/DisplayListCanvas.h index 06e72a06eaca..e5711e35a88b 100644 --- a/libs/hwui/DisplayListCanvas.h +++ b/libs/hwui/DisplayListCanvas.h @@ -256,10 +256,6 @@ private: return dstBuffer; } - inline char* refText(const char* text, size_t byteLength) { - return (char*) refBuffer((uint8_t*)text, byteLength); - } - inline const SkPath* refPath(const SkPath* path) { if (!path) return nullptr; diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 92217edc2f16..20501ba3c1d9 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -1229,7 +1229,7 @@ public: class DrawSomeTextOp : public DrawOp { public: - DrawSomeTextOp(const char* text, int bytesCount, int count, const SkPaint* paint) + DrawSomeTextOp(const glyph_t* text, int bytesCount, int count, const SkPaint* paint) : DrawOp(paint), mText(text), mBytesCount(bytesCount), mCount(count) {}; virtual void output(int level, uint32_t logFlags) const override { @@ -1251,14 +1251,14 @@ public: } protected: - const char* mText; + const glyph_t* mText; int mBytesCount; int mCount; }; class DrawTextOnPathOp : public DrawSomeTextOp { public: - DrawTextOnPathOp(const char* text, int bytesCount, int count, + DrawTextOnPathOp(const glyph_t* text, int bytesCount, int count, const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) : DrawSomeTextOp(text, bytesCount, count, paint), mPath(path), mHOffset(hOffset), mVOffset(vOffset) { @@ -1280,7 +1280,7 @@ private: class DrawTextOp : public DrawStrokableOp { public: - DrawTextOp(const char* text, int bytesCount, int count, float x, float y, + DrawTextOp(const glyph_t* text, int bytesCount, int count, float x, float y, const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds) : DrawStrokableOp(bounds, paint), mText(text), mBytesCount(bytesCount), mCount(count), mX(x), mY(y), mPositions(positions), mTotalAdvance(totalAdvance) { @@ -1341,7 +1341,7 @@ public: virtual const char* name() override { return "DrawText"; } private: - const char* mText; + const glyph_t* mText; int mBytesCount; int mCount; float mX; diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 68bae6dc47f6..1b618c6a71c6 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -557,7 +557,7 @@ void FontRenderer::setFont(const SkPaint* paint, const SkMatrix& matrix) { mCurrentFont = Font::create(this, paint, matrix); } -FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text, +FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const glyph_t *glyphs, int numGlyphs, float radius, const float* positions) { checkInit(); @@ -577,7 +577,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co mBounds = nullptr; Rect bounds; - mCurrentFont->measure(paint, text, numGlyphs, &bounds, positions); + mCurrentFont->measure(paint, glyphs, numGlyphs, &bounds, positions); uint32_t intRadius = Blur::convertRadiusToInt(radius); uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius; @@ -609,7 +609,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co // text has non-whitespace, so draw and blur to create the shadow // NOTE: bounds.isEmpty() can't be used here, since vertical coordinates are inverted // TODO: don't draw pure whitespace in the first place, and avoid needing this check - mCurrentFont->render(paint, text, numGlyphs, penX, penY, + mCurrentFont->render(paint, glyphs, numGlyphs, penX, penY, Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, nullptr, positions); // Unbind any PBO we might have used @@ -643,17 +643,17 @@ void FontRenderer::finishRender() { issueDrawCommand(); } -void FontRenderer::precache(const SkPaint* paint, const char* text, int numGlyphs, +void FontRenderer::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, const SkMatrix& matrix) { Font* font = Font::create(this, paint, matrix); - font->precache(paint, text, numGlyphs); + font->precache(paint, glyphs, numGlyphs); } void FontRenderer::endPrecaching() { checkTextureUpdate(); } -bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const char *text, +bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs, int numGlyphs, int x, int y, const float* positions, Rect* bounds, TextDrawFunctor* functor, bool forceFinish) { if (!mCurrentFont) { @@ -662,7 +662,7 @@ bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const c } initRender(clip, bounds, functor); - mCurrentFont->render(paint, text, numGlyphs, x, y, positions); + mCurrentFont->render(paint, glyphs, numGlyphs, x, y, positions); if (forceFinish) { finishRender(); @@ -671,7 +671,7 @@ bool FontRenderer::renderPosText(const SkPaint* paint, const Rect* clip, const c return mDrawn; } -bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text, +bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs, int numGlyphs, const SkPath* path, float hOffset, float vOffset, Rect* bounds, TextDrawFunctor* functor) { if (!mCurrentFont) { @@ -680,7 +680,7 @@ bool FontRenderer::renderTextOnPath(const SkPaint* paint, const Rect* clip, cons } initRender(clip, bounds, functor); - mCurrentFont->render(paint, text, numGlyphs, path, hOffset, vOffset); + mCurrentFont->render(paint, glyphs, numGlyphs, path, hOffset, vOffset); finishRender(); return mDrawn; diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index 99944985cda8..e10a81b8ccd8 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -104,14 +104,14 @@ public: void setFont(const SkPaint* paint, const SkMatrix& matrix); - void precache(const SkPaint* paint, const char* text, int numGlyphs, const SkMatrix& matrix); + void precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, const SkMatrix& matrix); void endPrecaching(); - bool renderPosText(const SkPaint* paint, const Rect* clip, const char *text, + bool renderPosText(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs, int numGlyphs, int x, int y, const float* positions, Rect* outBounds, TextDrawFunctor* functor, bool forceFinish = true); - bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const char *text, + bool renderTextOnPath(const SkPaint* paint, const Rect* clip, const glyph_t* glyphs, int numGlyphs, const SkPath* path, float hOffset, float vOffset, Rect* outBounds, TextDrawFunctor* functor); @@ -125,7 +125,7 @@ public: // After renderDropShadow returns, the called owns the memory in DropShadow.image // and is responsible for releasing it when it's done with it - DropShadow renderDropShadow(const SkPaint* paint, const char *text, int numGlyphs, + DropShadow renderDropShadow(const SkPaint* paint, const glyph_t *glyphs, int numGlyphs, float radius, const float* positions); void setTextureFiltering(bool linearFiltering) { diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 587be92b4cbc..b7a5923cdd65 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -1949,7 +1949,7 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, } } -void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, +void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const glyph_t* glyphs, int count, const float* positions, FontRenderer& fontRenderer, int alpha, float x, float y) { mCaches.textureState().activateTexture(0); @@ -1963,7 +1963,7 @@ void OpenGLRenderer::drawTextShadow(const SkPaint* paint, const char* text, // if shader-based correction is enabled mCaches.dropShadowCache.setFontRenderer(fontRenderer); ShadowTexture* texture = mCaches.dropShadowCache.get( - paint, text, count, textShadow.radius, positions); + paint, glyphs, count, textShadow.radius, positions); // If the drop shadow exceeds the max texture size or couldn't be // allocated, skip drawing if (!texture) return; @@ -2084,14 +2084,14 @@ void OpenGLRenderer::setProjectionPathMask(LinearAllocator& allocator, const SkP mState.setProjectionPathMask(allocator, path); } -void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y, +void OpenGLRenderer::drawText(const glyph_t* glyphs, int bytesCount, int count, float x, float y, const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode) { if (drawOpMode == DrawOpMode::kImmediate) { // The checks for corner-case ignorable text and quick rejection is only done for immediate // drawing as ops from DeferredDisplayList are already filtered for these - if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint) || + if (glyphs == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint) || quickRejectSetupScissor(bounds)) { return; } @@ -2115,7 +2115,7 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float if (CC_UNLIKELY(PaintUtils::hasTextShadow(paint))) { fontRenderer.setFont(paint, SkMatrix::I()); - drawTextShadow(paint, text, count, positions, fontRenderer, + drawTextShadow(paint, glyphs, count, positions, fontRenderer, alpha, oldX, oldY); } @@ -2156,10 +2156,10 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float if (CC_UNLIKELY(paint->getTextAlign() != SkPaint::kLeft_Align)) { SkPaint paintCopy(*paint); paintCopy.setTextAlign(SkPaint::kLeft_Align); - status = fontRenderer.renderPosText(&paintCopy, clip, text, count, x, y, + status = fontRenderer.renderPosText(&paintCopy, clip, glyphs, count, x, y, positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish); } else { - status = fontRenderer.renderPosText(paint, clip, text, count, x, y, + status = fontRenderer.renderPosText(paint, clip, glyphs, count, x, y, positions, hasActiveLayer ? &layerBounds : nullptr, &functor, forceFinish); } @@ -2173,9 +2173,9 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float mDirty = true; } -void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, +void OpenGLRenderer::drawTextOnPath(const glyph_t* glyphs, int bytesCount, int count, const SkPath* path, float hOffset, float vOffset, const SkPaint* paint) { - if (text == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) { + if (glyphs == nullptr || count == 0 || mState.currentlyIgnored() || canSkipText(paint)) { return; } @@ -2198,7 +2198,7 @@ void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, const Rect* clip = &writableSnapshot()->getLocalClip(); Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); - if (fontRenderer.renderTextOnPath(paint, clip, text, count, path, + if (fontRenderer.renderTextOnPath(paint, clip, glyphs, count, path, hOffset, vOffset, hasLayer() ? &bounds : nullptr, &functor)) { dirtyLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, *currentTransform()); mDirty = true; diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 84bc9b059d33..dacd8ccaa6ea 100755 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -191,9 +191,9 @@ public: void drawPath(const SkPath* path, const SkPaint* paint); void drawLines(const float* points, int count, const SkPaint* paint); void drawPoints(const float* points, int count, const SkPaint* paint); - void drawTextOnPath(const char* text, int bytesCount, int count, const SkPath* path, + void drawTextOnPath(const glyph_t* glyphs, int bytesCount, int count, const SkPath* path, float hOffset, float vOffset, const SkPaint* paint); - void drawText(const char* text, int bytesCount, int count, float x, float y, + void drawText(const glyph_t* glyphs, int bytesCount, int count, float x, float y, const float* positions, const SkPaint* paint, float totalAdvance, const Rect& bounds, DrawOpMode drawOpMode = DrawOpMode::kImmediate); void drawRects(const float* rects, int count, const SkPaint* paint); @@ -647,7 +647,7 @@ private: * @param x The x coordinate where the shadow will be drawn * @param y The y coordinate where the shadow will be drawn */ - void drawTextShadow(const SkPaint* paint, const char* text, int count, + void drawTextShadow(const SkPaint* paint, const glyph_t* glyphs, int count, const float* positions, FontRenderer& fontRenderer, int alpha, float x, float y); diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 3e111516ac63..249b5b07fa11 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -31,9 +31,6 @@ namespace uirenderer { // Compile-time properties /////////////////////////////////////////////////////////////////////////////// -// If turned on, text is interpreted as glyphs instead of UTF-16 -#define RENDER_TEXT_AS_GLYPHS 1 - // Textures used by layers must have dimensions multiples of this number #define LAYER_SIZE 64 diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp index fe4b3d7507b2..e1f0b2a20172 100644 --- a/libs/hwui/TextDropShadowCache.cpp +++ b/libs/hwui/TextDropShadowCache.cpp @@ -37,9 +37,9 @@ hash_t ShadowText::hash() const { hash = JenkinsHashMix(hash, flags); hash = JenkinsHashMix(hash, android::hash_type(italicStyle)); hash = JenkinsHashMix(hash, android::hash_type(scaleX)); - if (text) { + if (glyphs) { hash = JenkinsHashMixShorts( - hash, reinterpret_cast(text), glyphCount); + hash, reinterpret_cast(glyphs), glyphCount); } if (positions) { for (uint32_t i = 0; i < glyphCount * 2; i++) { @@ -71,11 +71,11 @@ int ShadowText::compare(const ShadowText& lhs, const ShadowText& rhs) { if (lhs.scaleX < rhs.scaleX) return -1; if (lhs.scaleX > rhs.scaleX) return +1; - if (lhs.text != rhs.text) { - if (!lhs.text) return -1; - if (!rhs.text) return +1; + if (lhs.glyphs != rhs.glyphs) { + if (!lhs.glyphs) return -1; + if (!rhs.glyphs) return +1; - deltaInt = memcmp(lhs.text, rhs.text, lhs.glyphCount * sizeof(glyph_t)); + deltaInt = memcmp(lhs.glyphs, rhs.glyphs, lhs.glyphCount * sizeof(glyph_t)); if (deltaInt != 0) return deltaInt; } @@ -145,7 +145,7 @@ void TextDropShadowCache::clear() { mCache.clear(); } -ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const char* glyphs, int numGlyphs, +ShadowTexture* TextDropShadowCache::get(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, float radius, const float* positions) { ShadowText entry(paint, radius, numGlyphs, glyphs, positions); ShadowTexture* texture = mCache.get(entry); diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h index cf647882e5a7..d536c40756ff 100644 --- a/libs/hwui/TextDropShadowCache.h +++ b/libs/hwui/TextDropShadowCache.h @@ -35,26 +35,21 @@ class FontRenderer; struct ShadowText { ShadowText(): glyphCount(0), radius(0.0f), textSize(0.0f), typeface(nullptr), - flags(0), italicStyle(0.0f), scaleX(0), text(nullptr), positions(nullptr) { + flags(0), italicStyle(0.0f), scaleX(0), glyphs(nullptr), positions(nullptr) { } // len is the number of bytes in text - ShadowText(const SkPaint* paint, float radius, uint32_t glyphCount, const char* srcText, - const float* positions): - glyphCount(glyphCount), radius(radius), positions(positions) { - // TODO: Propagate this through the API, we should not cast here - text = (const char16_t*) srcText; - - textSize = paint->getTextSize(); - typeface = paint->getTypeface(); - - flags = 0; - if (paint->isFakeBoldText()) { - flags |= Font::kFakeBold; - } - - italicStyle = paint->getTextSkewX(); - scaleX = paint->getTextScaleX(); + ShadowText(const SkPaint* paint, float radius, uint32_t glyphCount, const glyph_t* srcGlyphs, + const float* positions) + : glyphCount(glyphCount) + , radius(radius) + , textSize(paint->getTextSize()) + , typeface(paint->getTypeface()) + , flags(paint->isFakeBoldText() ? Font::kFakeBold : 0) + , italicStyle(paint->getTextSkewX()) + , scaleX(paint->getTextScaleX()) + , glyphs(srcGlyphs) + , positions(positions) { } ~ShadowText() { @@ -73,8 +68,8 @@ struct ShadowText { } void copyTextLocally() { - str.setTo((const char16_t*) text, glyphCount); - text = str.string(); + str.setTo(reinterpret_cast(glyphs), glyphCount); + glyphs = reinterpret_cast(str.string()); if (positions != nullptr) { positionsCopy.clear(); positionsCopy.appendArray(positions, glyphCount * 2); @@ -89,7 +84,7 @@ struct ShadowText { uint32_t flags; float italicStyle; float scaleX; - const char16_t* text; + const glyph_t* glyphs; const float* positions; // Not directly used to compute the cache key @@ -135,7 +130,7 @@ public: */ void operator()(ShadowText& text, ShadowTexture*& texture) override; - ShadowTexture* get(const SkPaint* paint, const char* text, + ShadowTexture* get(const SkPaint* paint, const glyph_t* text, int numGlyphs, float radius, const float* positions); /** diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp index dc82041e8f89..9a825fdec601 100644 --- a/libs/hwui/font/Font.cpp +++ b/libs/hwui/font/Font.cpp @@ -291,15 +291,15 @@ CachedGlyphInfo* Font::getCachedGlyph(const SkPaint* paint, glyph_t textUnit, bo return cachedGlyph; } -void Font::render(const SkPaint* paint, const char *text, +void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, int x, int y, const float* positions) { - render(paint, text, numGlyphs, x, y, FRAMEBUFFER, nullptr, + render(paint, glyphs, numGlyphs, x, y, FRAMEBUFFER, nullptr, 0, 0, nullptr, positions); } -void Font::render(const SkPaint* paint, const char *text, int numGlyphs, +void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, const SkPath* path, float hOffset, float vOffset) { - if (numGlyphs == 0 || text == nullptr) { + if (numGlyphs == 0 || glyphs == nullptr) { return; } @@ -315,7 +315,7 @@ void Font::render(const SkPaint* paint, const char *text, int numGlyphs, float pathLength = SkScalarToFloat(measure.getLength()); if (paint->getTextAlign() != SkPaint::kLeft_Align) { - float textWidth = SkScalarToFloat(paint->measureText(text, numGlyphs * 2)); + float textWidth = SkScalarToFloat(paint->measureText(glyphs, numGlyphs * 2)); float pathOffset = pathLength; if (paint->getTextAlign() == SkPaint::kCenter_Align) { textWidth *= 0.5f; @@ -325,7 +325,7 @@ void Font::render(const SkPaint* paint, const char *text, int numGlyphs, } while (glyphsCount < numGlyphs && penX < pathLength) { - glyph_t glyph = GET_GLYPH(text); + glyph_t glyph = *(glyphs++); if (IS_END_OF_STRING(glyph)) { break; @@ -345,26 +345,26 @@ void Font::render(const SkPaint* paint, const char *text, int numGlyphs, } } -void Font::measure(const SkPaint* paint, const char* text, +void Font::measure(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, Rect *bounds, const float* positions) { if (bounds == nullptr) { ALOGE("No return rectangle provided to measure text"); return; } bounds->set(1e6, -1e6, -1e6, 1e6); - render(paint, text, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions); + render(paint, glyphs, numGlyphs, 0, 0, MEASURE, nullptr, 0, 0, bounds, positions); } -void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) { +void Font::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs) { ATRACE_NAME("Precache Glyphs"); - if (numGlyphs == 0 || text == nullptr) { + if (numGlyphs == 0 || glyphs == nullptr) { return; } int glyphsCount = 0; while (glyphsCount < numGlyphs) { - glyph_t glyph = GET_GLYPH(text); + glyph_t glyph = *(glyphs++); // Reached the end of the string if (IS_END_OF_STRING(glyph)) { @@ -376,10 +376,10 @@ void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) { } } -void Font::render(const SkPaint* paint, const char* text, +void Font::render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) { - if (numGlyphs == 0 || text == nullptr) { + if (numGlyphs == 0 || glyphs == nullptr) { return; } @@ -396,7 +396,7 @@ void Font::render(const SkPaint* paint, const char* text, int glyphsCount = 0; while (glyphsCount < numGlyphs) { - glyph_t glyph = GET_GLYPH(text); + glyph_t glyph = *(glyphs++); // Reached the end of the string if (IS_END_OF_STRING(glyph)) { diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h index 59518a1fb8ee..e8882d9f4bda 100644 --- a/libs/hwui/font/Font.h +++ b/libs/hwui/font/Font.h @@ -82,10 +82,10 @@ public: ~Font(); - void render(const SkPaint* paint, const char* text, + void render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, int x, int y, const float* positions); - void render(const SkPaint* paint, const char* text, + void render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, const SkPath* path, float hOffset, float vOffset); const Font::FontDescription& getDescription() const { @@ -111,13 +111,13 @@ private: MEASURE, }; - void precache(const SkPaint* paint, const char* text, int numGlyphs); + void precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs); - void render(const SkPaint* paint, const char *text, + void render(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions); - void measure(const SkPaint* paint, const char* text, + void measure(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs, Rect *bounds, const float* positions); void invalidateTextureCache(CacheTexture* cacheTexture = nullptr); diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h index 4e5debe33c4a..aa77d98c9343 100644 --- a/libs/hwui/font/FontUtil.h +++ b/libs/hwui/font/FontUtil.h @@ -40,26 +40,9 @@ #define CACHE_BLOCK_ROUNDING_SIZE 4 -#if RENDER_TEXT_AS_GLYPHS - typedef uint16_t glyph_t; - #define TO_GLYPH(g) g - #define GET_METRICS(cache, glyph) cache->getGlyphIDMetrics(glyph) - #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text) - #define IS_END_OF_STRING(glyph) false - - static inline glyph_t nextGlyph(const uint16_t** srcPtr) { - const uint16_t* src = *srcPtr; - glyph_t g = *src++; - *srcPtr = src; - return g; - } -#else - typedef SkUnichar glyph_t; - #define TO_GLYPH(g) ((SkUnichar) g) - #define GET_METRICS(cache, glyph) cache->getUnicharMetrics(glyph) - #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text) - #define IS_END_OF_STRING(glyph) glyph < 0 -#endif +typedef uint16_t glyph_t; +#define GET_METRICS(cache, glyph) cache->getGlyphIDMetrics(glyph) +#define IS_END_OF_STRING(glyph) false #define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16) diff --git a/libs/hwui/tests/common/TestUtils.cpp b/libs/hwui/tests/common/TestUtils.cpp index 5ed7aa464026..3440d03b4fc5 100644 --- a/libs/hwui/tests/common/TestUtils.cpp +++ b/libs/hwui/tests/common/TestUtils.cpp @@ -58,27 +58,22 @@ sp TestUtils::createTextureLayerUpdater( return layerUpdater; } -void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text, - const SkPaint& paint, float x, float y) { - // drawing text requires GlyphID TextEncoding (which JNI layer would have done) - LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding, - "must use glyph encoding"); +void TestUtils::layoutTextUnscaled(const SkPaint& paint, const char* text, + std::vector* outGlyphs, std::vector* outPositions, + float* outTotalAdvance, Rect* outBounds) { + Rect bounds; + float totalAdvance = 0; SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry); SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I()); - - float totalAdvance = 0; - std::vector glyphs; - std::vector positions; - Rect bounds; while (*text != '\0') { SkUnichar unichar = SkUTF8_NextUnichar(&text); glyph_t glyph = autoCache.getCache()->unicharToGlyph(unichar); autoCache.getCache()->unicharToGlyph(unichar); // push glyph and its relative position - glyphs.push_back(glyph); - positions.push_back(totalAdvance); - positions.push_back(0); + outGlyphs->push_back(glyph); + outPositions->push_back(totalAdvance); + outPositions->push_back(0); // compute bounds SkGlyph skGlyph = autoCache.getCache()->getUnicharMetrics(unichar); @@ -91,6 +86,23 @@ void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text, paint.getTextWidths(&glyph, sizeof(glyph), &skWidth, NULL); totalAdvance += skWidth; } + *outBounds = bounds; + *outTotalAdvance = totalAdvance; +} + +void TestUtils::drawTextToCanvas(TestCanvas* canvas, const char* text, + const SkPaint& paint, float x, float y) { + // drawing text requires GlyphID TextEncoding (which JNI layer would have done) + LOG_ALWAYS_FATAL_IF(paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding, + "must use glyph encoding"); + SkSurfaceProps surfaceProps(0, kUnknown_SkPixelGeometry); + SkAutoGlyphCacheNoGamma autoCache(paint, &surfaceProps, &SkMatrix::I()); + + std::vector glyphs; + std::vector positions; + float totalAdvance; + Rect bounds; + layoutTextUnscaled(paint, text, &glyphs, &positions, &totalAdvance, &bounds); // apply alignment via x parameter (which JNI layer would have done) if (paint.getTextAlign() == SkPaint::kCenter_Align) { diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index ae0814241055..6f237059dd33 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -205,6 +205,10 @@ public: static SkColor interpolateColor(float fraction, SkColor start, SkColor end); + static void layoutTextUnscaled(const SkPaint& paint, const char* text, + std::vector* outGlyphs, std::vector* outPositions, + float* outTotalAdvance, Rect* outBounds); + static void drawTextToCanvas(TestCanvas* canvas, const char* text, const SkPaint& paint, float x, float y); diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp index c54f2c365ee2..0d26df203f02 100644 --- a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp +++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp @@ -21,29 +21,31 @@ #include "utils/Blur.h" #include "tests/common/TestUtils.h" -#include #include using namespace android; using namespace android::uirenderer; RENDERTHREAD_TEST(TextDropShadowCache, addRemove) { + SkPaint paint; + paint.setTextSize(20); + GammaFontRenderer gammaFontRenderer; FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer(); - TextDropShadowCache cache(5000); + fontRenderer.setFont(&paint, SkMatrix::I()); + TextDropShadowCache cache(MB(5)); cache.setFontRenderer(fontRenderer); - SkPaint paint; - paint.setLooper(SkBlurDrawLooper::Create((SkColor)0xFFFFFFFF, - Blur::convertRadiusToSigma(10), 10, 10))->unref(); - std::string msg("This is a test"); - std::unique_ptr positions(new float[msg.length()]); - for (size_t i = 0; i < msg.length(); i++) { - positions[i] = i * 10.0f; - } - fontRenderer.setFont(&paint, SkMatrix::I()); - ShadowTexture* texture = cache.get(&paint, msg.c_str(), msg.length(), - 10.0f, positions.get()); + std::vector glyphs; + std::vector positions; + float totalAdvance; + uirenderer::Rect bounds; + TestUtils::layoutTextUnscaled(paint, "This is a test", + &glyphs, &positions, &totalAdvance, &bounds); + EXPECT_TRUE(bounds.contains(5, -10, 100, 0)) << "Expect input to be nontrivially sized"; + + ShadowTexture* texture = cache.get(&paint, glyphs.data(), glyphs.size(), 10, positions.data()); + ASSERT_TRUE(texture); ASSERT_FALSE(texture->cleanup); ASSERT_EQ((uint32_t) texture->objectSize(), cache.getSize()); -- cgit v1.2.3-59-g8ed1b