From 89889b69391a730f0ba2a1efb549864b7762263f Mon Sep 17 00:00:00 2001 From: reed Date: Wed, 29 Oct 2014 12:36:45 -0700 Subject: MultiPictureDraw is taskgroup aware. SampleApp is multipicturedraw aware. BUG=skia: Review URL: https://codereview.chromium.org/684923002 --- gyp/core.gypi | 2 + gyp/dm.gypi | 2 - gyp/pathops_skpclip.gyp | 1 - gyp/pathops_unittest.gyp | 1 - gyp/tools.gyp | 1 - include/core/SkMultiPictureDraw.h | 20 +++-- samplecode/SampleApp.cpp | 39 ++++++++- src/core/SkMultiPictureDraw.cpp | 137 ++++++++++++++++++++------------ src/core/SkTaskGroup.cpp | 161 ++++++++++++++++++++++++++++++++++++++ src/core/SkTaskGroup.h | 39 +++++++++ src/utils/SkTaskGroup.cpp | 160 ------------------------------------- src/utils/SkTaskGroup.h | 38 --------- tests/LazyPtrTest.cpp | 1 + tests/OnceTest.cpp | 1 + tests/PathOpsSkpClipTest.cpp | 1 + tests/skia_test.cpp | 1 + 16 files changed, 343 insertions(+), 262 deletions(-) create mode 100644 src/core/SkTaskGroup.cpp create mode 100644 src/core/SkTaskGroup.h delete mode 100644 src/utils/SkTaskGroup.cpp delete mode 100644 src/utils/SkTaskGroup.h diff --git a/gyp/core.gypi b/gyp/core.gypi index 9170fabde5..39454b6539 100644 --- a/gyp/core.gypi +++ b/gyp/core.gypi @@ -188,6 +188,8 @@ '<(skia_src_path)/core/SkStrokeRec.cpp', '<(skia_src_path)/core/SkStrokerPriv.cpp', '<(skia_src_path)/core/SkStrokerPriv.h', + '<(skia_src_path)/core/SkTaskGroup.cpp', + '<(skia_src_path)/core/SkTaskGroup.h', '<(skia_src_path)/core/SkTextBlob.cpp', '<(skia_src_path)/core/SkTextFormatParams.h', '<(skia_src_path)/core/SkTextMapStateProc.h', diff --git a/gyp/dm.gypi b/gyp/dm.gypi index c7447153d2..932f430dad 100644 --- a/gyp/dm.gypi +++ b/gyp/dm.gypi @@ -45,8 +45,6 @@ '../dm/DMWriteTask.cpp', '../gm/gm.cpp', - '../src/utils/SkTaskGroup.cpp', - '../src/pipe/utils/SamplePipeControllers.cpp', '../src/utils/debugger/SkDebugCanvas.cpp', '../src/utils/debugger/SkDrawCommand.cpp', diff --git a/gyp/pathops_skpclip.gyp b/gyp/pathops_skpclip.gyp index 32a909bd47..4dadd7942e 100755 --- a/gyp/pathops_skpclip.gyp +++ b/gyp/pathops_skpclip.gyp @@ -24,7 +24,6 @@ 'sources': [ '../tests/PathOpsDebug.cpp', '../tests/PathOpsSkpClipTest.cpp', - '../src/utils/SkTaskGroup.cpp', ], 'conditions': [ [ 'skia_android_framework == 1', { diff --git a/gyp/pathops_unittest.gyp b/gyp/pathops_unittest.gyp index 1aaccfa25d..c8732545f2 100644 --- a/gyp/pathops_unittest.gyp +++ b/gyp/pathops_unittest.gyp @@ -21,7 +21,6 @@ '../tests/PathOpsDebug.cpp', '../tests/PathOpsOpLoopThreadedTest.cpp', '../tests/skia_test.cpp', - '../src/utils/SkTaskGroup.cpp', ], 'conditions': [ [ 'skia_android_framework == 1', { diff --git a/gyp/tools.gyp b/gyp/tools.gyp index e689ed1fd7..dae1cf58c7 100644 --- a/gyp/tools.gyp +++ b/gyp/tools.gyp @@ -165,7 +165,6 @@ '../tools/skpdiff/SkImageDiffer.cpp', '../tools/skpdiff/SkPMetric.cpp', '../tools/skpdiff/skpdiff_util.cpp', - '../src/utils/SkTaskGroup.cpp', ], 'include_dirs': [ '../src/core/', # needed for SkTLList.h diff --git a/include/core/SkMultiPictureDraw.h b/include/core/SkMultiPictureDraw.h index d8d9cb7ecd..461d38136b 100644 --- a/include/core/SkMultiPictureDraw.h +++ b/include/core/SkMultiPictureDraw.h @@ -56,13 +56,23 @@ public: private: struct DrawData { - SkCanvas* canvas; // reffed - const SkPicture* picture; // reffed - SkMatrix matrix; - SkPaint* paint; // owned + SkCanvas* fCanvas; // reffed + const SkPicture* fPicture; // reffed + SkMatrix fMatrix; + SkPaint* fPaint; // owned + + void init(SkCanvas*, const SkPicture*, const SkMatrix*, const SkPaint*); + void draw(); + + static void Reset(SkTDArray&); + + static void Run(void* ctx) { + static_cast(ctx)->draw(); + } }; - SkTDArray fDrawData; + SkTDArray fThreadSafeDrawData; + SkTDArray fGPUDrawData; }; #endif diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp index 2c17379f88..1b4a55e5dd 100644 --- a/samplecode/SampleApp.cpp +++ b/samplecode/SampleApp.cpp @@ -742,6 +742,8 @@ DEFINE_bool(list, false, "List samples?"); DEFINE_string(pdfPath, "", "Path to direcotry of pdf files."); #endif +#include "SkTaskGroup.h" + SampleWindow::SampleWindow(void* hwnd, int argc, char** argv, DeviceManager* devManager) : INHERITED(hwnd) , fDevManager(NULL) { @@ -813,6 +815,7 @@ SampleWindow::SampleWindow(void* hwnd, int argc, char** argv, DeviceManager* dev fCurrIndex = 0; } + static SkTaskGroup::Enabler enabled(-1); gSampleWindow = this; #ifdef PIPE_FILE @@ -1306,7 +1309,7 @@ SkCanvas* SampleWindow::beforeChildren(SkCanvas* canvas) { return canvas; } - +#include "SkMultiPictureDraw.h" void SampleWindow::afterChildren(SkCanvas* orig) { if (fSaveToPdf) { fSaveToPdf = false; @@ -1336,7 +1339,39 @@ void SampleWindow::afterChildren(SkCanvas* orig) { if (true) { this->installDrawFilter(orig); - orig->drawPicture(picture); + + if (true) { + SkImageInfo info; + size_t rowBytes; + void* addr = orig->accessTopLayerPixels(&info, &rowBytes); + if (addr) { + SkSurface* surfs[4]; + SkMultiPictureDraw md; + + SkImageInfo n = SkImageInfo::Make(info.width()/2, info.height()/2, + info.colorType(), info.alphaType()); + int index = 0; + for (int y = 0; y < 2; ++y) { + for (int x = 0; x < 2; ++x) { + char* p = (char*)addr; + p += y * n.height() * rowBytes; + p += x * n.width() * sizeof(SkPMColor); + surfs[index] = SkSurface::NewRasterDirect(n, p, rowBytes); + SkCanvas* c = surfs[index]->getCanvas(); + c->translate(SkIntToScalar(-x * n.width()), + SkIntToScalar(-y * n.height())); + md.add(c, picture, NULL, NULL); + index++; + } + } + md.draw(); + for (int i = 0; i < 4; ++i) { + surfs[i]->unref(); + } + } + } else { + orig->drawPicture(picture); + } } else if (true) { SkDynamicMemoryWStream ostream; picture->serialize(&ostream); diff --git a/src/core/SkMultiPictureDraw.cpp b/src/core/SkMultiPictureDraw.cpp index 5fe3c0ea52..b59b63bffc 100644 --- a/src/core/SkMultiPictureDraw.cpp +++ b/src/core/SkMultiPictureDraw.cpp @@ -13,21 +13,49 @@ #include "SkCanvas.h" #include "SkMultiPictureDraw.h" #include "SkPicture.h" +#include "SkTaskGroup.h" + +void SkMultiPictureDraw::DrawData::draw() { + fCanvas->drawPicture(fPicture, &fMatrix, fPaint); +} + +void SkMultiPictureDraw::DrawData::init(SkCanvas* canvas, const SkPicture* picture, + const SkMatrix* matrix, const SkPaint* paint) { + fPicture = SkRef(picture); + fCanvas = SkRef(canvas); + if (matrix) { + fMatrix = *matrix; + } else { + fMatrix.setIdentity(); + } + if (paint) { + fPaint = SkNEW_ARGS(SkPaint, (*paint)); + } else { + fPaint = NULL; + } +} + +void SkMultiPictureDraw::DrawData::Reset(SkTDArray& data) { + for (int i = 0; i < data.count(); ++i) { + data[i].fPicture->unref(); + data[i].fCanvas->unref(); + SkDELETE(data[i].fPaint); + } + data.rewind(); +} + +////////////////////////////////////////////////////////////////////////////////////// SkMultiPictureDraw::SkMultiPictureDraw(int reserve) { if (reserve > 0) { - fDrawData.setReserve(reserve); + fGPUDrawData.setReserve(reserve); + fThreadSafeDrawData.setReserve(reserve); } } void SkMultiPictureDraw::reset() { - for (int i = 0; i < fDrawData.count(); ++i) { - fDrawData[i].picture->unref(); - fDrawData[i].canvas->unref(); - SkDELETE(fDrawData[i].paint); - } - - fDrawData.rewind(); + DrawData::Reset(fGPUDrawData); + DrawData::Reset(fThreadSafeDrawData); } void SkMultiPictureDraw::add(SkCanvas* canvas, @@ -39,47 +67,58 @@ void SkMultiPictureDraw::add(SkCanvas* canvas, return; } - DrawData* data = fDrawData.append(); - - data->picture = SkRef(picture); - data->canvas = SkRef(canvas); - if (matrix) { - data->matrix = *matrix; - } else { - data->matrix.setIdentity(); - } - if (paint) { - data->paint = SkNEW_ARGS(SkPaint, (*paint)); - } else { - data->paint = NULL; - } + SkTDArray& array = canvas->getGrContext() ? fGPUDrawData : fThreadSafeDrawData; + array.append()->init(canvas, picture, matrix, paint); } #undef SK_IGNORE_GPU_LAYER_HOISTING #define SK_IGNORE_GPU_LAYER_HOISTING 1 +class AutoMPDReset : SkNoncopyable { + SkMultiPictureDraw* fMPD; +public: + AutoMPDReset(SkMultiPictureDraw* mpd) : fMPD(mpd) {} + ~AutoMPDReset() { fMPD->reset(); } +}; + void SkMultiPictureDraw::draw() { + AutoMPDReset mpdreset(this); + // we place the taskgroup after the MPDReset, to ensure that we don't delete the DrawData + // objects until after we're finished the tasks (which have pointers to the data). + + SkTaskGroup group; + for (int i = 0; i < fThreadSafeDrawData.count(); ++i) { + group.add(DrawData::Run, &fThreadSafeDrawData[i]); + } + // we deliberately don't call wait() here, since the destructor will do that, this allows us + // to continue processing gpu-data without having to wait on the cpu tasks. + + const int count = fGPUDrawData.count(); + if (0 == count) { + return; + } #ifndef SK_IGNORE_GPU_LAYER_HOISTING - GrContext* context = NULL; + GrContext* context = fGPUDrawData[0].fCanvas->getGrContext(); + SkASSERT(context); - // Start by collecting all the layers that are going to be atlased and render + // Start by collecting all the layers that are going to be atlased and render // them (if necessary). Hoisting the free floating layers is deferred until // drawing the canvas that requires them. SkTDArray atlasedNeedRendering, atlasedRecycled; - for (int i = 0; i < fDrawData.count(); ++i) { - if (fDrawData[i].canvas->getGrContext() && - !fDrawData[i].paint && fDrawData[i].matrix.isIdentity()) { - SkASSERT(NULL == context || context == fDrawData[i].canvas->getGrContext()); - context = fDrawData[i].canvas->getGrContext(); + for (int i = 0; i < count; ++i) { + const DrawData& data = fGPUDrawData[i]; + // we only expect 1 context for all the canvases + SkASSERT(data.canvas->getGrContext() == context); + if (!data.fPaint && data.fMatrix.isIdentity()) { // TODO: this path always tries to optimize pictures. Should we // switch to this API approach (vs. SkCanvas::EXPERIMENTAL_optimize)? - fDrawData[i].canvas->EXPERIMENTAL_optimize(fDrawData[i].picture); + data.fCanvas->EXPERIMENTAL_optimize(data.fPicture); SkRect clipBounds; - if (!fDrawData[i].canvas->getClipBounds(&clipBounds)) { + if (!data.fCanvas->getClipBounds(&clipBounds)) { continue; } @@ -87,32 +126,33 @@ void SkMultiPictureDraw::draw() { // would improve the packing and reduce the number of swaps // TODO: another optimization would be to make a first pass to // lock any required layer that is already in the atlas - GrLayerHoister::FindLayersToAtlas(context, fDrawData[i].picture, + GrLayerHoister::FindLayersToAtlas(context, data.fPicture, clipBounds, &atlasedNeedRendering, &atlasedRecycled); } } - if (NULL != context) { - GrLayerHoister::DrawLayersToAtlas(context, atlasedNeedRendering); - } + GrLayerHoister::DrawLayersToAtlas(context, atlasedNeedRendering); SkTDArray needRendering, recycled; #endif - for (int i = 0; i < fDrawData.count(); ++i) { + for (int i = 0; i < count; ++i) { + const DrawData& data = fGPUDrawData[i]; + SkCanvas* canvas = data.fCanvas; + const SkPicture* picture = data.fPicture; + #ifndef SK_IGNORE_GPU_LAYER_HOISTING - if (fDrawData[i].canvas->getGrContext() && - !fDrawData[i].paint && fDrawData[i].matrix.isIdentity()) { + if (!data.fPaint && data.fMatrix.isIdentity()) { SkRect clipBounds; - if (!fDrawData[i].canvas->getClipBounds(&clipBounds)) { + if (!canvas->getClipBounds(&clipBounds)) { continue; } // Find the layers required by this canvas. It will return atlased // layers in the 'recycled' list since they have already been drawn. - GrLayerHoister::FindLayersToHoist(context, fDrawData[i].picture, + GrLayerHoister::FindLayersToHoist(context, picture, clipBounds, &needRendering, &recycled); GrLayerHoister::DrawLayers(context, needRendering); @@ -122,11 +162,10 @@ void SkMultiPictureDraw::draw() { GrLayerHoister::ConvertLayersToReplacements(needRendering, &replacements); GrLayerHoister::ConvertLayersToReplacements(recycled, &replacements); - const SkMatrix initialMatrix = fDrawData[i].canvas->getTotalMatrix(); + const SkMatrix initialMatrix = canvas->getTotalMatrix(); // Render the entire picture using new layers - GrRecordReplaceDraw(fDrawData[i].picture, fDrawData[i].canvas, - &replacements, initialMatrix, NULL); + GrRecordReplaceDraw(picture, canvas, &replacements, initialMatrix, NULL); GrLayerHoister::UnlockLayers(context, needRendering); GrLayerHoister::UnlockLayers(context, recycled); @@ -136,19 +175,13 @@ void SkMultiPictureDraw::draw() { } else #endif { - fDrawData[i].canvas->drawPicture(fDrawData[i].picture, - &fDrawData[i].matrix, - fDrawData[i].paint); + canvas->drawPicture(picture, &data.fMatrix, data.fPaint); } } #ifndef SK_IGNORE_GPU_LAYER_HOISTING - if (NULL != context) { - GrLayerHoister::UnlockLayers(context, atlasedNeedRendering); - GrLayerHoister::UnlockLayers(context, atlasedRecycled); - } + GrLayerHoister::UnlockLayers(context, atlasedNeedRendering); + GrLayerHoister::UnlockLayers(context, atlasedRecycled); #endif - - this->reset(); } diff --git a/src/core/SkTaskGroup.cpp b/src/core/SkTaskGroup.cpp new file mode 100644 index 0000000000..dd12538743 --- /dev/null +++ b/src/core/SkTaskGroup.cpp @@ -0,0 +1,161 @@ +#include "SkTaskGroup.h" + +#include "SkCondVar.h" +#include "SkRunnable.h" +#include "SkTDArray.h" +#include "SkThread.h" +#include "SkThreadUtils.h" + +#if defined(SK_BUILD_FOR_WIN32) + static inline int num_cores() { + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + return sysinfo.dwNumberOfProcessors; + } +#else + #include + static inline int num_cores() { + return (int) sysconf(_SC_NPROCESSORS_ONLN); + } +#endif + +namespace { + +class ThreadPool : SkNoncopyable { +public: + static void Add(SkRunnable* task, int32_t* pending) { + if (!gGlobal) { // If we have no threads, run synchronously. + return task->run(); + } + gGlobal->add(&CallRunnable, task, pending); + } + + static void Add(void (*fn)(void*), void* arg, int32_t* pending) { + if (!gGlobal) { + return fn(arg); + } + gGlobal->add(fn, arg, pending); + } + + static void Wait(int32_t* pending) { + if (!gGlobal) { // If we have no threads, the work must already be done. + SkASSERT(*pending == 0); + return; + } + while (sk_acquire_load(pending) > 0) { // Pairs with sk_atomic_dec here or in Loop. + // Lend a hand until our SkTaskGroup of interest is done. + Work work; + { + AutoLock lock(&gGlobal->fReady); + if (gGlobal->fWork.isEmpty()) { + // Someone has picked up all the work (including ours). How nice of them! + // (They may still be working on it, so we can't assert *pending == 0 here.) + continue; + } + gGlobal->fWork.pop(&work); + } + // This Work isn't necessarily part of our SkTaskGroup of interest, but that's fine. + // We threads gotta stick together. We're always making forward progress. + work.fn(work.arg); + sk_atomic_dec(work.pending); // Release pairs with the sk_acquire_load() just above. + } + } + +private: + struct AutoLock { + AutoLock(SkCondVar* c) : fC(c) { fC->lock(); } + ~AutoLock() { fC->unlock(); } + private: + SkCondVar* fC; + }; + + static void CallRunnable(void* arg) { static_cast(arg)->run(); } + + struct Work { + void (*fn)(void*); // A function to call, + void* arg; // its argument, + int32_t* pending; // then sk_atomic_dec(pending) afterwards. + }; + + explicit ThreadPool(int threads) : fDraining(false) { + if (threads == -1) { + threads = num_cores(); + } + for (int i = 0; i < threads; i++) { + fThreads.push(SkNEW_ARGS(SkThread, (&ThreadPool::Loop, this))); + fThreads.top()->start(); + } + } + + ~ThreadPool() { + SkASSERT(fWork.isEmpty()); // All SkTaskGroups should be destroyed by now. + { + AutoLock lock(&fReady); + fDraining = true; + fReady.broadcast(); + } + for (int i = 0; i < fThreads.count(); i++) { + fThreads[i]->join(); + } + SkASSERT(fWork.isEmpty()); // Can't hurt to double check. + fThreads.deleteAll(); + } + + void add(void (*fn)(void*), void* arg, int32_t* pending) { + Work work = { fn, arg, pending }; + sk_atomic_inc(pending); // No barrier needed. + { + AutoLock lock(&fReady); + fWork.push(work); + fReady.signal(); + } + } + + static void Loop(void* arg) { + ThreadPool* pool = (ThreadPool*)arg; + Work work; + while (true) { + { + AutoLock lock(&pool->fReady); + while (pool->fWork.isEmpty()) { + if (pool->fDraining) { + return; + } + pool->fReady.wait(); + } + pool->fWork.pop(&work); + } + work.fn(work.arg); + sk_atomic_dec(work.pending); // Release pairs with sk_acquire_load() in Wait(). + } + } + + SkTDArray fWork; + SkTDArray fThreads; + SkCondVar fReady; + bool fDraining; + + static ThreadPool* gGlobal; + friend struct SkTaskGroup::Enabler; +}; +ThreadPool* ThreadPool::gGlobal = NULL; + +} // namespace + +SkTaskGroup::Enabler::Enabler(int threads) { + SkASSERT(ThreadPool::gGlobal == NULL); + if (threads != 0) { + ThreadPool::gGlobal = SkNEW_ARGS(ThreadPool, (threads)); + } +} + +SkTaskGroup::Enabler::~Enabler() { + SkDELETE(ThreadPool::gGlobal); +} + +SkTaskGroup::SkTaskGroup() : fPending(0) {} + +void SkTaskGroup::add(SkRunnable* task) { ThreadPool::Add(task, &fPending); } +void SkTaskGroup::add(void (*fn)(void*), void* arg) { ThreadPool::Add(fn, arg, &fPending); } +void SkTaskGroup::wait() { ThreadPool::Wait(&fPending); } + diff --git a/src/core/SkTaskGroup.h b/src/core/SkTaskGroup.h new file mode 100644 index 0000000000..75443c37cb --- /dev/null +++ b/src/core/SkTaskGroup.h @@ -0,0 +1,39 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkTaskGroup_DEFINED +#define SkTaskGroup_DEFINED + +#include "SkTypes.h" + +struct SkRunnable; + +class SkTaskGroup : SkNoncopyable { +public: + // Create one of these in main() to enable SkTaskGroups globally. + struct Enabler : SkNoncopyable { + explicit Enabler(int threads = -1); // Default is system-reported core count. + ~Enabler(); + }; + + SkTaskGroup(); + ~SkTaskGroup() { this->wait(); } + + // Add a task to this SkTaskGroup. It will likely run on another thread. + // Neither add() method takes owership of any of its parameters. + void add(SkRunnable*); + void add(void (*fn)(void*), void* arg); + + // Block until all Tasks previously add()ed to this SkTaskGroup have run. + // You may safely reuse this SkTaskGroup after wait() returns. + void wait(); + +private: + /*atomic*/ int32_t fPending; +}; + +#endif//SkTaskGroup_DEFINED diff --git a/src/utils/SkTaskGroup.cpp b/src/utils/SkTaskGroup.cpp deleted file mode 100644 index f1ec7f4278..0000000000 --- a/src/utils/SkTaskGroup.cpp +++ /dev/null @@ -1,160 +0,0 @@ -#include "SkTaskGroup.h" - -#include "SkCondVar.h" -#include "SkTDArray.h" -#include "SkThread.h" -#include "SkThreadUtils.h" - -#if defined(SK_BUILD_FOR_WIN32) - static inline int num_cores() { - SYSTEM_INFO sysinfo; - GetSystemInfo(&sysinfo); - return sysinfo.dwNumberOfProcessors; - } -#else - #include - static inline int num_cores() { - return (int) sysconf(_SC_NPROCESSORS_ONLN); - } -#endif - -namespace { - -class ThreadPool : SkNoncopyable { -public: - static void Add(SkRunnable* task, int32_t* pending) { - if (!gGlobal) { // If we have no threads, run synchronously. - return task->run(); - } - gGlobal->add(&CallRunnable, task, pending); - } - - static void Add(void (*fn)(void*), void* arg, int32_t* pending) { - if (!gGlobal) { - return fn(arg); - } - gGlobal->add(fn, arg, pending); - } - - static void Wait(int32_t* pending) { - if (!gGlobal) { // If we have no threads, the work must already be done. - SkASSERT(*pending == 0); - return; - } - while (sk_acquire_load(pending) > 0) { // Pairs with sk_atomic_dec here or in Loop. - // Lend a hand until our SkTaskGroup of interest is done. - Work work; - { - AutoLock lock(&gGlobal->fReady); - if (gGlobal->fWork.isEmpty()) { - // Someone has picked up all the work (including ours). How nice of them! - // (They may still be working on it, so we can't assert *pending == 0 here.) - continue; - } - gGlobal->fWork.pop(&work); - } - // This Work isn't necessarily part of our SkTaskGroup of interest, but that's fine. - // We threads gotta stick together. We're always making forward progress. - work.fn(work.arg); - sk_atomic_dec(work.pending); // Release pairs with the sk_acquire_load() just above. - } - } - -private: - struct AutoLock { - AutoLock(SkCondVar* c) : fC(c) { fC->lock(); } - ~AutoLock() { fC->unlock(); } - private: - SkCondVar* fC; - }; - - static void CallRunnable(void* arg) { static_cast(arg)->run(); } - - struct Work { - void (*fn)(void*); // A function to call, - void* arg; // its argument, - int32_t* pending; // then sk_atomic_dec(pending) afterwards. - }; - - explicit ThreadPool(int threads) : fDraining(false) { - if (threads == -1) { - threads = num_cores(); - } - for (int i = 0; i < threads; i++) { - fThreads.push(SkNEW_ARGS(SkThread, (&ThreadPool::Loop, this))); - fThreads.top()->start(); - } - } - - ~ThreadPool() { - SkASSERT(fWork.isEmpty()); // All SkTaskGroups should be destroyed by now. - { - AutoLock lock(&fReady); - fDraining = true; - fReady.broadcast(); - } - for (int i = 0; i < fThreads.count(); i++) { - fThreads[i]->join(); - } - SkASSERT(fWork.isEmpty()); // Can't hurt to double check. - fThreads.deleteAll(); - } - - void add(void (*fn)(void*), void* arg, int32_t* pending) { - Work work = { fn, arg, pending }; - sk_atomic_inc(pending); // No barrier needed. - { - AutoLock lock(&fReady); - fWork.push(work); - fReady.signal(); - } - } - - static void Loop(void* arg) { - ThreadPool* pool = (ThreadPool*)arg; - Work work; - while (true) { - { - AutoLock lock(&pool->fReady); - while (pool->fWork.isEmpty()) { - if (pool->fDraining) { - return; - } - pool->fReady.wait(); - } - pool->fWork.pop(&work); - } - work.fn(work.arg); - sk_atomic_dec(work.pending); // Release pairs with sk_acquire_load() in Wait(). - } - } - - SkTDArray fWork; - SkTDArray fThreads; - SkCondVar fReady; - bool fDraining; - - static ThreadPool* gGlobal; - friend struct SkTaskGroup::Enabler; -}; -ThreadPool* ThreadPool::gGlobal = NULL; - -} // namespace - -SkTaskGroup::Enabler::Enabler(int threads) { - SkASSERT(ThreadPool::gGlobal == NULL); - if (threads != 0) { - ThreadPool::gGlobal = SkNEW_ARGS(ThreadPool, (threads)); - } -} - -SkTaskGroup::Enabler::~Enabler() { - SkDELETE(ThreadPool::gGlobal); -} - -SkTaskGroup::SkTaskGroup() : fPending(0) {} - -void SkTaskGroup::add(SkRunnable* task) { ThreadPool::Add(task, &fPending); } -void SkTaskGroup::add(void (*fn)(void*), void* arg) { ThreadPool::Add(fn, arg, &fPending); } -void SkTaskGroup::wait() { ThreadPool::Wait(&fPending); } - diff --git a/src/utils/SkTaskGroup.h b/src/utils/SkTaskGroup.h deleted file mode 100644 index c60ceda52e..0000000000 --- a/src/utils/SkTaskGroup.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2014 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkTaskGroup_DEFINED -#define SkTaskGroup_DEFINED - -#include "SkTypes.h" -#include "SkRunnable.h" - -class SkTaskGroup : SkNoncopyable { -public: - // Create one of these in main() to enable SkTaskGroups globally. - struct Enabler : SkNoncopyable { - explicit Enabler(int threads = -1); // Default is system-reported core count. - ~Enabler(); - }; - - SkTaskGroup(); - ~SkTaskGroup() { this->wait(); } - - // Add a task to this SkTaskGroup. It will likely run on another thread. - // Neither add() method takes owership of any of its parameters. - void add(SkRunnable*); - void add(void (*fn)(void*), void* arg); - - // Block until all Tasks previously add()ed to this SkTaskGroup have run. - // You may safely reuse this SkTaskGroup after wait() returns. - void wait(); - -private: - /*atomic*/ int32_t fPending; -}; - -#endif//SkTaskGroup_DEFINED diff --git a/tests/LazyPtrTest.cpp b/tests/LazyPtrTest.cpp index f719c2e0bf..799f705085 100644 --- a/tests/LazyPtrTest.cpp +++ b/tests/LazyPtrTest.cpp @@ -1,5 +1,6 @@ #include "Test.h" #include "SkLazyPtr.h" +#include "SkRunnable.h" #include "SkTaskGroup.h" namespace { diff --git a/tests/OnceTest.cpp b/tests/OnceTest.cpp index e2711f00bb..da901f41c4 100644 --- a/tests/OnceTest.cpp +++ b/tests/OnceTest.cpp @@ -6,6 +6,7 @@ */ #include "SkOnce.h" +#include "SkRunnable.h" #include "SkTaskGroup.h" #include "Test.h" diff --git a/tests/PathOpsSkpClipTest.cpp b/tests/PathOpsSkpClipTest.cpp index b8142cd654..ad0a95f22b 100755 --- a/tests/PathOpsSkpClipTest.cpp +++ b/tests/PathOpsSkpClipTest.cpp @@ -15,6 +15,7 @@ #include "SkPathOpsDebug.h" #include "SkPicture.h" #include "SkRTConf.h" +#include "SkRunnable.h" #include "SkTSort.h" #include "SkStream.h" #include "SkString.h" diff --git a/tests/skia_test.cpp b/tests/skia_test.cpp index 0058215ccf..172328ee4a 100644 --- a/tests/skia_test.cpp +++ b/tests/skia_test.cpp @@ -11,6 +11,7 @@ #include "SkCommonFlags.h" #include "SkGraphics.h" #include "SkOSFile.h" +#include "SkRunnable.h" #include "SkTArray.h" #include "SkTaskGroup.h" #include "SkTemplates.h" -- cgit v1.2.3