Move the code over using the same template type approach previously used for willPlayBackBitmaps in http://skbug.com/2702.

Also unifies that flag and this one into a struct so they and others can be computed together. The struct is stored const to enforce lifetime expectations. Adds a few new cases to the unit test.

BUG=skia:2700
R=mtklein@google.com, reed@google.com, robertphillips@google.com, tomhudson@google.com

Committed: https://skia.googlesource.com/skia/+/60c2a79cfa8ceebcbafc243407564dc71f5e3b4f

Author: tomhudson@chromium.org

Review URL: https://codereview.chromium.org/364823009
This commit is contained in:
tomhudson 2014-08-20 05:29:41 -07:00 committed by Commit bot
parent 5dfe779c2e
commit 3a0f279167
7 changed files with 277 additions and 124 deletions

View File

@ -158,8 +158,6 @@
'<(skia_src_path)/core/SkReadBuffer.h',
'<(skia_src_path)/core/SkReadBuffer.cpp',
'<(skia_src_path)/core/SkReader32.h',
'<(skia_src_path)/core/SkRecordAnalysis.cpp',
'<(skia_src_path)/core/SkRecordAnalysis.h',
'<(skia_src_path)/core/SkRecordDraw.cpp',
'<(skia_src_path)/core/SkRecordOpts.cpp',
'<(skia_src_path)/core/SkRecorder.cpp',

View File

@ -305,7 +305,20 @@ private:
SkAutoTDelete<SkRecord> fRecord;
SkAutoTUnref<SkBBoxHierarchy> fBBH;
bool fRecordWillPlayBackBitmaps; // TODO: const
struct Analysis {
// To get setup to work cleanly, we cast away constness and call init()
// instead of trying to set everything during construction.
void init(const SkRecord&);
bool suitableForGpuRasterization(const char** reason, int sampleCount) const;
bool fWillPlaybackBitmaps;
int fNumPaintWithPathEffectUses;
int fNumFastPathDashEffects;
int fNumAAConcavePaths;
int fNumAAHairlineConcavePaths;
} const fAnalysis;
};
#endif

View File

@ -19,11 +19,13 @@
#include "SkChunkAlloc.h"
#include "SkDrawPictureCallback.h"
#include "SkPaintPriv.h"
#include "SkPathEffect.h"
#include "SkPicture.h"
#include "SkRecordAnalysis.h"
#include "SkRegion.h"
#include "SkShader.h"
#include "SkStream.h"
#include "SkTDArray.h"
#include "SkTLogic.h"
#include "SkTSearch.h"
#include "SkTime.h"
@ -46,12 +48,184 @@ template <typename T> int SafeCount(const T* obj) {
///////////////////////////////////////////////////////////////////////////////
namespace {
// Some commands have a paint, some have an optional paint. Either way, get back a pointer.
static const SkPaint* AsPtr(const SkPaint& p) { return &p; }
static const SkPaint* AsPtr(const SkRecords::Optional<SkPaint>& p) { return p; }
/** SkRecords visitor to determine whether an instance may require an
"external" bitmap to rasterize. May return false positives.
Does not return true for bitmap text.
Expected use is to determine whether images need to be decoded before
rasterizing a particular SkRecord.
*/
struct BitmapTester {
// Helpers. These create HasMember_bitmap and HasMember_paint.
SK_CREATE_MEMBER_DETECTOR(bitmap);
SK_CREATE_MEMBER_DETECTOR(paint);
// Main entry for visitor:
// If the command has a bitmap directly, return true.
// If the command has a paint and the paint has a bitmap, return true.
// Otherwise, return false.
template <typename T>
bool operator()(const T& r) { return CheckBitmap(r); }
// If the command has a bitmap, of course we're going to play back bitmaps.
template <typename T>
static SK_WHEN(HasMember_bitmap<T>, bool) CheckBitmap(const T&) { return true; }
// If not, look for one in its paint (if it has a paint).
template <typename T>
static SK_WHEN(!HasMember_bitmap<T>, bool) CheckBitmap(const T& r) { return CheckPaint(r); }
// If we have a paint, dig down into the effects looking for a bitmap.
template <typename T>
static SK_WHEN(HasMember_paint<T>, bool) CheckPaint(const T& r) {
const SkPaint* paint = AsPtr(r.paint);
if (paint) {
const SkShader* shader = paint->getShader();
if (shader &&
shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
return true;
}
}
return false;
}
// If we don't have a paint, that non-paint has no bitmap.
template <typename T>
static SK_WHEN(!HasMember_paint<T>, bool) CheckPaint(const T&) { return false; }
};
bool WillPlaybackBitmaps(const SkRecord& record) {
BitmapTester tester;
for (unsigned i = 0; i < record.count(); i++) {
if (record.visit<bool>(i, tester)) {
return true;
}
}
return false;
}
/** SkRecords visitor to determine heuristically whether or not a SkPicture
will be performant when rasterized on the GPU.
*/
struct PathCounter {
SK_CREATE_MEMBER_DETECTOR(paint);
PathCounter()
: numPaintWithPathEffectUses (0)
, numFastPathDashEffects (0)
, numAAConcavePaths (0)
, numAAHairlineConcavePaths (0) {
}
void checkPaint(const SkPaint* paint) {
if (paint && paint->getPathEffect()) {
numPaintWithPathEffectUses++;
}
}
void operator()(const SkRecords::DrawPoints& op) {
this->checkPaint(&op.paint);
const SkPathEffect* effect = op.paint.getPathEffect();
if (effect) {
SkPathEffect::DashInfo info;
SkPathEffect::DashType dashType = effect->asADash(&info);
if (2 == op.count && SkPaint::kRound_Cap != op.paint.getStrokeCap() &&
SkPathEffect::kDash_DashType == dashType && 2 == info.fCount) {
numFastPathDashEffects++;
}
}
}
void operator()(const SkRecords::DrawPath& op) {
this->checkPaint(&op.paint);
if (op.paint.isAntiAlias() && !op.path.isConvex()) {
numAAConcavePaths++;
if (SkPaint::kStroke_Style == op.paint.getStyle() &&
0 == op.paint.getStrokeWidth()) {
numAAHairlineConcavePaths++;
}
}
}
template <typename T>
SK_WHEN(HasMember_paint<T>, void) operator()(const T& op) {
this->checkPaint(AsPtr(op.paint));
}
template <typename T>
SK_WHEN(!HasMember_paint<T>, void) operator()(const T& op) { /* do nothing */ }
int numPaintWithPathEffectUses;
int numFastPathDashEffects;
int numAAConcavePaths;
int numAAHairlineConcavePaths;
};
} // namespace
void SkPicture::Analysis::init(const SkRecord& record) {
fWillPlaybackBitmaps = WillPlaybackBitmaps(record);
PathCounter counter;
for (unsigned i = 0; i < record.count(); i++) {
record.visit<void>(i, counter);
}
fNumPaintWithPathEffectUses = counter.numPaintWithPathEffectUses;
fNumFastPathDashEffects = counter.numFastPathDashEffects;
fNumAAConcavePaths = counter.numAAConcavePaths;
fNumAAHairlineConcavePaths = counter.numAAHairlineConcavePaths;
}
bool SkPicture::Analysis::suitableForGpuRasterization(const char** reason,
int sampleCount) const {
// TODO: the heuristic used here needs to be refined
static const int kNumPaintWithPathEffectsUsesTol = 1;
static const int kNumAAConcavePathsTol = 5;
int numNonDashedPathEffects = fNumPaintWithPathEffectUses -
fNumFastPathDashEffects;
bool suitableForDash = (0 == fNumPaintWithPathEffectUses) ||
(numNonDashedPathEffects < kNumPaintWithPathEffectsUsesTol
&& 0 == sampleCount);
bool ret = suitableForDash &&
(fNumAAConcavePaths - fNumAAHairlineConcavePaths)
< kNumAAConcavePathsTol;
if (!ret && NULL != reason) {
if (!suitableForDash) {
if (0 != sampleCount) {
*reason = "Can't use multisample on dash effect.";
} else {
*reason = "Too many non dashed path effects.";
}
} else if ((fNumAAConcavePaths - fNumAAHairlineConcavePaths)
>= kNumAAConcavePathsTol)
*reason = "Too many anti-aliased concave paths.";
else
*reason = "Unknown reason for GPU unsuitability.";
}
return ret;
}
///////////////////////////////////////////////////////////////////////////////
#ifdef SK_SUPPORT_LEGACY_DEFAULT_PICTURE_CTOR
// fRecord OK
SkPicture::SkPicture()
: fWidth(0)
, fHeight(0)
, fRecordWillPlayBackBitmaps(false) {
, fHeight(0) {
this->needsNewGenID();
}
#endif
@ -62,7 +236,7 @@ SkPicture::SkPicture(int width, int height,
bool deepCopyOps)
: fWidth(width)
, fHeight(height)
, fRecordWillPlayBackBitmaps(false) {
, fAnalysis() {
this->needsNewGenID();
SkPictInfo info;
@ -137,7 +311,6 @@ SkPicture* SkPicture::clone() const {
}
SkPicture* clone = SkNEW_ARGS(SkPicture, (newData.detach(), fWidth, fHeight));
clone->fRecordWillPlayBackBitmaps = fRecordWillPlayBackBitmaps;
clone->fUniqueID = this->uniqueID(); // need to call method to ensure != 0
return clone;
@ -271,7 +444,7 @@ SkPicture::SkPicture(SkPictureData* data, int width, int height)
: fData(data)
, fWidth(width)
, fHeight(height)
, fRecordWillPlayBackBitmaps(false) {
, fAnalysis() {
this->needsNewGenID();
}
@ -386,8 +559,11 @@ void SkPicture::flatten(SkWriteBuffer& buffer) const {
}
#if SK_SUPPORT_GPU
// fRecord TODO
// fRecord OK
bool SkPicture::suitableForGpuRasterization(GrContext* context, const char **reason) const {
if (fRecord.get()) {
return fAnalysis.suitableForGpuRasterization(reason, 0);
}
if (NULL == fData.get()) {
if (NULL != reason) {
*reason = "Missing internal data.";
@ -407,7 +583,7 @@ bool SkPicture::hasText() const {
// fRecord OK
bool SkPicture::willPlayBackBitmaps() const {
if (fRecord.get()) {
return fRecordWillPlayBackBitmaps;
return fAnalysis.fWillPlaybackBitmaps;
}
if (!fData.get()) {
return false;
@ -441,8 +617,10 @@ SkPicture::SkPicture(int width, int height, SkRecord* record, SkBBoxHierarchy* b
, fHeight(height)
, fRecord(record)
, fBBH(SkSafeRef(bbh))
, fRecordWillPlayBackBitmaps(SkRecordWillPlaybackBitmaps(*record)) {
, fAnalysis() {
// TODO: delay as much of this work until just before first playback?
const_cast<Analysis*>(&fAnalysis)->init(*record);
if (fBBH.get()) {
SkRecordFillBounds(*record, fBBH.get());
}

View File

@ -1,66 +0,0 @@
#include "SkRecordAnalysis.h"
#include "SkShader.h"
#include "SkTLogic.h"
/** SkRecords visitor to determine whether an instance may require an
"external" bitmap to rasterize. May return false positives.
Does not return true for bitmap text.
Expected use is to determine whether images need to be decoded before
rasterizing a particular SkRecord.
*/
struct BitmapTester {
// Helpers. These create HasMember_bitmap and HasMember_paint.
SK_CREATE_MEMBER_DETECTOR(bitmap);
SK_CREATE_MEMBER_DETECTOR(paint);
// Some commands have a paint, some have an optional paint. Either way, get back a pointer.
static const SkPaint* AsPtr(const SkPaint& p) { return &p; }
static const SkPaint* AsPtr(const SkRecords::Optional<SkPaint>& p) { return p; }
// Main entry for visitor:
// If the command has a bitmap directly, return true.
// If the command has a paint and the paint has a bitmap, return true.
// Otherwise, return false.
template <typename T>
bool operator()(const T& r) { return CheckBitmap(r); }
// If the command has a bitmap, of course we're going to play back bitmaps.
template <typename T>
static SK_WHEN(HasMember_bitmap<T>, bool) CheckBitmap(const T&) { return true; }
// If not, look for one in its paint (if it has a paint).
template <typename T>
static SK_WHEN(!HasMember_bitmap<T>, bool) CheckBitmap(const T& r) { return CheckPaint(r); }
// If we have a paint, dig down into the effects looking for a bitmap.
template <typename T>
static SK_WHEN(HasMember_paint<T>, bool) CheckPaint(const T& r) {
const SkPaint* paint = AsPtr(r.paint);
if (paint) {
const SkShader* shader = paint->getShader();
if (shader &&
shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType) {
return true;
}
}
return false;
}
// If we don't have a paint, that non-paint has no bitmap.
template <typename T>
static SK_WHEN(!HasMember_paint<T>, bool) CheckPaint(const T&) { return false; }
};
bool SkRecordWillPlaybackBitmaps(const SkRecord& record) {
BitmapTester tester;
for (unsigned i = 0; i < record.count(); i++) {
if (record.visit<bool>(i, tester)) {
return true;
}
}
return false;
}

View File

@ -1,8 +0,0 @@
#ifndef SkRecordAnalysis_DEFINED
#define SkRecordAnalysis_DEFINED
#include "SkRecord.h"
bool SkRecordWillPlaybackBitmaps(const SkRecord& record);
#endif // SkRecordAnalysis_DEFINED

View File

@ -578,6 +578,43 @@ static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
}
}
#define GENERATE_CANVAS(recorder, x) \
(x) ? recorder.EXPERIMENTAL_beginRecording(100, 100) \
: recorder.beginRecording(100,100);
/* Hit a few SkPicture::Analysis cases not handled elsewhere. */
static void test_analysis(skiatest::Reporter* reporter, bool useNewPath) {
SkPictureRecorder recorder;
SkCanvas* canvas = GENERATE_CANVAS(recorder, useNewPath);
{
canvas->drawRect(SkRect::MakeWH(10, 10), SkPaint ());
}
SkAutoTUnref<SkPicture> picture(recorder.endRecording());
REPORTER_ASSERT(reporter, !picture->willPlayBackBitmaps());
canvas = GENERATE_CANVAS(recorder, useNewPath);
{
SkPaint paint;
// CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
// gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
SkBitmap bitmap;
bitmap.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
bitmap.eraseColor(SK_ColorBLUE);
*(bitmap.getAddr32(0, 0)) = SK_ColorGREEN;
SkShader* shader = SkShader::CreateBitmapShader(bitmap, SkShader::kClamp_TileMode,
SkShader::kClamp_TileMode);
paint.setShader(shader)->unref();
REPORTER_ASSERT(reporter,
shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType);
canvas->drawRect(SkRect::MakeWH(10, 10), paint);
}
picture.reset(recorder.endRecording());
REPORTER_ASSERT(reporter, picture->willPlayBackBitmaps());
}
static void test_gatherpixelrefsandrects(skiatest::Reporter* reporter) {
const int IW = 32;
const int IH = IW;
@ -706,11 +743,13 @@ static void rand_op(SkCanvas* canvas, SkRandom& rand) {
}
#if SK_SUPPORT_GPU
static void test_gpu_veto(skiatest::Reporter* reporter) {
static void test_gpu_veto(skiatest::Reporter* reporter,
bool useNewPath) {
SkPictureRecorder recorder;
SkCanvas* canvas = recorder.beginRecording(100, 100);
SkCanvas* canvas = GENERATE_CANVAS(recorder, useNewPath);
{
SkPath path;
path.moveTo(0, 0);
@ -732,7 +771,7 @@ static void test_gpu_veto(skiatest::Reporter* reporter) {
REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL, &reason));
REPORTER_ASSERT(reporter, NULL != reason);
canvas = recorder.beginRecording(100, 100);
canvas = GENERATE_CANVAS(recorder, useNewPath);
{
SkPath path;
@ -754,7 +793,7 @@ static void test_gpu_veto(skiatest::Reporter* reporter) {
// A lot of AA concave paths currently render an SkPicture undesireable for GPU rendering
REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
canvas = recorder.beginRecording(100, 100);
canvas = GENERATE_CANVAS(recorder, useNewPath);
{
SkPath path;
@ -777,8 +816,37 @@ static void test_gpu_veto(skiatest::Reporter* reporter) {
picture.reset(recorder.endRecording());
// hairline stroked AA concave paths are fine for GPU rendering
REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
canvas = GENERATE_CANVAS(recorder, useNewPath);
{
SkPaint paint;
SkScalar intervals [] = { 10, 20 };
SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
paint.setPathEffect(pe)->unref();
SkPoint points [2] = { { 0, 0 }, { 100, 0 } };
canvas->drawPoints(SkCanvas::kLines_PointMode, 2, points, paint);
}
picture.reset(recorder.endRecording());
// fast-path dashed effects are fine for GPU rendering ...
REPORTER_ASSERT(reporter, picture->suitableForGpuRasterization(NULL));
canvas = GENERATE_CANVAS(recorder, useNewPath);
{
SkPaint paint;
SkScalar intervals [] = { 10, 20 };
SkPathEffect* pe = SkDashPathEffect::Create(intervals, 2, 25);
paint.setPathEffect(pe)->unref();
canvas->drawRect(SkRect::MakeWH(10, 10), paint);
}
picture.reset(recorder.endRecording());
// ... but only when applied to drawPoint() calls
REPORTER_ASSERT(reporter, !picture->suitableForGpuRasterization(NULL));
}
#undef GENERATE_CANVAS
static void test_gpu_picture_optimization(skiatest::Reporter* reporter,
GrContextFactory* factory) {
for (int i= 0; i < GrContextFactory::kGLContextTypeCnt; ++i) {
@ -1617,9 +1685,12 @@ DEF_TEST(Picture, reporter) {
test_unbalanced_save_restores(reporter);
test_peephole();
#if SK_SUPPORT_GPU
test_gpu_veto(reporter);
test_gpu_veto(reporter, false);
test_gpu_veto(reporter, true);
#endif
test_has_text(reporter);
test_analysis(reporter, false);
test_analysis(reporter, true);
test_gatherpixelrefs(reporter);
test_gatherpixelrefsandrects(reporter);
test_bitmap_with_encoded_data(reporter);

View File

@ -11,7 +11,6 @@
#include "SkImageInfo.h"
#include "SkShader.h"
#include "SkRecord.h"
#include "SkRecordAnalysis.h"
#include "SkRecords.h"
// Sums the area of any DrawRect command it sees.
@ -77,37 +76,5 @@ DEF_TEST(Record, r) {
REPORTER_ASSERT(r, summer.area() == 500);
}
DEF_TEST(RecordAnalysis, r) {
SkRecord record;
SkRect rect = SkRect::MakeWH(10, 10);
SkPaint paint;
APPEND(record, SkRecords::DrawRect, paint, rect);
REPORTER_ASSERT(r, !SkRecordWillPlaybackBitmaps(record));
SkBitmap bitmap;
APPEND(record, SkRecords::DrawBitmap, &paint, bitmap, 0.0f, 0.0f);
REPORTER_ASSERT(r, SkRecordWillPlaybackBitmaps(record));
SkNEW_PLACEMENT_ARGS(record.replace<SkRecords::DrawRect>(1),
SkRecords::DrawRect, (paint, rect));
REPORTER_ASSERT(r, !SkRecordWillPlaybackBitmaps(record));
SkPaint paint2;
// CreateBitmapShader is too smart for us; an empty (or 1x1) bitmap shader
// gets optimized into a non-bitmap form, so we create a 2x2 bitmap here.
SkBitmap bitmap2;
bitmap2.allocPixels(SkImageInfo::MakeN32Premul(2, 2));
bitmap2.eraseColor(SK_ColorBLUE);
*(bitmap2.getAddr32(0, 0)) = SK_ColorGREEN;
SkShader* shader = SkShader::CreateBitmapShader(bitmap2, SkShader::kClamp_TileMode,
SkShader::kClamp_TileMode);
paint2.setShader(shader)->unref();
REPORTER_ASSERT(r, shader->asABitmap(NULL, NULL, NULL) == SkShader::kDefault_BitmapType);
APPEND(record, SkRecords::DrawRect, paint2, rect);
REPORTER_ASSERT(r, SkRecordWillPlaybackBitmaps(record));
}
#undef APPEND