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 */
40class GrDeferredProxyUploader : public SkNoncopyable {
41public:
42    GrDeferredProxyUploader() : fScheduledUpload(false), fWaited(false) {}
43
44    virtual ~GrDeferredProxyUploader() {
45        // In normal usage (i.e., through GrTDeferredProxyUploader) this will be redundant
46        this->wait();
47    }
48
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,
62                              SkIRect::MakeSize(fPixels.dimensions()),
63                              pixelColorType,
64                              this->fPixels.addr(),
65                              this->fPixels.rowBytes());
66            }
67            // Upload has finished, so tell the proxy to release this GrDeferredProxyUploader
68            proxy->texPriv().resetDeferredUploader();
69        };
70        flushState->addASAPUpload(std::move(uploadMask));
71        fScheduledUpload = true;
72    }
73
74    void signalAndFreeData() {
75        this->freeData();
76        fPixelsReady.signal();
77    }
78
79    SkAutoPixmapStorage* getPixels() { return &fPixels; }
80
81protected:
82    void wait() {
83        if (!fWaited) {
84            fPixelsReady.wait();
85            fWaited = true;
86        }
87    }
88
89private:
90    virtual void freeData() {}
91
92    SkAutoPixmapStorage fPixels;
93    SkSemaphore fPixelsReady;
94    bool fScheduledUpload;
95    bool fWaited;
96};
97
98template <typename T>
99class GrTDeferredProxyUploader : public GrDeferredProxyUploader {
100public:
101    template <typename... Args>
102    GrTDeferredProxyUploader(Args&&... args)
103        : fData(std::make_unique<T>(std::forward<Args>(args)...)) {
104    }
105
106    ~GrTDeferredProxyUploader() override {
107        // We need to wait here, so that we don't free fData before the worker thread is done
108        // with it. (This happens if the proxy is deleted early due to a full clear or failure
109        // of an op list to instantiate).
110        this->wait();
111    }
112
113    T& data() { return *fData; }
114
115private:
116    void freeData() override {
117        fData.reset();
118    }
119
120    std::unique_ptr<T> fData;
121};
122
123#endif
124