From 48b649060c8d3ae104274703553d7c03a58f3ddd Mon Sep 17 00:00:00 2001 From: Mike Klein Date: Wed, 25 Jul 2018 13:28:44 -0400 Subject: remove SkThreadedBMPDevice and friends It is unused, is becoming a maintainence burden and source of bugs, and takes up a lot of time on the *SAN bots. Change-Id: If383eb6e4838ca23140f9e16d518b1bfc655fa12 Reviewed-on: https://skia-review.googlesource.com/143307 Auto-Submit: Mike Klein Commit-Queue: Herb Derby Reviewed-by: Herb Derby --- src/core/SkBitmapDevice.h | 1 - src/core/SkDraw.cpp | 50 +------- src/core/SkDraw.h | 7 +- src/core/SkTaskGroup2D.cpp | 86 -------------- src/core/SkTaskGroup2D.h | 123 ------------------- src/core/SkThreadedBMPDevice.cpp | 250 --------------------------------------- src/core/SkThreadedBMPDevice.h | 225 ----------------------------------- 7 files changed, 6 insertions(+), 736 deletions(-) delete mode 100644 src/core/SkTaskGroup2D.cpp delete mode 100644 src/core/SkTaskGroup2D.h delete mode 100644 src/core/SkThreadedBMPDevice.cpp delete mode 100644 src/core/SkThreadedBMPDevice.h (limited to 'src') diff --git a/src/core/SkBitmapDevice.h b/src/core/SkBitmapDevice.h index 379d42ddd1..8abc677f99 100644 --- a/src/core/SkBitmapDevice.h +++ b/src/core/SkBitmapDevice.h @@ -155,7 +155,6 @@ private: friend class SkDrawTiler; friend class SkDeviceFilteredPaint; friend class SkSurface_Raster; - friend class SkThreadedBMPDevice; // to copy fRCStack class BDDraw; diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp index 8a49abfe3a..bd607553cd 100644 --- a/src/core/SkDraw.cpp +++ b/src/core/SkDraw.cpp @@ -35,7 +35,6 @@ #include "SkTLazy.h" #include "SkTemplates.h" #include "SkTextMapStateProc.h" -#include "SkThreadedBMPDevice.h" #include "SkTo.h" #include "SkUtils.h" @@ -957,11 +956,8 @@ SkScalar SkDraw::ComputeResScaleForStroking(const SkMatrix& matrix) { } void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawCoverage, - SkBlitter* customBlitter, bool doFill, SkInitOnceData* iData) const { + SkBlitter* customBlitter, bool doFill) const { if (SkPathPriv::TooBigForMath(devPath)) { - if (iData) { - iData->setEmptyDrawFn(); - } return; } SkBlitter* blitter = nullptr; @@ -976,9 +972,6 @@ void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawC SkStrokeRec::InitStyle style = doFill ? SkStrokeRec::kFill_InitStyle : SkStrokeRec::kHairline_InitStyle; if (as_MFB(paint.getMaskFilter())->filterPath(devPath, *fMatrix, *fRC, blitter, style)) { - if (iData) { - iData->setEmptyDrawFn(); - } return; // filterPath() called the blitter, so we're done } } @@ -1024,48 +1017,16 @@ void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawC } } - if (iData == nullptr) { - proc(devPath, *fRC, blitter); // proceed directly if we're not in threaded init-once - } else if (!doFill || !paint.isAntiAlias()) { - // We're in threaded init-once but we can't use DAA. Hence we'll stop here and hand all the - // remaining work to draw phase. This is a simple example of how to add init-once to - // existing drawXXX commands: simply send in SkInitOnceData, do as much init work as - // possible, and finally wrap the remaining work into iData->fElement->fDrawFn. - SkASSERT(customBlitter == nullptr); - devPath.updateBoundsCache(); // make it thread safe - iData->fElement->setDrawFn([proc, devPath, paint, drawCoverage](SkArenaAlloc* alloc, - const SkThreadedBMPDevice::DrawState& ds, const SkIRect& tileBounds) { - SkThreadedBMPDevice::TileDraw tileDraw(ds, tileBounds); - SkAutoBlitterChoose blitterStorage(tileDraw, nullptr, paint, drawCoverage); - proc(devPath, *tileDraw.fRC, blitterStorage.get()); - }); - } else { - // We can use DAA to do scan conversion in the init-once phase. - SkDAARecord* record = iData->fAlloc->make(iData->fAlloc); - SkNullBlitter nullBlitter; // We don't want to blit anything during the init phase - SkScan::AntiFillPath(devPath, *fRC, &nullBlitter, record); - SkASSERT(customBlitter == nullptr); - iData->fElement->setDrawFn([record, devPath, paint, drawCoverage](SkArenaAlloc* alloc, - const SkThreadedBMPDevice::DrawState& ds, const SkIRect& tileBounds) { - SkASSERT(record->fType != SkDAARecord::Type::kToBeComputed); - SkThreadedBMPDevice::TileDraw tileDraw(ds, tileBounds); - SkAutoBlitterChoose blitterStorage(tileDraw, nullptr, paint, drawCoverage); - SkScan::AntiFillPath(devPath, *tileDraw.fRC, blitterStorage.get(), record); - }); - } + proc(devPath, *fRC, blitter); } void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint, const SkMatrix* prePathMatrix, bool pathIsMutable, - bool drawCoverage, SkBlitter* customBlitter, - SkInitOnceData* iData) const { + bool drawCoverage, SkBlitter* customBlitter) const { SkDEBUGCODE(this->validate();) // nothing to draw if (fRC->isEmpty()) { - if (iData) { - iData->setEmptyDrawFn(); - } return; } @@ -1075,9 +1036,6 @@ void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint, SkPath* tmpPath = &tmpPathStorage; SkMatrix tmpMatrix; const SkMatrix* matrix = fMatrix; - if (iData) { - tmpPath = iData->fAlloc->make(); - } tmpPath->setIsVolatile(true); SkPathPriv::SetIsBadForDAA(*tmpPath, SkPathPriv::IsBadForDAA(origSrcPath)); @@ -1142,7 +1100,7 @@ void SkDraw::drawPath(const SkPath& origSrcPath, const SkPaint& origPaint, // transform the path into device space pathPtr->transform(*matrix, devPathPtr); - this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill, iData); + this->drawDevPath(*devPathPtr, *paint, drawCoverage, customBlitter, doFill); } void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap, const SkPaint& paint) const { diff --git a/src/core/SkDraw.h b/src/core/SkDraw.h index deb7451b99..a64abe74ff 100644 --- a/src/core/SkDraw.h +++ b/src/core/SkDraw.h @@ -31,7 +31,6 @@ class SkRasterClip; struct SkDrawProcs; struct SkRect; class SkRRect; -struct SkInitOnceData; class SkDraw { public: @@ -137,11 +136,11 @@ private: void drawPath(const SkPath&, const SkPaint&, const SkMatrix* preMatrix, bool pathIsMutable, bool drawCoverage, - SkBlitter* customBlitter = nullptr, SkInitOnceData* iData = nullptr) const; + SkBlitter* customBlitter = nullptr) const; void drawLine(const SkPoint[2], const SkPaint&) const; void drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawCoverage, - SkBlitter* customBlitter, bool doFill, SkInitOnceData* iData = nullptr) const; + SkBlitter* customBlitter, bool doFill) const; /** * Return the current clip bounds, in local coordinates, with slop to account * for antialiasing or hairlines (i.e. device-bounds outset by 1, and then @@ -169,8 +168,6 @@ public: #else void validate() const {} #endif - - friend class SkThreadedBMPDevice; // to access private method drawPath }; #endif diff --git a/src/core/SkTaskGroup2D.cpp b/src/core/SkTaskGroup2D.cpp deleted file mode 100644 index 59eecf9f9a..0000000000 --- a/src/core/SkTaskGroup2D.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkTaskGroup2D.h" - -void SkTaskGroup2D::start() { - fThreadsGroup->batch(fThreadCnt, [this](int threadId){ - this->work(threadId); - }); -} - -void SkTaskGroup2D::addColumn() { - SkASSERT(!fIsFinishing); // we're not supposed to add more work after the calling of finish - fWidth++; -} - -void SkTaskGroup2D::finish() { - fIsFinishing = true; - fThreadsGroup->wait(); -} - -void SkSpinningTaskGroup2D::work(int threadId) { - int workCol = 0; - int initCol = 0; - - while (true) { - SkASSERT(workCol <= fWidth); - if (this->isFinishing() && workCol >= fWidth) { - return; - } - - // Note that row = threadId - if (workCol < fWidth && fKernel->work2D(threadId, workCol, threadId)) { - workCol++; - } else { - // Initialize something if we can't work - this->initAnUninitializedColumn(initCol, threadId); - } - } -} - -void SkFlexibleTaskGroup2D::work(int threadId) { - int row = threadId; - int initCol = 0; - int numRowsCompleted = 0; - std::vector completedRows(fHeight, false); - - // Only keep fHeight - numRowsCompleted number of threads looping. When rows are about to - // complete, this strategy keeps the contention low. - while (threadId < fHeight - numRowsCompleted) { - RowData& rowData = fRowData[row]; - - // The Android roller somehow gets a false-positive compile warning/error about the try-lock - // and unlock process. Hence we disable -Wthread-safety-analysis to bypass it. -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wthread-safety-analysis" -#endif - if (rowData.fMutex.try_lock()) { - while (rowData.fNextColumn < fWidth && - fKernel->work2D(row, rowData.fNextColumn, threadId)) { - rowData.fNextColumn++; - } - // isFinishing can never go from true to false. Once it's true, we count how many rows - // are completed (out of work). If that count reaches fHeight, then we're out of work - // for the whole group and we can stop. - if (rowData.fNextColumn == fWidth && this->isFinishing()) { - numRowsCompleted += (completedRows[row] == false); - completedRows[row] = true; // so we won't count this row twice - } - rowData.fMutex.unlock(); - } -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - - // By reaching here, we're either unable to acquire the row, or out of work, or blocked by - // initialization - row = (row + 1) % fHeight; // Move to the next row - this->initAnUninitializedColumn(initCol, threadId); // Initialize something - } -} diff --git a/src/core/SkTaskGroup2D.h b/src/core/SkTaskGroup2D.h deleted file mode 100644 index 20915cf9d7..0000000000 --- a/src/core/SkTaskGroup2D.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkTaskGroup2D_DEFINED -#define SkTaskGroup2D_DEFINED - -#include "SkTaskGroup.h" - -#include -#include - -// The interface for doing work on a 2D grid with possible initialization on columns. -class SkWorkKernel2D { -public: - // Return false iff the column needs initialization and such initialization is not finished yet. - virtual bool work2D(int row, int column, int thread) = 0; - - // Return false if no initialization is done for this colum (e.g., it's already initialized; or - // maybe some other thread is initializing the column). - virtual bool initColumn(int column, int thread) = 0; - - virtual ~SkWorkKernel2D() {} -}; - -// A 2D grid (height rows x width columns) of tasks to be executed on a given executor with -// threadCnt number of threads. -// -// The height (number of rows) is fixed. The width (number of columns) may be dynamically expanded. -// -// The task on row i and column j is abstracted as work2D(i, j, t). Parameter t is the thread id and -// it shouldn't affect the work to be done. It's only used to allow some variables that are not -// thread safe and should be used exclusively by one thread (e.g., thread allocators). We guarantee -// that the task on the same row will be executed in order (i.e., work2D(1, 1, t) is guaranteed to -// finish before calling work2D(1, 2, t)). Tasks in different rows can happen in any order. -// -// There are also width number of init calls, one per column. work2D(i, j, t) may return false if -// column j requires initialization but it's not initialized yet. In that case, a thread t needs to -// call initColumn(j, t) once to unblock all rows that depend on the initialization of column j. -// (Again, t shouldn't affect the init work to be done; it's just for some non-thread-safe -// variables). The init calls have no order requirement so we can call them in any order. -// -// Multiple therads may try to init the same column j at the same time. InitFn is expected to handle -// this gracefully (e.g., let only one thread do the init and return immediately for other threads). -class SkTaskGroup2D { -public: - SkTaskGroup2D(SkWorkKernel2D* kernel, int height, SkExecutor* executor, int threadCnt) - : fKernel(kernel), fHeight(height), fThreadCnt(threadCnt), fIsFinishing(false) - , fWidth(0), fThreadsGroup(new SkTaskGroup(*executor)) {} - - virtual ~SkTaskGroup2D() {} - - virtual void addColumn(); // Add a new column of tasks. - - void start(); // start threads to execute tasks - void finish(); // wait and finish all tasks (no more tasks can be added after calling this) - - SK_ALWAYS_INLINE bool isFinishing() const { - return fIsFinishing; - } - -protected: - static constexpr int MAX_CACHE_LINE = 64; - - // Finish all tasks on the threadId and then return. - virtual void work(int threadId) = 0; - - // Initialize a column that needs to be initialized. The parameter initCol is not thread safe - // and should only be exclusively accessed by the working thread which will modify it to the - // column that may need to be initialized next. - void initAnUninitializedColumn(int& initCol, int threadId) { - bool didSomeInit = false; - while (initCol < fWidth && !didSomeInit) { - didSomeInit = fKernel->initColumn(initCol++, threadId); - } - } - - SkWorkKernel2D* fKernel; - const int fHeight; - const int fThreadCnt; - - std::atomic fIsFinishing; - std::atomic fWidth; - - std::unique_ptr fThreadsGroup; -}; - -// A simple spinning task group that assumes height equals threadCnt. -class SkSpinningTaskGroup2D final : public SkTaskGroup2D { -public: - SkSpinningTaskGroup2D(SkWorkKernel2D* kernel, int h, SkExecutor* x, int t) - : SkTaskGroup2D(kernel, h, x, t) { - SkASSERT(h == t); // height must be equal to threadCnt - } - -protected: - void work(int threadId) override; -}; - -class SkFlexibleTaskGroup2D final : public SkTaskGroup2D { -public: - SkFlexibleTaskGroup2D(SkWorkKernel2D* kernel, int h, SkExecutor* x, int t) - : SkTaskGroup2D(kernel, h, x, t), fRowData(h) {} - -protected: - void work(int threadId) override; - -private: - // alignas(MAX_CACHE_LINE) to avoid false sharing by cache lines - struct alignas(MAX_CACHE_LINE) RowData { - RowData() : fNextColumn(0) {} - - int fNextColumn; // next column index to work - std::mutex fMutex; // the mutex for the thread to acquire - }; - - std::vector fRowData; -}; - -#endif//SkTaskGroup2D_DEFINED diff --git a/src/core/SkThreadedBMPDevice.cpp b/src/core/SkThreadedBMPDevice.cpp deleted file mode 100644 index 1dea5e6d2d..0000000000 --- a/src/core/SkThreadedBMPDevice.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkThreadedBMPDevice.h" - -#include "SkPath.h" -#include "SkSpecialImage.h" -#include "SkTaskGroup.h" -#include "SkVertices.h" - -// Calling init(j, k) would initialize the j-th element on k-th thread. It returns false if it's -// already initiailized. -bool SkThreadedBMPDevice::DrawQueue::initColumn(int column, int thread) { - return fElements[column].tryInitOnce(&fThreadAllocs[thread]); -} - -// Calling work(i, j, k) would draw j-th element the i-th tile on k-th thead. If the element still -// needs to be initialized, drawFn will return false without drawing. -bool SkThreadedBMPDevice::DrawQueue::work2D(int row, int column, int thread) { - return fElements[column].tryDraw(fDevice->fTileBounds[row], &fThreadAllocs[thread]); -} - -void SkThreadedBMPDevice::DrawQueue::reset() { - if (fTasks) { - fTasks->finish(); - } - - fThreadAllocs.reset(fDevice->fThreadCnt); - fSize = 0; - - // using TaskGroup2D = SkSpinningTaskGroup2D; - using TaskGroup2D = SkFlexibleTaskGroup2D; - - fTasks.reset(new TaskGroup2D(this, fDevice->fTileCnt, fDevice->fExecutor, - fDevice->fThreadCnt)); - fTasks->start(); -} - -SkThreadedBMPDevice::SkThreadedBMPDevice(const SkBitmap& bitmap, - int tiles, - int threads, - SkExecutor* executor) - : INHERITED(bitmap) - , fTileCnt(tiles) - , fThreadCnt(threads <= 0 ? tiles : threads) - , fQueue(this) -{ - if (executor == nullptr) { - fInternalExecutor = SkExecutor::MakeFIFOThreadPool(fThreadCnt); - executor = fInternalExecutor.get(); - } - fExecutor = executor; - - // Tiling using stripes for now; we'll explore better tiling in the future. - int h = (bitmap.height() + fTileCnt - 1) / SkTMax(fTileCnt, 1); - int w = bitmap.width(); - int top = 0; - for(int tid = 0; tid < fTileCnt; ++tid, top += h) { - fTileBounds.push_back(SkIRect::MakeLTRB(0, top, w, top + h)); - } - fQueue.reset(); -} - -void SkThreadedBMPDevice::flush() { - fQueue.reset(); - fAlloc.reset(); -} - -SkThreadedBMPDevice::DrawState::DrawState(SkThreadedBMPDevice* dev) { - // we need fDst to be set, and if we're actually drawing, to dirty the genID - if (!dev->accessPixels(&fDst)) { - // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels - fDst.reset(dev->imageInfo(), nullptr, 0); - } - fMatrix = dev->ctm(); - fMatrix.getType(); // make it thread safe - fRC = dev->fRCStack.rc(); -} - -SkDraw SkThreadedBMPDevice::DrawState::getDraw() const { - SkDraw draw; - draw.fDst = fDst; - draw.fMatrix = &fMatrix; - draw.fRC = &fRC; - return draw; -} - -SkThreadedBMPDevice::TileDraw::TileDraw(const DrawState& ds, const SkIRect& tileBounds) - : fTileRC(ds.fRC) { - fDst = ds.fDst; - fMatrix = &ds.fMatrix; - fTileRC.op(tileBounds, SkRegion::kIntersect_Op); - fRC = &fTileRC; -} - -static inline SkRect get_fast_bounds(const SkRect& r, const SkPaint& p) { - SkRect result; - if (p.canComputeFastBounds()) { - result = p.computeFastBounds(r, &result); - } else { - result = SkRectPriv::MakeLargest(); - } - return result; -} - -void SkThreadedBMPDevice::drawPaint(const SkPaint& paint) { - SkRect drawBounds = SkRectPriv::MakeLargest(); - fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){ - TileDraw(ds, tileBounds).drawPaint(paint); - }); -} - -void SkThreadedBMPDevice::drawPoints(SkCanvas::PointMode mode, size_t count, - const SkPoint pts[], const SkPaint& paint) { - SkPoint* clonedPts = this->cloneArray(pts, count); - SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds - fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){ - TileDraw(ds, tileBounds).drawPoints(mode, count, clonedPts, paint, nullptr); - }); -} - -void SkThreadedBMPDevice::drawRect(const SkRect& r, const SkPaint& paint) { - SkRect drawBounds = get_fast_bounds(r, paint); - fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){ - TileDraw(ds, tileBounds).drawRect(r, paint); - }); -} - -void SkThreadedBMPDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) { -#ifdef SK_IGNORE_BLURRED_RRECT_OPT - SkPath path; - - path.addRRect(rrect); - // call the VIRTUAL version, so any subclasses who do handle drawPath aren't - // required to override drawRRect. - this->drawPath(path, paint, nullptr, false); -#else - SkRect drawBounds = get_fast_bounds(rrect.getBounds(), paint); - fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){ - TileDraw(ds, tileBounds).drawRRect(rrect, paint); - }); -#endif -} - -void SkThreadedBMPDevice::drawPath(const SkPath& path, const SkPaint& paint, - const SkMatrix* prePathMatrix, bool pathIsMutable) { - SkRect drawBounds = path.isInverseFillType() ? SkRectPriv::MakeLargest() - : get_fast_bounds(path.getBounds(), paint); - // when path is small, init-once has too much overhead; init-once also can't handle mask filter - if (path.countVerbs() < 4 || paint.getMaskFilter()) { - fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds) { - TileDraw(ds, tileBounds).drawPath(path, paint, prePathMatrix, false); - }); - } else { - fQueue.push(drawBounds, [=](SkArenaAlloc* alloc, DrawElement* elem) { - SkInitOnceData data = {alloc, elem}; - elem->getDraw().drawPath(path, paint, prePathMatrix, false, false, nullptr, &data); - }); - } -} - -SkBitmap SkThreadedBMPDevice::snapBitmap(const SkBitmap& bitmap) { - // We can't use bitmap.isImmutable() because it could be temporarily immutable - // TODO(liyuqian): use genID to reduce the copy frequency - SkBitmap snap; - snap.allocPixels(bitmap.info()); - bitmap.readPixels(snap.pixmap()); - return snap; -} - -void SkThreadedBMPDevice::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix, - const SkRect* dstOrNull, const SkPaint& paint) { - SkRect drawBounds; - SkRect* clonedDstOrNull = nullptr; - if (dstOrNull == nullptr) { - drawBounds = SkRect::MakeWH(bitmap.width(), bitmap.height()); - matrix.mapRect(&drawBounds); - } else { - drawBounds = *dstOrNull; - clonedDstOrNull = fAlloc.make(*dstOrNull); - } - - SkBitmap snap = this->snapBitmap(bitmap); - fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){ - SkBitmap local = snap; // bitmap is not thread safe; copy a local one. - TileDraw(ds, tileBounds).drawBitmap(local, matrix, clonedDstOrNull, paint); - }); -} - -void SkThreadedBMPDevice::drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, - const SkRect& dst, const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) { - // SkBitmapDevice::drawBitmapRect may use shader and drawRect. In that case, we need to snap - // the bitmap here because we won't go into SkThreadedBMPDevice::drawBitmap. - SkBitmap snap = this->snapBitmap(bitmap); - this->SkBitmapDevice::drawBitmapRect(snap, src, dst, paint, constraint); -} - - -void SkThreadedBMPDevice::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) { - SkRect drawBounds = SkRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()); - SkBitmap snap = this->snapBitmap(bitmap); - fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, - const SkIRect& tileBounds){ - SkBitmap local = snap; // bitmap is not thread safe; copy a local one. - TileDraw(ds, tileBounds).drawSprite(local, x, y, paint); - }); -} - -void SkThreadedBMPDevice::drawPosText(const void* text, size_t len, const SkScalar xpos[], - int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) { - char* clonedText = this->cloneArray((const char*)text, len); - SkScalar* clonedXpos = this->cloneArray(xpos, paint.countText(text, len) * scalarsPerPos); - SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds - SkSurfaceProps prop(SkBitmapDeviceFilteredSurfaceProps(fBitmap, paint, this->surfaceProps())()); - fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){ - TileDraw(ds, tileBounds).drawPosText(clonedText, len, clonedXpos, scalarsPerPos, offset, - paint, &prop); - }); -} - -void SkThreadedBMPDevice::drawVertices(const SkVertices* vertices, const SkMatrix* bones, - int boneCount, SkBlendMode bmode, const SkPaint& paint) { - const sk_sp verts = sk_ref_sp(vertices); // retain vertices until flush - SkRect drawBounds = SkRectPriv::MakeLargest(); // TODO tighter drawBounds - - // Make a copy of the bone matrices. - SkMatrix* clonedBones = bones ? this->cloneArray(bones, boneCount) : nullptr; - - // Make the bone matrices thread-safe. - for (int i = 0; i < boneCount; i ++) { - clonedBones[i].getType(); - } - - fQueue.push(drawBounds, [=](SkArenaAlloc*, const DrawState& ds, const SkIRect& tileBounds){ - TileDraw(ds, tileBounds).drawVertices(verts->mode(), verts->vertexCount(), - verts->positions(), verts->texCoords(), - verts->colors(), verts->boneIndices(), - verts->boneWeights(), bmode, verts->indices(), - verts->indexCount(), paint, clonedBones, boneCount); - }); -} - -sk_sp SkThreadedBMPDevice::snapSpecial() { - this->flush(); - return this->makeSpecial(fBitmap); -} diff --git a/src/core/SkThreadedBMPDevice.h b/src/core/SkThreadedBMPDevice.h deleted file mode 100644 index 0cfb91db07..0000000000 --- a/src/core/SkThreadedBMPDevice.h +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkThreadedBMPDevice_DEFINED -#define SkThreadedBMPDevice_DEFINED - -#include "SkBitmapDevice.h" -#include "SkDraw.h" -#include "SkRectPriv.h" -#include "SkTaskGroup2D.h" -#include - -class SkThreadedBMPDevice : public SkBitmapDevice { -public: - // When threads = 0, we make fThreadCnt = tiles. Otherwise fThreadCnt = threads. - // When executor = nullptr, we manages the thread pool. Otherwise, the caller manages it. - SkThreadedBMPDevice(const SkBitmap& bitmap, int tiles, int threads = 0, - SkExecutor* executor = nullptr); - - ~SkThreadedBMPDevice() override { fQueue.finish(); } - -protected: - void drawPaint(const SkPaint& paint) override; - void drawPoints(SkCanvas::PointMode mode, size_t count, - const SkPoint[], const SkPaint& paint) override; - void drawRect(const SkRect& r, const SkPaint& paint) override; - void drawRRect(const SkRRect& rr, const SkPaint& paint) override; - - void drawPath(const SkPath&, const SkPaint&, const SkMatrix* prePathMatrix, - bool pathIsMutable) override; - void drawSprite(const SkBitmap&, int x, int y, const SkPaint&) override; - void drawPosText(const void* text, size_t len, const SkScalar pos[], - int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) override; - void drawVertices(const SkVertices*, const SkMatrix* bones, int boneCount, SkBlendMode, - const SkPaint&) override; - - void drawBitmap(const SkBitmap&, const SkMatrix&, const SkRect* dstOrNull, - const SkPaint&) override; - void drawBitmapRect(const SkBitmap& bitmap, const SkRect* src, const SkRect& dst, - const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) override; - - sk_sp snapSpecial() override; - - void flush() override; - -private: - // We store DrawState inside DrawElement because inifFn and drawFn both want to use it - struct DrawState { - SkPixmap fDst; - SkMatrix fMatrix; - SkRasterClip fRC; - - DrawState() {} - explicit DrawState(SkThreadedBMPDevice* dev); - - SkDraw getDraw() const; - }; - - class TileDraw : public SkDraw { - public: TileDraw(const DrawState& ds, const SkIRect& tileBounds); - private: SkRasterClip fTileRC; - }; - - class DrawElement { - public: - using InitFn = std::function; - using DrawFn = std::function; - - DrawElement() {} - DrawElement(SkThreadedBMPDevice* device, DrawFn&& drawFn, const SkIRect& drawBounds) - : fInitialized(true) - , fDrawFn(std::move(drawFn)) - , fDS(device) - , fDrawBounds(drawBounds) {} - DrawElement(SkThreadedBMPDevice* device, InitFn&& initFn, const SkIRect& drawBounds) - : fInitialized(false) - , fNeedInit(true) - , fInitFn(std::move(initFn)) - , fDS(device) - , fDrawBounds(drawBounds) {} - - SK_ALWAYS_INLINE bool tryInitOnce(SkArenaAlloc* alloc) { - bool t = true; - // If there are multiple threads reaching this point simutaneously, - // compare_exchange_strong ensures that only one thread can enter the if condition and - // do the initialization. - if (!fInitialized && fNeedInit && fNeedInit.compare_exchange_strong(t, false)) { -#ifdef SK_DEBUG - fDrawFn = 0; // Invalidate fDrawFn -#endif - fInitFn(alloc, this); - fInitialized = true; - SkASSERT(fDrawFn != 0); // Ensure that fInitFn does populate fDrawFn - return true; - } - return false; - } - - SK_ALWAYS_INLINE bool tryDraw(const SkIRect& tileBounds, SkArenaAlloc* alloc) { - if (!SkIRect::Intersects(tileBounds, fDrawBounds)) { - return true; - } - if (fInitialized) { - fDrawFn(alloc, fDS, tileBounds); - return true; - } - return false; - } - - SkDraw getDraw() const { return fDS.getDraw(); } - void setDrawFn(DrawFn&& fn) { fDrawFn = std::move(fn); } - - private: - std::atomic fInitialized; - std::atomic fNeedInit; - InitFn fInitFn; - DrawFn fDrawFn; - DrawState fDS; - SkIRect fDrawBounds; - }; - - class DrawQueue : public SkWorkKernel2D { - public: - static constexpr int MAX_QUEUE_SIZE = 100000; - - DrawQueue(SkThreadedBMPDevice* device) : fDevice(device) {} - void reset(); - - // For ~SkThreadedBMPDevice() to shutdown tasks, we use this instead of reset because reset - // will start new tasks. - void finish() { fTasks->finish(); } - - // Push a draw command into the queue. If Fn is DrawFn, we're pushing an element without - // the need of initialization. If Fn is InitFn, we're pushing an element with init-once - // and the InitFn will generate the DrawFn during initialization. - template - SK_ALWAYS_INLINE void push(const SkRect& rawDrawBounds, Fn&& fn) { - if (fSize == MAX_QUEUE_SIZE) { - this->reset(); - } - SkASSERT(fSize < MAX_QUEUE_SIZE); - SkIRect drawBounds = fDevice->transformDrawBounds(rawDrawBounds); - fElements[fSize].~DrawElement(); // release previous resources to prevent memory leak - new (&fElements[fSize++]) DrawElement(fDevice, std::move(fn), drawBounds); - fTasks->addColumn(); - } - - // SkWorkKernel2D - bool initColumn(int column, int thread) override; - bool work2D(int row, int column, int thread) override; - - private: - SkThreadedBMPDevice* fDevice; - std::unique_ptr fTasks; - SkTArray> fThreadAllocs; // 8k stack size - DrawElement fElements[MAX_QUEUE_SIZE]; - int fSize; - }; - - template - SkIRect transformDrawBounds(const SkRect& drawBounds) const { - if (drawBounds == SkRectPriv::MakeLargest()) { - return SkRectPriv::MakeILarge(); - } - SkRect transformedBounds; - if (useCTM) { - this->ctm().mapRect(&transformedBounds, drawBounds); - } else { - transformedBounds = drawBounds; - } - return transformedBounds.roundOut(); - } - - - - template - T* cloneArray(const T* array, int count) { - T* clone = fAlloc.makeArrayDefault(count); - memcpy(clone, array, sizeof(T) * count); - return clone; - } - - SkBitmap snapBitmap(const SkBitmap& bitmap); - - const int fTileCnt; - const int fThreadCnt; - SkTArray fTileBounds; - - /** - * This can either be - * 1. fInternalExecutor.get() which means that we're managing the thread pool's life cycle. - * 2. provided by our caller which means that our caller is managing the threads' life cycle. - * In the 2nd case, fInternalExecutor == nullptr. - */ - SkExecutor* fExecutor = nullptr; - std::unique_ptr fInternalExecutor; - - SkSTArenaAlloc<8 << 10> fAlloc; // so we can allocate memory that lives until flush - - DrawQueue fQueue; - - friend struct SkInitOnceData; // to access DrawElement and DrawState - friend class SkDraw; // to access DrawState - - typedef SkBitmapDevice INHERITED; -}; - -// Passed to SkDraw::drawXXX to enable threaded draw with init-once. The goal is to reuse as much -// code as possible from SkDraw. (See SkDraw::drawPath and SkDraw::drawDevPath for an example.) -struct SkInitOnceData { - SkArenaAlloc* fAlloc; - SkThreadedBMPDevice::DrawElement* fElement; - - void setEmptyDrawFn() { - fElement->setDrawFn([](SkArenaAlloc* threadAlloc, const SkThreadedBMPDevice::DrawState& ds, - const SkIRect& tileBounds){}); - } -}; - -#endif // SkThreadedBMPDevice_DEFINED -- cgit v1.2.3