From 22886c493596655d8fd9512951f9010869b7fbc5 Mon Sep 17 00:00:00 2001 From: Matt Sarett Date: Tue, 22 Nov 2016 11:31:41 -0500 Subject: [PATCH] Add SkOverdrawCanvas to detect overdraw This is the first part of a multi-part change to detect and display gpu overdraw on Android. BUG:32370375 GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=4907 Change-Id: Ibba9d7343f2fd57397fa1168a5a5b1ef6ef91287 Reviewed-on: https://skia-review.googlesource.com/4907 Reviewed-by: Ben Wagner Reviewed-by: Brian Salomon Commit-Queue: Matt Sarett --- gn/core.gni | 2 + include/core/SkCanvas.h | 1 + src/core/SkOverdrawCanvas.cpp | 265 ++++++++++++++++++++++++++++++++++++++++++ src/core/SkOverdrawCanvas.h | 69 +++++++++++ src/image/SkSurface.cpp | 5 + src/image/SkSurface_Base.h | 7 ++ tests/SurfaceTest.cpp | 28 +++++ 7 files changed, 377 insertions(+) create mode 100644 src/core/SkOverdrawCanvas.cpp create mode 100644 src/core/SkOverdrawCanvas.h diff --git a/gn/core.gni b/gn/core.gni index fcc8de1..d88ac5a 100644 --- a/gn/core.gni +++ b/gn/core.gni @@ -213,6 +213,8 @@ skia_core_sources = [ "$_src/core/SkOpts.cpp", "$_src/core/SkOpts.h", "$_src/core/SkOrderedReadBuffer.h", + "$_src/core/SkOverdrawCanvas.cpp", + "$_src/core/SkOverdrawCanvas.h", "$_src/core/SkPaint.cpp", "$_src/core/SkPaintDefaults.h", "$_src/core/SkPaintPriv.cpp", diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index 95667e4..aabc7d8 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -1628,6 +1628,7 @@ private: friend class SkPicturePlayback; // SaveFlagsToSaveLayerFlags friend class SkPipeCanvas; // InitFlags friend class SkDeferredCanvas; // For use of resetForNextPicture + friend class SkOverdrawCanvas; enum InitFlags { kDefault_InitFlags = 0, diff --git a/src/core/SkOverdrawCanvas.cpp b/src/core/SkOverdrawCanvas.cpp new file mode 100644 index 0000000..b8141c5 --- /dev/null +++ b/src/core/SkOverdrawCanvas.cpp @@ -0,0 +1,265 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkColorFilter.h" +#include "SkFindAndPlaceGlyph.h" +#include "SkOverdrawCanvas.h" +#include "SkPatchUtils.h" +#include "SkPath.h" +#include "SkRRect.h" +#include "SkRSXform.h" +#include "SkTextBlob.h" +#include "SkTextBlobRunIterator.h" + +namespace { +class ProcessOneGlyphBounds { +public: + ProcessOneGlyphBounds(SkOverdrawCanvas* canvas) + : fCanvas(canvas) + {} + + void operator()(const SkGlyph& glyph, SkPoint position, SkPoint rounding) { + int left = SkScalarFloorToInt(position.fX) + glyph.fLeft; + int top = SkScalarFloorToInt(position.fY) + glyph.fTop; + int right = left + glyph.fWidth; + int bottom = top + glyph.fHeight; + fCanvas->onDrawRect(SkRect::MakeLTRB(left, top, right, bottom), SkPaint()); + } + +private: + SkOverdrawCanvas* fCanvas; +}; +}; + +SkOverdrawCanvas::SkOverdrawCanvas(SkCanvas* canvas) + : INHERITED(canvas->onImageInfo().width(), canvas->onImageInfo().height()) + , fCanvas(canvas) +{ + static constexpr float kIncrementAlpha[] = { + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, + }; + + fPaint.setAntiAlias(false); + fPaint.setBlendMode(SkBlendMode::kPlus); + fPaint.setColorFilter(SkColorFilter::MakeMatrixFilterRowMajor255(kIncrementAlpha)); +} + +void SkOverdrawCanvas::onDrawDRRect(const SkRRect& outer , const SkRRect&, const SkPaint&) { + fCanvas->onDrawRect(outer.getBounds(), fPaint); +} + +void SkOverdrawCanvas::onDrawText(const void* text, size_t byteLength, SkScalar x, SkScalar y, + const SkPaint& paint) { + ProcessOneGlyphBounds processBounds(this); + SkSurfaceProps props(0, kUnknown_SkPixelGeometry); + this->getProps(&props); + SkAutoGlyphCache cache(paint, &props, 0, &this->getTotalMatrix()); + SkFindAndPlaceGlyph::ProcessText(paint.getTextEncoding(), (const char*) text, byteLength, + SkPoint::Make(x, y), SkMatrix(), paint.getTextAlign(), + cache.get(), processBounds); +} + +void SkOverdrawCanvas::drawPosTextCommon(const void* text, size_t byteLength, const SkScalar pos[], + int scalarsPerPos, const SkPoint& offset, + const SkPaint& paint) { + ProcessOneGlyphBounds processBounds(this); + SkSurfaceProps props(0, kUnknown_SkPixelGeometry); + this->getProps(&props); + SkAutoGlyphCache cache(paint, &props, 0, &this->getTotalMatrix()); + SkFindAndPlaceGlyph::ProcessPosText(paint.getTextEncoding(), (const char*) text, byteLength, + SkPoint::Make(0, 0), SkMatrix(), (const SkScalar*) pos, 2, + paint.getTextAlign(), cache.get(), processBounds); +} + +void SkOverdrawCanvas::onDrawPosText(const void* text, size_t byteLength, const SkPoint pos[], + const SkPaint& paint) { + this->drawPosTextCommon(text, byteLength, (SkScalar*) pos, 2, SkPoint::Make(0, 0), paint); +} + +void SkOverdrawCanvas::onDrawPosTextH(const void* text, size_t byteLength, const SkScalar xs[], + SkScalar y, const SkPaint& paint) { + this->drawPosTextCommon(text, byteLength, (SkScalar*) xs, 1, SkPoint::Make(0, y), paint); +} + +void SkOverdrawCanvas::onDrawTextOnPath(const void* text, size_t byteLength, const SkPath& path, + const SkMatrix* matrix, const SkPaint& paint) { + SkASSERT(false); + return; +} + +typedef int (*CountTextProc)(const char* text); +static int count_utf16(const char* text) { + const uint16_t* prev = (uint16_t*)text; + (void)SkUTF16_NextUnichar(&prev); + return SkToInt((const char*)prev - text); +} +static int return_4(const char* text) { return 4; } +static int return_2(const char* text) { return 2; } + +void SkOverdrawCanvas::onDrawTextRSXform(const void* text, size_t byteLength, + const SkRSXform xform[], const SkRect*, + const SkPaint& paint) { + CountTextProc proc = nullptr; + switch (paint.getTextEncoding()) { + case SkPaint::kUTF8_TextEncoding: + proc = SkUTF8_CountUTF8Bytes; + break; + case SkPaint::kUTF16_TextEncoding: + proc = count_utf16; + break; + case SkPaint::kUTF32_TextEncoding: + proc = return_4; + break; + case SkPaint::kGlyphID_TextEncoding: + proc = return_2; + break; + } + SkASSERT(proc); + + SkMatrix matrix; + const void* stopText = (const char*)text + byteLength; + while ((const char*)text < (const char*)stopText) { + matrix.setRSXform(*xform++); + matrix.setConcat(this->getTotalMatrix(), matrix); + int subLen = proc((const char*)text); + + this->save(); + this->concat(matrix); + this->drawText(text, subLen, 0, 0, paint); + this->restore(); + + text = (const char*)text + subLen; + } +} + +void SkOverdrawCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, + const SkPaint& paint) { + SkPaint runPaint = paint; + SkTextBlobRunIterator it(blob); + for (;!it.done(); it.next()) { + size_t textLen = it.glyphCount() * sizeof(uint16_t); + const SkPoint& offset = it.offset(); + it.applyFontToPaint(&runPaint); + switch (it.positioning()) { + case SkTextBlob::kDefault_Positioning: + this->onDrawText(it.glyphs(), textLen, x + offset.x(), y + offset.y(), runPaint); + break; + case SkTextBlob::kHorizontal_Positioning: + this->drawPosTextCommon(it.glyphs(), textLen, it.pos(), 1, + SkPoint::Make(x, y + offset.y()), runPaint); + break; + case SkTextBlob::kFull_Positioning: + this->drawPosTextCommon(it.glyphs(), textLen, it.pos(), 2, SkPoint::Make(x, y), + runPaint); + break; + default: + SkASSERT(false); + break; + } + } +} + +void SkOverdrawCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], + const SkPoint texCoords[4], SkBlendMode blendMode, + const SkPaint&) { + fCanvas->onDrawPatch(cubics, colors, texCoords, blendMode, fPaint); +} + +void SkOverdrawCanvas::onDrawPaint(const SkPaint&) { + fCanvas->onDrawPaint(fPaint); +} + +void SkOverdrawCanvas::onDrawRect(const SkRect& rect, const SkPaint&) { + fCanvas->onDrawRect(rect, fPaint); +} + +void SkOverdrawCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) { + fCanvas->onDrawRegion(region, fPaint); +} + +void SkOverdrawCanvas::onDrawOval(const SkRect& oval, const SkPaint&) { + fCanvas->onDrawOval(oval, fPaint); +} + +void SkOverdrawCanvas::onDrawArc(const SkRect& arc, SkScalar startAngle, SkScalar sweepAngle, + bool useCenter, const SkPaint&) { + fCanvas->onDrawArc(arc, startAngle, sweepAngle, useCenter, fPaint); +} + +void SkOverdrawCanvas::onDrawRRect(const SkRRect& rect, const SkPaint&) { + fCanvas->onDrawRRect(rect, fPaint); +} + +void SkOverdrawCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint points[], + const SkPaint&) { + fCanvas->onDrawPoints(mode, count, points, fPaint); +} + +void SkOverdrawCanvas::onDrawVertices(VertexMode vertexMode, int vertexCount, + const SkPoint vertices[], const SkPoint texs[], + const SkColor colors[], SkBlendMode blendMode, + const uint16_t indices[], int indexCount, const SkPaint&) { + fCanvas->onDrawVertices(vertexMode, vertexCount, vertices, texs, colors, blendMode, indices, + indexCount, fPaint); +} + +void SkOverdrawCanvas::onDrawAtlas(const SkImage* image, const SkRSXform xform[], + const SkRect texs[], const SkColor colors[], int count, + SkBlendMode mode, const SkRect* cull, const SkPaint*) { + fCanvas->onDrawAtlas(image, xform, texs, colors, count, mode, cull, &fPaint); +} + +void SkOverdrawCanvas::onDrawPath(const SkPath& path, const SkPaint&) { + fCanvas->onDrawPath(path, fPaint); +} + +void SkOverdrawCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint*) { + fCanvas->onDrawRect(SkRect::MakeXYWH(x, y, image->width(), image->height()), fPaint); +} + +void SkOverdrawCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, + const SkPaint*, SrcRectConstraint) { + fCanvas->onDrawRect(dst, fPaint); +} + +void SkOverdrawCanvas::onDrawImageNine(const SkImage*, const SkIRect&, const SkRect& dst, + const SkPaint*) { + fCanvas->onDrawRect(dst, fPaint); +} + +void SkOverdrawCanvas::onDrawImageLattice(const SkImage*, const Lattice&, const SkRect& dst, + const SkPaint*) { + fCanvas->onDrawRect(dst, fPaint); +} + +void SkOverdrawCanvas::onDrawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y, + const SkPaint*) { + fCanvas->onDrawRect(SkRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()), fPaint); +} + +void SkOverdrawCanvas::onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect& dst, + const SkPaint*, SrcRectConstraint) { + fCanvas->onDrawRect(dst, fPaint); +} + +void SkOverdrawCanvas::onDrawBitmapNine(const SkBitmap&, const SkIRect&, const SkRect& dst, + const SkPaint*) { + fCanvas->onDrawRect(dst, fPaint); +} + +void SkOverdrawCanvas::onDrawBitmapLattice(const SkBitmap&, const Lattice&, const SkRect& dst, + const SkPaint*) { + fCanvas->onDrawRect(dst, fPaint); +} + +void SkOverdrawCanvas::onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) { + SkASSERT(false); + return; +} diff --git a/src/core/SkOverdrawCanvas.h b/src/core/SkOverdrawCanvas.h new file mode 100644 index 0000000..65eabbc --- /dev/null +++ b/src/core/SkOverdrawCanvas.h @@ -0,0 +1,69 @@ +/* + * Copyright 2016 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkOverdrawCanvas_DEFINED +#define SkOverdrawCanvas_DEFINED + +#include "SkCanvas.h" + +/** + * Captures all drawing commands. Rather than draw the actual content, this device + * increments the alpha channel of each pixel every time it would have been touched + * by a draw call. This is useful for detecting overdraw. + */ +class SkOverdrawCanvas : public SkCanvas { +public: + /* Does not take ownership of canvas */ + SkOverdrawCanvas(SkCanvas*); + + void onDrawDRRect(const SkRRect&, const SkRRect&, const SkPaint&) override; + void onDrawText(const void*, size_t, SkScalar, SkScalar, const SkPaint&) override; + void onDrawPosText(const void*, size_t, const SkPoint[], const SkPaint&) override; + void onDrawPosTextH(const void*, size_t, const SkScalar[], SkScalar, const SkPaint&) override; + void onDrawTextOnPath(const void*, size_t, const SkPath&, const SkMatrix*, + const SkPaint&) override; + void onDrawTextRSXform(const void*, size_t, const SkRSXform[], const SkRect*, + const SkPaint&) override; + void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override; + void onDrawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode, + const SkPaint&) override; + void onDrawPaint(const SkPaint&) override; + void onDrawRect(const SkRect&, const SkPaint&) override; + void onDrawRegion(const SkRegion&, const SkPaint&) override; + void onDrawOval(const SkRect&, const SkPaint&) override; + void onDrawArc(const SkRect&, SkScalar, SkScalar, bool, const SkPaint&) override; + void onDrawRRect(const SkRRect&, const SkPaint&) override; + void onDrawPoints(PointMode, size_t, const SkPoint[], const SkPaint&) override; + void onDrawVertices(VertexMode, int, const SkPoint[], const SkPoint[], const SkColor[], + SkBlendMode, const uint16_t[], int, const SkPaint&) override; + void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], + int, SkBlendMode, const SkRect*, const SkPaint*) override; + void onDrawPath(const SkPath&, const SkPaint&) override; + void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override; + void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*, + SrcRectConstraint) override; + void onDrawImageNine(const SkImage*, const SkIRect&, const SkRect&, const SkPaint*) override; + void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override; + void onDrawBitmap(const SkBitmap&, SkScalar, SkScalar, const SkPaint*) override; + void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*, + SrcRectConstraint) override; + void onDrawBitmapNine(const SkBitmap&, const SkIRect&, const SkRect&, const SkPaint*) override; + void onDrawBitmapLattice(const SkBitmap&, const Lattice&, const SkRect&, + const SkPaint*) override; + void onDrawPicture(const SkPicture*, const SkMatrix*, const SkPaint*) override; + +private: + void drawPosTextCommon(const void*, size_t, const SkScalar[], int, const SkPoint&, + const SkPaint&); + + SkCanvas* fCanvas; + SkPaint fPaint; + + typedef SkCanvas INHERITED; +}; + +#endif diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp index 3d6670f..47583a1 100644 --- a/src/image/SkSurface.cpp +++ b/src/image/SkSurface.cpp @@ -9,6 +9,7 @@ #include "SkSurface_Base.h" #include "SkImagePriv.h" #include "SkCanvas.h" +#include "SkOverdrawCanvas.h" #include "SkFontLCDConfig.h" static SkPixelGeometry compute_default_geometry() { @@ -117,6 +118,10 @@ void SkSurface_Base::aboutToDraw(ContentChangeMode mode) { } } +std::unique_ptr SkSurface_Base::onMakeOverdrawCanvas() { + return std::unique_ptr(new SkOverdrawCanvas(this->getCachedCanvas())); +} + uint32_t SkSurface_Base::newGenerationID() { SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this); static int32_t gID; diff --git a/src/image/SkSurface_Base.h b/src/image/SkSurface_Base.h index a8c1d8f..f824bbb 100644 --- a/src/image/SkSurface_Base.h +++ b/src/image/SkSurface_Base.h @@ -80,6 +80,13 @@ public: */ virtual void onPrepareForExternalIO() {} + /** + * Creates an overdraw debugging canvas. + * Draws to this canvas will not actually draw any content. Instead, they will + * increment the alpha channel each time a pixel would have been touched. + * It may be efficient to use kAlpha8 as the color type on the surface. + */ + std::unique_ptr onMakeOverdrawCanvas(); inline SkCanvas* getCachedCanvas(); inline sk_sp refCachedImage(SkBudgeted, ForceUnique); diff --git a/tests/SurfaceTest.cpp b/tests/SurfaceTest.cpp index 2ac8d29..e613e1d 100644 --- a/tests/SurfaceTest.cpp +++ b/tests/SurfaceTest.cpp @@ -1021,3 +1021,31 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(SurfaceCreationWithColorSpace_Gpu, reporter, } } #endif + +static void test_overdraw_surface(skiatest::Reporter* r, SkSurface* surface) { + std::unique_ptr canvas = ((SkSurface_Base*) surface)->onMakeOverdrawCanvas(); + canvas->drawPaint(SkPaint()); + sk_sp image = surface->makeImageSnapshot(); + + SkBitmap bitmap; + image->asLegacyBitmap(&bitmap, SkImage::kRO_LegacyBitmapMode); + bitmap.lockPixels(); + for (int y = 0; y < 10; y++) { + for (int x = 0; x < 10; x++) { + REPORTER_ASSERT(r, 1 == SkGetPackedA32(*bitmap.getAddr32(x, y))); + } + } +} + +DEF_TEST(OverdrawSurface_Raster, r) { + sk_sp surface = create_surface(); + test_overdraw_surface(r, surface.get()); +} + +#if SK_SUPPORT_GPU +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(OverdrawSurface_Gpu, r, ctxInfo) { + GrContext* context = ctxInfo.grContext(); + sk_sp surface = create_gpu_surface(context); + test_overdraw_surface(r, surface.get()); +} +#endif -- 2.7.4