diff options
author | mtklein <mtklein@google.com> | 2015-05-18 14:53:43 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-05-18 14:53:43 -0700 |
commit | 72743b165424efc4ef6f6614add9033ea1ef31db (patch) | |
tree | 26ebea2ec4df12ec4d94cb3089b4f3d82a032322 /src/core/SkPicture.cpp | |
parent | 4f2dba625dd858ea3591974d793ef18c10c2ca67 (diff) |
Revert of Sketch splitting SkPicture into an interface and SkBigPicture. (patchset #25 id:480001 of https://codereview.chromium.org/1112523006/)
Reason for revert:
win_chromium_compile_dbg_ng
FAILED: ninja -t msvc -e environment.x86 -- E:\b\build\goma/gomacc "E:\b\depot_tools\win_toolchain\vs2013_files\VC\bin\amd64_x86\cl.exe" /nologo /showIncludes /FC @obj\third_party\skia\src\core\skia.SkBitmapHeap.obj.rsp /c ..\..\third_party\skia\src\core\SkBitmapHeap.cpp /Foobj\third_party\skia\src\core\skia.SkBitmapHeap.obj /Fdobj\skia\skia.cc.pdb
e:\b\build\slave\win\build\src\third_party\skia\include\core\skpicture.h(176) : error C2487: 'CURRENT_PICTURE_VERSION' : member of dll interface class may not be declared with dll interface
Original issue's description:
> Sketch splitting SkPicture into an interface and SkBigPicture.
>
> Adds small pictures for drawRect(), drawTextBlob(), and drawPath().
> These cover about 89% of draw calls from Blink SKPs,
> and about 25% of draw calls from our GMs.
>
> SkPicture handles:
> - serialization and deserialization
> - unique IDs
>
> Everything else is left to the subclasses:
> - playback(), cullRect()
> - hasBitmap(), hasText(), suitableForGPU(), etc.
> - LayerInfo / AccelData if applicable.
>
> The time to record a 1-op picture improves a good chunk
> (2 mallocs to 1), and the time to record a 0-op picture
> greatly improves (2 mallocs to none):
>
> picture_overhead_draw: 450ns -> 350ns
> picture_overhead_nodraw: 300ns -> 90ns
>
> BUG=skia:
>
> Committed: https://skia.googlesource.com/skia/+/c92c129ff85b05a714bd1bf921c02d5e14651f8b
>
> Latest blink_linux_rel:
>
> http://build.chromium.org/p/tryserver.blink/builders/linux_blink_rel/builds/61248
>
> Committed: https://skia.googlesource.com/skia/+/15877b6eae33a9282458bdb904a6d00440eca0ec
TBR=reed@google.com,robertphillips@google.com,fmalita@chromium.org,mtklein@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=skia:
Review URL: https://codereview.chromium.org/1130283004
Diffstat (limited to 'src/core/SkPicture.cpp')
-rw-r--r-- | src/core/SkPicture.cpp | 426 |
1 files changed, 357 insertions, 69 deletions
diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp index 6c1f6cb759..0145cbe5d7 100644 --- a/src/core/SkPicture.cpp +++ b/src/core/SkPicture.cpp @@ -5,107 +5,330 @@ * found in the LICENSE file. */ -#include "SkAtomics.h" -#include "SkMessageBus.h" -#include "SkPicture.h" + +#include "SkPictureFlat.h" #include "SkPictureData.h" #include "SkPicturePlayback.h" #include "SkPictureRecord.h" #include "SkPictureRecorder.h" +#include "SkAtomics.h" +#include "SkBitmapDevice.h" +#include "SkCanvas.h" +#include "SkChunkAlloc.h" +#include "SkMessageBus.h" +#include "SkPaintPriv.h" +#include "SkPathEffect.h" +#include "SkPicture.h" +#include "SkRegion.h" +#include "SkShader.h" +#include "SkStream.h" +#include "SkTDArray.h" +#include "SkTLogic.h" +#include "SkTSearch.h" +#include "SkTime.h" + +#include "SkReader32.h" +#include "SkWriter32.h" +#include "SkRTree.h" + +#if SK_SUPPORT_GPU +#include "GrContext.h" +#endif + +#include "SkRecord.h" +#include "SkRecordDraw.h" +#include "SkRecordOpts.h" +#include "SkRecorder.h" + DECLARE_SKMESSAGEBUS_MESSAGE(SkPicture::DeletionMessage); -/* SkPicture impl. This handles generic responsibilities like unique IDs and serialization. */ +template <typename T> int SafeCount(const T* obj) { + return obj ? obj->count() : 0; +} -SkPicture::SkPicture() : fUniqueID(0) {} +/////////////////////////////////////////////////////////////////////////////// -SkPicture::~SkPicture() { - // TODO: move this to ~SkBigPicture() only? +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 is a DrawPicture, recurse. + // 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. + bool operator()(const SkRecords::DrawPicture& op) { return op.picture->willPlayBackBitmaps(); } + + 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; +} + +// SkRecord visitor to find recorded text. +struct TextHunter { + // All ops with text have that text as a char array member named "text". + SK_CREATE_MEMBER_DETECTOR(text); + bool operator()(const SkRecords::DrawPicture& op) { return op.picture->hasText(); } + template <typename T> SK_WHEN(HasMember_text<T>, bool) operator()(const T&) { return true; } + template <typename T> SK_WHEN(!HasMember_text<T>, bool) operator()(const T&) { return false; } +}; + +} // namespace + +/** SkRecords visitor to determine heuristically whether or not a SkPicture + will be performant when rasterized on the GPU. + */ +struct SkPicture::PathCounter { + SK_CREATE_MEMBER_DETECTOR(paint); + PathCounter() : fNumSlowPathsAndDashEffects(0) {} + + // Recurse into nested pictures. + void operator()(const SkRecords::DrawPicture& op) { + const SkPicture::Analysis& analysis = op.picture->analysis(); + fNumSlowPathsAndDashEffects += analysis.fNumSlowPathsAndDashEffects; + } + + void checkPaint(const SkPaint* paint) { + if (paint && paint->getPathEffect()) { + // Initially assume it's slow. + fNumSlowPathsAndDashEffects++; + } + } + + 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) { + fNumSlowPathsAndDashEffects--; + } + } + } + + void operator()(const SkRecords::DrawPath& op) { + this->checkPaint(&op.paint); + if (op.paint.isAntiAlias() && !op.path.isConvex()) { + SkPaint::Style paintStyle = op.paint.getStyle(); + const SkRect& pathBounds = op.path.getBounds(); + if (SkPaint::kStroke_Style == paintStyle && + 0 == op.paint.getStrokeWidth()) { + // AA hairline concave path is not slow. + } else if (SkPaint::kFill_Style == paintStyle && pathBounds.width() < 64.f && + pathBounds.height() < 64.f && !op.path.isVolatile()) { + // AADF eligible concave path is not slow. + } else { + fNumSlowPathsAndDashEffects++; + } + } + } + + 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 fNumSlowPathsAndDashEffects; +}; + +SkPicture::Analysis::Analysis(const SkRecord& record) { + fWillPlaybackBitmaps = WillPlaybackBitmaps(record); + + PathCounter counter; + for (unsigned i = 0; i < record.count(); i++) { + record.visit<void>(i, counter); + } + fNumSlowPathsAndDashEffects = SkTMin<int>(counter.fNumSlowPathsAndDashEffects, 255); + + fHasText = false; + TextHunter text; + for (unsigned i = 0; i < record.count(); i++) { + if (record.visit<bool>(i, text)) { + fHasText = true; + break; + } + } +} + +bool SkPicture::Analysis::suitableForGpuRasterization(const char** reason, + int sampleCount) const { + // TODO: the heuristic used here needs to be refined + static const int kNumSlowPathsTol = 6; + + bool ret = fNumSlowPathsAndDashEffects < kNumSlowPathsTol; + + if (!ret && reason) { + *reason = "Too many slow paths (either concave or dashed)."; + } + return ret; +} + +/////////////////////////////////////////////////////////////////////////////// + +int SkPicture::drawableCount() const { + return fDrawablePicts.get() ? fDrawablePicts->count() : 0; +} + +SkPicture const* const* SkPicture::drawablePicts() const { + return fDrawablePicts.get() ? fDrawablePicts->begin() : NULL; +} + +SkPicture::~SkPicture() { // If the ID is still zero, no one has read it, so no need to send this message. uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed); if (id != 0) { - SkPicture::DeletionMessage msg = { (int32_t)id }; + SkPicture::DeletionMessage msg; + msg.fUniqueID = id; SkMessageBus<SkPicture::DeletionMessage>::Post(msg); } } -uint32_t SkPicture::uniqueID() const { - static uint32_t gNextID = 1; - uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed); - while (id == 0) { - uint32_t next = sk_atomic_fetch_add(&gNextID, 1u); - if (sk_atomic_compare_exchange(&fUniqueID, &id, next, - sk_memory_order_relaxed, - sk_memory_order_relaxed)) { - id = next; - } else { - // sk_atomic_compare_exchange replaced id with the current value of fUniqueID. - } +const SkPicture::AccelData* SkPicture::EXPERIMENTAL_getAccelData( + SkPicture::AccelData::Key key) const { + if (fAccelData.get() && fAccelData->getKey() == key) { + return fAccelData.get(); } - return id; + return NULL; } -static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' }; +SkPicture::AccelData::Domain SkPicture::AccelData::GenerateDomain() { + static int32_t gNextID = 0; -SkPictInfo SkPicture::createHeader() const { - SkPictInfo info; - // Copy magic bytes at the beginning of the header - static_assert(sizeof(kMagic) == 8, ""); - static_assert(sizeof(kMagic) == sizeof(info.fMagic), ""); - memcpy(info.fMagic, kMagic, sizeof(kMagic)); + int32_t id = sk_atomic_inc(&gNextID); + if (id >= 1 << (8 * sizeof(Domain))) { + SK_CRASH(); + } - // Set picture info after magic bytes in the header - info.fVersion = CURRENT_PICTURE_VERSION; - info.fCullRect = this->cullRect(); - info.fFlags = SkPictInfo::kCrossProcess_Flag; - // TODO: remove this flag, since we're always float (now) - info.fFlags |= SkPictInfo::kScalarIsFloat_Flag; + return static_cast<Domain>(id); +} - if (8 == sizeof(void*)) { - info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag; - } - return info; +/////////////////////////////////////////////////////////////////////////////// + +void SkPicture::playback(SkCanvas* canvas, AbortCallback* callback) const { + SkASSERT(canvas); + + // If the query contains the whole picture, don't bother with the BBH. + SkRect clipBounds = { 0, 0, 0, 0 }; + (void)canvas->getClipBounds(&clipBounds); + const bool useBBH = !clipBounds.contains(this->cullRect()); + + SkRecordDraw(*fRecord, canvas, this->drawablePicts(), NULL, this->drawableCount(), + useBBH ? fBBH.get() : NULL, callback); } +/////////////////////////////////////////////////////////////////////////////// + +#include "SkStream.h" + +static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'c', 't' }; + bool SkPicture::IsValidPictInfo(const SkPictInfo& info) { if (0 != memcmp(info.fMagic, kMagic, sizeof(kMagic))) { return false; } - if (info.fVersion < MIN_PICTURE_VERSION || info.fVersion > CURRENT_PICTURE_VERSION) { + + if (info.fVersion < MIN_PICTURE_VERSION || + info.fVersion > CURRENT_PICTURE_VERSION) { return false; } + return true; } bool SkPicture::InternalOnly_StreamIsSKP(SkStream* stream, SkPictInfo* pInfo) { - if (!stream) { + if (NULL == stream) { return false; } + // Check magic bytes. SkPictInfo info; SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); + if (!stream->read(&info.fMagic, sizeof(kMagic))) { return false; } - info.fVersion = stream->readU32(); - info.fCullRect.fLeft = stream->readScalar(); - info.fCullRect.fTop = stream->readScalar(); - info.fCullRect.fRight = stream->readScalar(); + info.fVersion = stream->readU32(); + info.fCullRect.fLeft = stream->readScalar(); + info.fCullRect.fTop = stream->readScalar(); + info.fCullRect.fRight = stream->readScalar(); info.fCullRect.fBottom = stream->readScalar(); - info.fFlags = stream->readU32(); - if (IsValidPictInfo(info)) { - if (pInfo) { *pInfo = info; } - return true; + info.fFlags = stream->readU32(); + + if (!IsValidPictInfo(info)) { + return false; } - return false; + + if (pInfo != NULL) { + *pInfo = info; + } + return true; } bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo) { + // Check magic bytes. SkPictInfo info; SkASSERT(sizeof(kMagic) == sizeof(info.fMagic)); + if (!buffer->readByteArray(&info.fMagic, sizeof(kMagic))) { return false; } @@ -114,29 +337,32 @@ bool SkPicture::InternalOnly_BufferIsSKP(SkReadBuffer* buffer, SkPictInfo* pInfo buffer->readRect(&info.fCullRect); info.fFlags = buffer->readUInt(); - if (IsValidPictInfo(info)) { - if (pInfo) { *pInfo = info; } - return true; + if (!IsValidPictInfo(info)) { + return false; } - return false; + + if (pInfo != NULL) { + *pInfo = info; + } + return true; } SkPicture* SkPicture::Forwardport(const SkPictInfo& info, const SkPictureData* data) { if (!data) { - return nullptr; + return NULL; } SkPicturePlayback playback(data); SkPictureRecorder r; playback.draw(r.beginRecording(SkScalarCeilToInt(info.fCullRect.width()), SkScalarCeilToInt(info.fCullRect.height())), - nullptr/*no callback*/); + NULL/*no callback*/); return r.endRecording(); } SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc proc) { SkPictInfo info; if (!InternalOnly_StreamIsSKP(stream, &info) || !stream->readBool()) { - return nullptr; + return NULL; } SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromStream(stream, info, proc)); return Forwardport(info, data); @@ -145,24 +371,45 @@ SkPicture* SkPicture::CreateFromStream(SkStream* stream, InstallPixelRefProc pro SkPicture* SkPicture::CreateFromBuffer(SkReadBuffer& buffer) { SkPictInfo info; if (!InternalOnly_BufferIsSKP(&buffer, &info) || !buffer.readBool()) { - return nullptr; + return NULL; } SkAutoTDelete<SkPictureData> data(SkPictureData::CreateFromBuffer(buffer, info)); return Forwardport(info, data); } -SkPictureData* SkPicture::backport() const { - SkPictInfo info = this->createHeader(); +void SkPicture::createHeader(SkPictInfo* info) const { + // Copy magic bytes at the beginning of the header + SkASSERT(sizeof(kMagic) == 8); + SkASSERT(sizeof(kMagic) == sizeof(info->fMagic)); + memcpy(info->fMagic, kMagic, sizeof(kMagic)); + + // Set picture info after magic bytes in the header + info->fVersion = CURRENT_PICTURE_VERSION; + info->fCullRect = this->cullRect(); + info->fFlags = SkPictInfo::kCrossProcess_Flag; + // TODO: remove this flag, since we're always float (now) + info->fFlags |= SkPictInfo::kScalarIsFloat_Flag; + + if (8 == sizeof(void*)) { + info->fFlags |= SkPictInfo::kPtrIs64Bit_Flag; + } +} + +// This for compatibility with serialization code only. This is not cheap. +SkPictureData* SkPicture::Backport(const SkRecord& src, const SkPictInfo& info, + SkPicture const* const drawablePicts[], int drawableCount) { SkPictureRecord rec(SkISize::Make(info.fCullRect.width(), info.fCullRect.height()), 0/*flags*/); rec.beginRecording(); - this->playback(&rec); + SkRecordDraw(src, &rec, drawablePicts, NULL, drawableCount, NULL/*bbh*/, NULL/*callback*/); rec.endRecording(); return SkNEW_ARGS(SkPictureData, (rec, info, false/*deep copy ops?*/)); } void SkPicture::serialize(SkWStream* stream, SkPixelSerializer* pixelSerializer) const { - SkPictInfo info = this->createHeader(); - SkAutoTDelete<SkPictureData> data(this->backport()); + SkPictInfo info; + this->createHeader(&info); + SkAutoTDelete<SkPictureData> data(Backport(*fRecord, info, this->drawablePicts(), + this->drawableCount())); stream->write(&info, sizeof(info)); if (data) { @@ -174,8 +421,10 @@ void SkPicture::serialize(SkWStream* stream, SkPixelSerializer* pixelSerializer) } void SkPicture::flatten(SkWriteBuffer& buffer) const { - SkPictInfo info = this->createHeader(); - SkAutoTDelete<SkPictureData> data(this->backport()); + SkPictInfo info; + this->createHeader(&info); + SkAutoTDelete<SkPictureData> data(Backport(*fRecord, info, this->drawablePicts(), + this->drawableCount())); buffer.writeByteArray(&info.fMagic, sizeof(info.fMagic)); buffer.writeUInt(info.fVersion); @@ -189,10 +438,49 @@ void SkPicture::flatten(SkWriteBuffer& buffer) const { } } -bool SkPicture::suitableForGpuRasterization(GrContext*, const char** whyNot) const { - if (this->numSlowPaths() > 5) { - if (whyNot) { *whyNot = "Too many slow paths (either concave or dashed)."; } - return false; +const SkPicture::Analysis& SkPicture::analysis() const { + auto create = [&](){ return SkNEW_ARGS(Analysis, (*fRecord)); }; + return *fAnalysis.get(create); +} + +#if SK_SUPPORT_GPU +bool SkPicture::suitableForGpuRasterization(GrContext*, const char **reason) const { + return this->analysis().suitableForGpuRasterization(reason, 0); +} +#endif + +bool SkPicture::hasText() const { return this->analysis().fHasText; } +bool SkPicture::willPlayBackBitmaps() const { return this->analysis().fWillPlaybackBitmaps; } +int SkPicture::approximateOpCount() const { return fRecord->count(); } + +SkPicture::SkPicture(const SkRect& cullRect, + SkRecord* record, + SnapshotArray* drawablePicts, + SkBBoxHierarchy* bbh, + AccelData* accelData, + size_t approxBytesUsedBySubPictures) + : fUniqueID(0) + , fCullRect(cullRect) + , fRecord(record) // Take ownership of caller's ref. + , fDrawablePicts(drawablePicts) // Take ownership. + , fBBH(bbh) // Take ownership of caller's ref. + , fAccelData(accelData) // Take ownership of caller's ref. + , fApproxBytesUsedBySubPictures(approxBytesUsedBySubPictures) +{} + + +static uint32_t gNextID = 1; +uint32_t SkPicture::uniqueID() const { + uint32_t id = sk_atomic_load(&fUniqueID, sk_memory_order_relaxed); + while (id == 0) { + uint32_t next = sk_atomic_fetch_add(&gNextID, 1u); + if (sk_atomic_compare_exchange(&fUniqueID, &id, next, + sk_memory_order_relaxed, + sk_memory_order_relaxed)) { + id = next; + } else { + // sk_atomic_compare_exchange replaced id with the current value of fUniqueID. + } } - return true; + return id; } |