aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkPicture.cpp
diff options
context:
space:
mode:
authorGravatar mtklein <mtklein@google.com>2015-05-18 14:53:43 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-05-18 14:53:43 -0700
commit72743b165424efc4ef6f6614add9033ea1ef31db (patch)
tree26ebea2ec4df12ec4d94cb3089b4f3d82a032322 /src/core/SkPicture.cpp
parent4f2dba625dd858ea3591974d793ef18c10c2ca67 (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.cpp426
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;
}