1 /*
2  * Copyright 2017 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef GrDeferredProxyUploader_DEFINED
9 #define GrDeferredProxyUploader_DEFINED
10 
11 #include "include/core/SkRefCnt.h"
12 #include "include/private/SkSemaphore.h"
13 #include "src/core/SkAutoPixmapStorage.h"
14 
15 #include "src/gpu/GrOpFlushState.h"
16 #include "src/gpu/GrTextureProxyPriv.h"
17 
18 /**
19  * GrDeferredProxyUploader assists with threaded generation of textures. Currently used by both
20  * software clip masks, and the software path renderer. The calling code typically needs to store
21  * some additional data (T) for use on the worker thread. GrTDeferredProxyUploader allows storing
22  * such data. The common flow is:
23  *
24  * 1) A GrTDeferredProxyUploader is created, with some payload (eg an SkPath to draw).
25  *    The uploader is owned by the proxy that it's going to populate.
26  * 2) A task is created with a pointer to the uploader. A worker thread executes that task, using
27  *    the payload data to allocate and fill in the fPixels pixmap.
28  * 3) The worker thread calls signalAndFreeData(), which notifies the main thread that the pixmap
29  *    is ready, and then deletes the payload data (which is no longer needed).
30  * 4) In parallel to 2-3, on the main thread... Some op is created that refers to the proxy. When
31  *    that op is added to an op list, the op list retains a pointer to the "deferred" proxies.
32  * 5) At flush time, the op list ensures that the deferred proxies are instantiated, then calls
33  *    scheduleUpload on those proxies, which calls scheduleUpload on the uploader (below).
34  * 6) scheduleUpload defers the upload even further, by adding an ASAPUpload to the flush.
35  * 7) When the ASAP upload happens, we wait to make sure that the pixels are marked ready
36  *    (from step #3 on the worker thread). Then we perform the actual upload to the texture.
37  *    Finally, we call resetDeferredUploader, which deletes the uploader object, causing fPixels
38  *    to be freed.
39  */
40 class GrDeferredProxyUploader : public SkNoncopyable {
41 public:
GrDeferredProxyUploader()42     GrDeferredProxyUploader() : fScheduledUpload(false), fWaited(false) {}
43 
~GrDeferredProxyUploader()44     virtual ~GrDeferredProxyUploader() {
45         // In normal usage (i.e., through GrTDeferredProxyUploader) this will be redundant
46         this->wait();
47     }
48 
scheduleUpload(GrOpFlushState * flushState,GrTextureProxy * proxy)49     void scheduleUpload(GrOpFlushState* flushState, GrTextureProxy* proxy) {
50         if (fScheduledUpload) {
51             // Multiple references to the owning proxy may have caused us to already execute
52             return;
53         }
54 
55         auto uploadMask = [this, proxy](GrDeferredTextureUploadWritePixelsFn& writePixelsFn) {
56             this->wait();
57             GrColorType pixelColorType = SkColorTypeToGrColorType(this->fPixels.info().colorType());
58             // If the worker thread was unable to allocate pixels, this check will fail, and we'll
59             // end up drawing with an uninitialized mask texture, but at least we won't crash.
60             if (this->fPixels.addr()) {
61                 writePixelsFn(proxy, 0, 0, this->fPixels.width(), this->fPixels.height(),
62                               pixelColorType, this->fPixels.addr(), this->fPixels.rowBytes());
63             }
64             // Upload has finished, so tell the proxy to release this GrDeferredProxyUploader
65             proxy->texPriv().resetDeferredUploader();
66         };
67         flushState->addASAPUpload(std::move(uploadMask));
68         fScheduledUpload = true;
69     }
70 
signalAndFreeData()71     void signalAndFreeData() {
72         this->freeData();
73         fPixelsReady.signal();
74     }
75 
getPixels()76     SkAutoPixmapStorage* getPixels() { return &fPixels; }
77 
78 protected:
wait()79     void wait() {
80         if (!fWaited) {
81             fPixelsReady.wait();
82             fWaited = true;
83         }
84     }
85 
86 private:
freeData()87     virtual void freeData() {}
88 
89     SkAutoPixmapStorage fPixels;
90     SkSemaphore fPixelsReady;
91     bool fScheduledUpload;
92     bool fWaited;
93 };
94 
95 template <typename T>
96 class GrTDeferredProxyUploader : public GrDeferredProxyUploader {
97 public:
98     template <typename... Args>
GrTDeferredProxyUploader(Args &&...args)99     GrTDeferredProxyUploader(Args&&... args)
100         : fData(std::make_unique<T>(std::forward<Args>(args)...)) {
101     }
102 
~GrTDeferredProxyUploader()103     ~GrTDeferredProxyUploader() override {
104         // We need to wait here, so that we don't free fData before the worker thread is done
105         // with it. (This happens if the proxy is deleted early due to a full clear or failure
106         // of an op list to instantiate).
107         this->wait();
108     }
109 
data()110     T& data() { return *fData; }
111 
112 private:
freeData()113     void freeData() override {
114         fData.reset();
115     }
116 
117     std::unique_ptr<T> fData;
118 };
119 
120 #endif
121