/* * 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 GrDeferredProxyUploader_DEFINED #define GrDeferredProxyUploader_DEFINED #include "SkAutoPixmapStorage.h" #include "SkMakeUnique.h" #include "SkRefCnt.h" #include "SkSemaphore.h" #include "GrOpFlushState.h" #include "GrTextureProxyPriv.h" /** * GrDeferredProxyUploader assists with threaded generation of textures. Currently used by both * software clip masks, and the software path renderer. The calling code typically needs to store * some additional data (T) for use on the worker thread. GrTDeferredProxyUploader allows storing * such data. The common flow is: * * 1) A GrTDeferredProxyUploader is created, with some payload (eg an SkPath to draw). * The uploader is owned by the proxy that it's going to populate. * 2) A task is created with a pointer to the uploader. A worker thread executes that task, using * the payload data to allocate and fill in the fPixels pixmap. * 3) The worker thread calls signalAndFreeData(), which notifies the main thread that the pixmap * is ready, and then deletes the payload data (which is no longer needed). * 4) In parallel to 2-3, on the main thread... Some op is created that refers to the proxy. When * that op is added to an op list, the op list retains a pointer to the "deferred" proxies. * 5) At flush time, the op list ensures that the deferred proxies are instantiated, then calls * scheduleUpload on those proxies, which calls scheduleUpload on the uploader (below). * 6) scheduleUpload defers the upload even further, by adding an ASAPUpload to the flush. * 7) When the ASAP upload happens, we wait to make sure that the pixels are marked ready * (from step #3 on the worker thread). Then we perform the actual upload to the texture. * Finally, we call resetDeferredUploader, which deletes the uploader object, causing fPixels * to be freed. */ class GrDeferredProxyUploader : public SkNoncopyable { public: GrDeferredProxyUploader() : fScheduledUpload(false), fWaited(false) {} virtual ~GrDeferredProxyUploader() { // In normal usage (i.e., through GrTDeferredProxyUploader) this will be redundant this->wait(); } void scheduleUpload(GrOpFlushState* flushState, GrTextureProxy* proxy) { if (fScheduledUpload) { // Multiple references to the owning proxy may have caused us to already execute return; } auto uploadMask = [this, proxy](GrDeferredTextureUploadWritePixelsFn& writePixelsFn) { this->wait(); GrColorType pixelColorType = SkColorTypeToGrColorType(this->fPixels.info().colorType()); // If the worker thread was unable to allocate pixels, this check will fail, and we'll // end up drawing with an uninitialized mask texture, but at least we won't crash. if (this->fPixels.addr()) { writePixelsFn(proxy, 0, 0, this->fPixels.width(), this->fPixels.height(), pixelColorType, this->fPixels.addr(), this->fPixels.rowBytes()); } // Upload has finished, so tell the proxy to release this GrDeferredProxyUploader proxy->texPriv().resetDeferredUploader(); }; flushState->addASAPUpload(std::move(uploadMask)); fScheduledUpload = true; } void signalAndFreeData() { this->freeData(); fPixelsReady.signal(); } SkAutoPixmapStorage* getPixels() { return &fPixels; } protected: void wait() { if (!fWaited) { fPixelsReady.wait(); fWaited = true; } } private: virtual void freeData() {} SkAutoPixmapStorage fPixels; SkSemaphore fPixelsReady; bool fScheduledUpload; bool fWaited; }; template class GrTDeferredProxyUploader : public GrDeferredProxyUploader { public: template GrTDeferredProxyUploader(Args&&... args) : fData(skstd::make_unique(std::forward(args)...)) { } ~GrTDeferredProxyUploader() override { // We need to wait here, so that we don't free fData before the worker thread is done // with it. (This happens if the proxy is deleted early due to a full clear or failure // of an op list to instantiate). this->wait(); } T& data() { return *fData; } private: void freeData() override { fData.reset(); } std::unique_ptr fData; }; #endif