aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu/GrBackendTextureImageGenerator.cpp
blob: 466a842f23022ffceb750cd15e9d4768d94e17a2 (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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/*
 * Copyright 2017 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "GrBackendTextureImageGenerator.h"

#include "GrContext.h"
#include "GrContextPriv.h"
#include "GrGpu.h"
#include "GrProxyProvider.h"
#include "GrRenderTargetContext.h"
#include "GrResourceCache.h"
#include "GrResourceProvider.h"
#include "GrResourceProviderPriv.h"
#include "GrSemaphore.h"
#include "GrTexture.h"
#include "GrTexturePriv.h"
#include "GrTextureProxyPriv.h"

#include "SkGr.h"
#include "SkMessageBus.h"

GrBackendTextureImageGenerator::RefHelper::~RefHelper() {
    SkASSERT(nullptr == fBorrowedTexture);

    // Generator has been freed, and no one is borrowing the texture. Notify the original cache
    // that it can free the last ref, so it happens on the correct thread.
    GrGpuResourceFreedMessage msg { fOriginalTexture, fOwningContextID };
    SkMessageBus<GrGpuResourceFreedMessage>::Post(msg);
}

std::unique_ptr<SkImageGenerator>
GrBackendTextureImageGenerator::Make(sk_sp<GrTexture> texture, GrSurfaceOrigin origin,
                                     sk_sp<GrSemaphore> semaphore, SkColorType colorType,
                                     SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
    GrContext* context = texture->getContext();

    // Attach our texture to this context's resource cache. This ensures that deletion will happen
    // in the correct thread/context. This adds the only ref to the texture that will persist from
    // this point. That ref will be released when the generator's RefHelper is freed.
    context->contextPriv().getResourceCache()->insertCrossContextGpuResource(texture.get());

    GrBackendTexture backendTexture = texture->getBackendTexture();
    if (!context->contextPriv().caps()->validateBackendTexture(backendTexture, colorType,
                                                               &backendTexture.fConfig)) {
        return nullptr;
    }

    SkImageInfo info = SkImageInfo::Make(texture->width(), texture->height(), colorType, alphaType,
                                         std::move(colorSpace));
    return std::unique_ptr<SkImageGenerator>(new GrBackendTextureImageGenerator(
          info, texture.get(), origin, context->uniqueID(), std::move(semaphore), backendTexture));
}

GrBackendTextureImageGenerator::GrBackendTextureImageGenerator(const SkImageInfo& info,
                                                               GrTexture* texture,
                                                               GrSurfaceOrigin origin,
                                                               uint32_t owningContextID,
                                                               sk_sp<GrSemaphore> semaphore,
                                                               const GrBackendTexture& backendTex)
    : INHERITED(info)
    , fRefHelper(new RefHelper(texture, owningContextID))
    , fSemaphore(std::move(semaphore))
    , fBackendTexture(backendTex)
    , fConfig(backendTex.config())
    , fSurfaceOrigin(origin) { }

GrBackendTextureImageGenerator::~GrBackendTextureImageGenerator() {
    fRefHelper->unref();
}

///////////////////////////////////////////////////////////////////////////////////////////////////

void GrBackendTextureImageGenerator::ReleaseRefHelper_TextureReleaseProc(void* ctx) {
    RefHelper* refHelper = static_cast<RefHelper*>(ctx);
    SkASSERT(refHelper);

    refHelper->fBorrowedTexture = nullptr;
    refHelper->fBorrowingContextReleaseProc = nullptr;
    refHelper->fBorrowingContextID = SK_InvalidGenID;
    refHelper->unref();
}

sk_sp<GrTextureProxy> GrBackendTextureImageGenerator::onGenerateTexture(
        GrContext* context, const SkImageInfo& info, const SkIPoint& origin, bool willNeedMipMaps) {
    SkASSERT(context);

    if (context->contextPriv().getBackend() != fBackendTexture.backend()) {
        return nullptr;
    }
    if (info.colorType() != this->getInfo().colorType()) {
        return nullptr;
    }

    auto proxyProvider = context->contextPriv().proxyProvider();

    fBorrowingMutex.acquire();
    sk_sp<GrReleaseProcHelper> releaseProcHelper;
    if (SK_InvalidGenID != fRefHelper->fBorrowingContextID) {
        if (fRefHelper->fBorrowingContextID != context->uniqueID()) {
            fBorrowingMutex.release();
            return nullptr;
        } else {
            SkASSERT(fRefHelper->fBorrowingContextReleaseProc);
            // Ref the release proc to be held by the proxy we make below
            releaseProcHelper = sk_ref_sp(fRefHelper->fBorrowingContextReleaseProc);
        }
    } else {
        SkASSERT(!fRefHelper->fBorrowingContextReleaseProc);
        // The ref we add to fRefHelper here will be passed into and owned by the
        // GrReleaseProcHelper.
        fRefHelper->ref();
        releaseProcHelper.reset(new GrReleaseProcHelper(ReleaseRefHelper_TextureReleaseProc,
                                                        fRefHelper));
        fRefHelper->fBorrowingContextReleaseProc = releaseProcHelper.get();
    }
    fRefHelper->fBorrowingContextID = context->uniqueID();
    fBorrowingMutex.release();

    SkASSERT(fRefHelper->fBorrowingContextID == context->uniqueID());

    GrSurfaceDesc desc;
    desc.fWidth = fBackendTexture.width();
    desc.fHeight = fBackendTexture.height();
    desc.fConfig = fConfig;
    GrMipMapped mipMapped = fBackendTexture.hasMipMaps() ? GrMipMapped::kYes : GrMipMapped::kNo;

    // Must make copies of member variables to capture in the lambda since this image generator may
    // be deleted before we actuallly execute the lambda.
    sk_sp<GrSemaphore> semaphore = fSemaphore;
    GrBackendTexture backendTexture = fBackendTexture;
    RefHelper* refHelper = fRefHelper;

    sk_sp<GrTextureProxy> proxy = proxyProvider->createLazyProxy(
            [refHelper, releaseProcHelper, semaphore,
             backendTexture](GrResourceProvider* resourceProvider) {
                if (!resourceProvider) {
                    return sk_sp<GrTexture>();
                }

                if (semaphore) {
                    resourceProvider->priv().gpu()->waitSemaphore(semaphore);
                }

                sk_sp<GrTexture> tex;
                if (refHelper->fBorrowedTexture) {
                    // If a client re-draws the same image multiple times, the texture we return
                    // will be cached and re-used. If they draw a subset, though, we may be
                    // re-called. In that case, we want to re-use the borrowed texture we've
                    // previously created.
                    tex = sk_ref_sp(refHelper->fBorrowedTexture);
                    SkASSERT(tex);
                } else {
                    // We just gained access to the texture. If we're on the original context, we
                    // could use the original texture, but we'd have no way of detecting that it's
                    // no longer in-use. So we always make a wrapped copy, where the release proc
                    // informs us that the context is done with it. This is unfortunate - we'll have
                    // two texture objects referencing the same GPU object. However, no client can
                    // ever see the original texture, so this should be safe.
                    tex = resourceProvider->wrapBackendTexture(backendTexture,
                                                               kBorrow_GrWrapOwnership);
                    if (!tex) {
                        return sk_sp<GrTexture>();
                    }
                    refHelper->fBorrowedTexture = tex.get();

                    tex->setRelease(releaseProcHelper);
                }

                return tex;

            },
            desc, fSurfaceOrigin, mipMapped, SkBackingFit::kExact, SkBudgeted::kNo);

    if (!proxy) {
        return nullptr;
    }

    if (0 == origin.fX && 0 == origin.fY &&
        info.width() == fBackendTexture.width() && info.height() == fBackendTexture.height() &&
        (!willNeedMipMaps || GrMipMapped::kYes == proxy->mipMapped())) {
        // If the caller wants the entire texture and we have the correct mip support, we're done
        return proxy;
    } else {
        // Otherwise, make a copy of the requested subset. Make sure our temporary is renderable,
        // because Vulkan will want to do the copy as a draw. All other copies would require a
        // layout change in Vulkan and we do not change the layout of borrowed images.
        GrMipMapped mipMapped = willNeedMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;

        sk_sp<GrRenderTargetContext> rtContext(
            context->contextPriv().makeDeferredRenderTargetContext(
                SkBackingFit::kExact, info.width(), info.height(), proxy->config(), nullptr, 1,
                mipMapped, proxy->origin(), nullptr, SkBudgeted::kYes));

        if (!rtContext) {
            return nullptr;
        }

        SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, info.width(), info.height());
        if (!rtContext->copy(proxy.get(), subset, SkIPoint::Make(0, 0))) {
            return nullptr;
        }

        return rtContext->asTextureProxyRef();
    }
}