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 <bungeman@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Matt Sarett <msarett@google.com>
This commit is contained in:
parent
248ff02331
commit
22886c4935
@ -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",
|
||||
|
@ -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,
|
||||
|
265
src/core/SkOverdrawCanvas.cpp
Normal file
265
src/core/SkOverdrawCanvas.cpp
Normal file
@ -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;
|
||||
}
|
69
src/core/SkOverdrawCanvas.h
Normal file
69
src/core/SkOverdrawCanvas.h
Normal file
@ -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
|
@ -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<SkCanvas> SkSurface_Base::onMakeOverdrawCanvas() {
|
||||
return std::unique_ptr<SkCanvas>(new SkOverdrawCanvas(this->getCachedCanvas()));
|
||||
}
|
||||
|
||||
uint32_t SkSurface_Base::newGenerationID() {
|
||||
SkASSERT(!fCachedCanvas || fCachedCanvas->getSurfaceBase() == this);
|
||||
static int32_t gID;
|
||||
|
@ -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<SkCanvas> onMakeOverdrawCanvas();
|
||||
inline SkCanvas* getCachedCanvas();
|
||||
inline sk_sp<SkImage> refCachedImage(SkBudgeted, ForceUnique);
|
||||
|
||||
|
@ -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<SkCanvas> canvas = ((SkSurface_Base*) surface)->onMakeOverdrawCanvas();
|
||||
canvas->drawPaint(SkPaint());
|
||||
sk_sp<SkImage> 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<SkSurface> 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<SkSurface> surface = create_gpu_surface(context);
|
||||
test_overdraw_surface(r, surface.get());
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user