aboutsummaryrefslogtreecommitdiff
path: root/src/image/SkImage_GpuBase.h
blob: 9a85e56414ae99c797bd6720c42fc56b14e24437 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkImage_GpuBase_DEFINED
#define SkImage_GpuBase_DEFINED

#include "GrBackendSurface.h"
#include "GrTypesPriv.h"
#include "SkImage_Base.h"
#include "SkYUVAIndex.h"

class GrContext;
class SkColorSpace;

class SkImage_GpuBase : public SkImage_Base {
public:
    SkImage_GpuBase(sk_sp<GrContext>, int width, int height, uint32_t uniqueID,
                    SkAlphaType, SkBudgeted, sk_sp<SkColorSpace>);
    ~SkImage_GpuBase() override;

    GrContext* context() const final { return fContext.get(); }

    bool getROPixels(SkBitmap*, CachingHint) const final;
    sk_sp<SkImage> onMakeSubset(const SkIRect& subset) const final;

    bool onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
                      int srcX, int srcY, CachingHint) const override;

    sk_sp<GrTextureProxy> asTextureProxyRef() const override {
        // we shouldn't end up calling this
        SkASSERT(false);
        return this->INHERITED::asTextureProxyRef();
    }
    sk_sp<GrTextureProxy> asTextureProxyRef(GrContext*, const GrSamplerState&,
                                            SkScalar scaleAdjust[2]) const final;

    sk_sp<GrTextureProxy> refPinnedTextureProxy(uint32_t* uniqueID) const final {
        *uniqueID = this->uniqueID();
        return this->asTextureProxyRef();
    }

    GrBackendTexture onGetBackendTexture(bool flushPendingGrContextIO,
                                         GrSurfaceOrigin* origin) const final;

    GrTexture* onGetTexture() const final;

    sk_sp<SkImage> onMakeColorSpace(sk_sp<SkColorSpace>) const final;

    bool onIsValid(GrContext*) const final;

#if GR_TEST_UTILS
    void resetContext(sk_sp<GrContext> newContext) {
        SkASSERT(fContext->uniqueID() == newContext->uniqueID());
        fContext = newContext;
    }
#endif

    static bool ValidateBackendTexture(GrContext* ctx, const GrBackendTexture& tex,
                                       GrPixelConfig* config, SkColorType ct, SkAlphaType at,
                                       sk_sp<SkColorSpace> cs);
    static bool MakeTempTextureProxies(GrContext* ctx, const GrBackendTexture yuvaTextures[],
                                       int numTextures, const SkYUVAIndex [4],
                                       GrSurfaceOrigin imageOrigin,
                                       sk_sp<GrTextureProxy> tempTextureProxies[4]);

    static SkAlphaType GetAlphaTypeFromYUVAIndices(const SkYUVAIndex yuvaIndices[4]) {
        return -1 != yuvaIndices[SkYUVAIndex::kA_Index].fIndex ? kPremul_SkAlphaType
                                                               : kOpaque_SkAlphaType;
    }

    typedef ReleaseContext TextureContext;
    typedef void(*TextureFulfillProc)(TextureContext textureContext, GrBackendTexture* outTexture);
    typedef void(*PromiseDoneProc)(TextureContext textureContext);

protected:
    static bool RenderYUVAToRGBA(GrContext* ctx, GrRenderTargetContext* renderTargetContext,
                                 const SkRect& rect, SkYUVColorSpace yuvColorSpace,
                                 const sk_sp<GrTextureProxy> proxies[4],
                                 const SkYUVAIndex yuvaIndices[4]);

    sk_sp<GrContext>      fContext;
    const SkAlphaType     fAlphaType;  // alpha type for final image
    const SkBudgeted      fBudgeted;
    sk_sp<SkColorSpace>   fColorSpace; // color space for final image

private:
    typedef SkImage_Base INHERITED;
};


/**
 * This helper holds the normal hard ref for the Release proc as well as a hard ref on the DoneProc.
 * Thus when a GrTexture is being released, it will unref both the ReleaseProc and DoneProc.
 */
class SkPromiseReleaseProcHelper : public GrReleaseProcHelper {
public:
    SkPromiseReleaseProcHelper(SkImage_GpuBase::TextureReleaseProc releaseProc,
                               SkImage_GpuBase::TextureContext context,
                               sk_sp<GrReleaseProcHelper> doneHelper)
        : INHERITED(releaseProc, context)
        , fDoneProcHelper(std::move(doneHelper)) {
    }

    void weak_dispose() const override {
        // Call the inherited weak_dispose first so that we call the ReleaseProc before the DoneProc
        // if we hold the last ref to the DoneProc.
        INHERITED::weak_dispose();
        fDoneProcHelper.reset();
    }

private:
    mutable sk_sp<GrReleaseProcHelper> fDoneProcHelper;

    typedef GrReleaseProcHelper INHERITED;
};

/**
 * This helper class manages the ref counting for the the ReleaseProc and DoneProc for promise
 * images. It holds a weak ref on the ReleaseProc (hard refs are owned by GrTextures). The weak ref
 * allows us to reuse an outstanding ReleaseProc (because we dropped our GrTexture but the GrTexture
 * isn't done on the GPU) without needing to call FulfillProc again. It also holds a hard ref on the
 * DoneProc. The idea is that after every flush we may call the ReleaseProc so that the client can
 * free up their GPU memory if they want to. The life time of the DoneProc matches that of any
 * outstanding ReleaseProc as well as the PromiseImageHelper. Thus we won't call the DoneProc until
 * all ReleaseProcs are finished and we are finished with the PromiseImageHelper (i.e. won't call
 * FulfillProc again).
 */
class SkPromiseImageHelper {
public:
    SkPromiseImageHelper()
        : fFulfillProc(nullptr)
        , fReleaseProc(nullptr)
        , fContext(nullptr)
        , fDoneHelper(nullptr) {
    }

    void set(SkImage_GpuBase::TextureFulfillProc fulfillProc,
             SkImage_GpuBase::TextureReleaseProc releaseProc,
             SkImage_GpuBase::PromiseDoneProc doneProc,
             SkImage_GpuBase::TextureContext context) {
        fFulfillProc = fulfillProc;
        fReleaseProc = releaseProc;
        fContext = context;
        fDoneHelper.reset(new GrReleaseProcHelper(doneProc, context));
    }

    SkPromiseImageHelper(SkImage_GpuBase::TextureFulfillProc fulfillProc,
                         SkImage_GpuBase::TextureReleaseProc releaseProc,
                         SkImage_GpuBase::PromiseDoneProc doneProc,
                         SkImage_GpuBase::TextureContext context)
        : fFulfillProc(fulfillProc)
        , fReleaseProc(releaseProc)
        , fContext(context)
        , fDoneHelper(new GrReleaseProcHelper(doneProc, context)) {
    }

    bool isValid() { return SkToBool(fDoneHelper); }

    void reset() {
        this->resetReleaseHelper();
        fDoneHelper.reset();
    }

    sk_sp<GrTexture> getTexture(GrResourceProvider* resourceProvider, GrPixelConfig config);

private:
    // Weak unrefs fReleaseHelper and sets it to null
    void resetReleaseHelper() {
        if (fReleaseHelper) {
            fReleaseHelper->weak_unref();
            fReleaseHelper = nullptr;
        }
    }

    SkImage_GpuBase::TextureFulfillProc fFulfillProc;
    SkImage_GpuBase::TextureReleaseProc fReleaseProc;
    SkImage_GpuBase::TextureContext     fContext;

    // We cache the GrBackendTexture so that if we deleted the GrTexture but the the release proc
    // has yet not been called (this can happen on Vulkan), then we can create a new texture without
    // needing to call the fulfill proc again.
    GrBackendTexture           fBackendTex;
    // The fReleaseHelper is used to track a weak ref on the release proc. This helps us make sure
    // we are always pairing fulfill and release proc calls correctly.
    SkPromiseReleaseProcHelper*  fReleaseHelper = nullptr;
    // We don't want to call the fDoneHelper until we are done with the PromiseImageHelper and all
    // ReleaseHelpers are finished. Thus we hold a hard ref here and we will pass a hard ref to each
    // fReleaseHelper we make.
    sk_sp<GrReleaseProcHelper> fDoneHelper;
};

#endif