• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "GrBackendTextureImageGenerator.h"
9 
10 #include "GrContext.h"
11 #include "GrContextPriv.h"
12 #include "GrGpu.h"
13 #include "GrProxyProvider.h"
14 #include "GrRenderTargetContext.h"
15 #include "GrResourceCache.h"
16 #include "GrResourceProvider.h"
17 #include "GrResourceProviderPriv.h"
18 #include "GrSemaphore.h"
19 #include "GrTexture.h"
20 #include "GrTexturePriv.h"
21 
22 #include "SkGr.h"
23 #include "SkMessageBus.h"
24 
~RefHelper()25 GrBackendTextureImageGenerator::RefHelper::~RefHelper() {
26     SkASSERT(nullptr == fBorrowedTexture);
27 
28     // Generator has been freed, and no one is borrowing the texture. Notify the original cache
29     // that it can free the last ref, so it happens on the correct thread.
30     GrGpuResourceFreedMessage msg { fOriginalTexture, fOwningContextID };
31     SkMessageBus<GrGpuResourceFreedMessage>::Post(msg);
32 }
33 
34 std::unique_ptr<SkImageGenerator>
Make(sk_sp<GrTexture> texture,GrSurfaceOrigin origin,sk_sp<GrSemaphore> semaphore,SkAlphaType alphaType,sk_sp<SkColorSpace> colorSpace)35 GrBackendTextureImageGenerator::Make(sk_sp<GrTexture> texture, GrSurfaceOrigin origin,
36                                      sk_sp<GrSemaphore> semaphore,
37                                      SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) {
38     SkColorType colorType = kUnknown_SkColorType;
39     if (!GrPixelConfigToColorType(texture->config(), &colorType)) {
40         return nullptr;
41     }
42 
43     GrContext* context = texture->getContext();
44 
45     // Attach our texture to this context's resource cache. This ensures that deletion will happen
46     // in the correct thread/context. This adds the only ref to the texture that will persist from
47     // this point. That ref will be released when the generator's RefHelper is freed.
48     context->contextPriv().getResourceCache()->insertCrossContextGpuResource(texture.get());
49 
50     GrBackendTexture backendTexture = texture->getBackendTexture();
51 
52     SkImageInfo info = SkImageInfo::Make(texture->width(), texture->height(), colorType, alphaType,
53                                          std::move(colorSpace));
54     return std::unique_ptr<SkImageGenerator>(new GrBackendTextureImageGenerator(
55           info, texture.get(), origin, context->uniqueID(), std::move(semaphore), backendTexture));
56 }
57 
GrBackendTextureImageGenerator(const SkImageInfo & info,GrTexture * texture,GrSurfaceOrigin origin,uint32_t owningContextID,sk_sp<GrSemaphore> semaphore,const GrBackendTexture & backendTex)58 GrBackendTextureImageGenerator::GrBackendTextureImageGenerator(const SkImageInfo& info,
59                                                                GrTexture* texture,
60                                                                GrSurfaceOrigin origin,
61                                                                uint32_t owningContextID,
62                                                                sk_sp<GrSemaphore> semaphore,
63                                                                const GrBackendTexture& backendTex)
64     : INHERITED(info)
65     , fRefHelper(new RefHelper(texture, owningContextID))
66     , fSemaphore(std::move(semaphore))
67     , fBackendTexture(backendTex)
68     , fConfig(backendTex.config())
69     , fSurfaceOrigin(origin) { }
70 
~GrBackendTextureImageGenerator()71 GrBackendTextureImageGenerator::~GrBackendTextureImageGenerator() {
72     fRefHelper->unref();
73 }
74 
75 ///////////////////////////////////////////////////////////////////////////////////////////////////
76 
77 #if SK_SUPPORT_GPU
ReleaseRefHelper_TextureReleaseProc(void * ctx)78 void GrBackendTextureImageGenerator::ReleaseRefHelper_TextureReleaseProc(void* ctx) {
79     RefHelper* refHelper = static_cast<RefHelper*>(ctx);
80     SkASSERT(refHelper);
81 
82     refHelper->fBorrowedTexture = nullptr;
83     refHelper->fBorrowingContextReleaseProc = nullptr;
84     refHelper->fBorrowingContextID = SK_InvalidGenID;
85     refHelper->unref();
86 }
87 
onGenerateTexture(GrContext * context,const SkImageInfo & info,const SkIPoint & origin,SkTransferFunctionBehavior,bool willNeedMipMaps)88 sk_sp<GrTextureProxy> GrBackendTextureImageGenerator::onGenerateTexture(
89         GrContext* context, const SkImageInfo& info, const SkIPoint& origin,
90         SkTransferFunctionBehavior, bool willNeedMipMaps) {
91     SkASSERT(context);
92 
93     if (context->contextPriv().getBackend() != fBackendTexture.backend()) {
94         return nullptr;
95     }
96 
97     auto proxyProvider = context->contextPriv().proxyProvider();
98 
99     fBorrowingMutex.acquire();
100     sk_sp<GrReleaseProcHelper> releaseProcHelper;
101     if (SK_InvalidGenID != fRefHelper->fBorrowingContextID) {
102         if (fRefHelper->fBorrowingContextID != context->uniqueID()) {
103             fBorrowingMutex.release();
104             return nullptr;
105         } else {
106             SkASSERT(fRefHelper->fBorrowingContextReleaseProc);
107             // Ref the release proc to be held by the proxy we make below
108             releaseProcHelper = sk_ref_sp(fRefHelper->fBorrowingContextReleaseProc);
109         }
110     } else {
111         SkASSERT(!fRefHelper->fBorrowingContextReleaseProc);
112         // The ref we add to fRefHelper here will be passed into and owned by the
113         // GrReleaseProcHelper.
114         fRefHelper->ref();
115         releaseProcHelper.reset(new GrReleaseProcHelper(ReleaseRefHelper_TextureReleaseProc,
116                                                         fRefHelper));
117         fRefHelper->fBorrowingContextReleaseProc = releaseProcHelper.get();
118     }
119     fRefHelper->fBorrowingContextID = context->uniqueID();
120     fBorrowingMutex.release();
121 
122     SkASSERT(fRefHelper->fBorrowingContextID == context->uniqueID());
123 
124     GrSurfaceDesc desc;
125     desc.fOrigin = fSurfaceOrigin;
126     desc.fWidth = fBackendTexture.width();
127     desc.fHeight = fBackendTexture.height();
128     desc.fConfig = fConfig;
129     GrMipMapped mipMapped = fBackendTexture.hasMipMaps() ? GrMipMapped::kYes : GrMipMapped::kNo;
130 
131     // Must make copies of member variables to capture in the lambda since this image generator may
132     // be deleted before we actuallly execute the lambda.
133     sk_sp<GrSemaphore> semaphore = fSemaphore;
134     GrBackendTexture backendTexture = fBackendTexture;
135     RefHelper* refHelper = fRefHelper;
136 
137     sk_sp<GrTextureProxy> proxy = proxyProvider->createLazyProxy(
138             [refHelper, releaseProcHelper, semaphore, backendTexture]
139             (GrResourceProvider* resourceProvider) {
140                 if (!resourceProvider) {
141                     return sk_sp<GrTexture>();
142                 }
143 
144                 if (semaphore) {
145                     resourceProvider->priv().gpu()->waitSemaphore(semaphore);
146                 }
147 
148                 sk_sp<GrTexture> tex;
149                 if (refHelper->fBorrowedTexture) {
150                     // If a client re-draws the same image multiple times, the texture we return
151                     // will be cached and re-used. If they draw a subset, though, we may be
152                     // re-called. In that case, we want to re-use the borrowed texture we've
153                     // previously created.
154                     tex = sk_ref_sp(refHelper->fBorrowedTexture);
155                     SkASSERT(tex);
156                 } else {
157                     // We just gained access to the texture. If we're on the original context, we
158                     // could use the original texture, but we'd have no way of detecting that it's
159                     // no longer in-use. So we always make a wrapped copy, where the release proc
160                     // informs us that the context is done with it. This is unfortunate - we'll have
161                     // two texture objects referencing the same GPU object. However, no client can
162                     // ever see the original texture, so this should be safe.
163                     tex = resourceProvider->wrapBackendTexture(backendTexture,
164                                                                kBorrow_GrWrapOwnership);
165                     if (!tex) {
166                         return sk_sp<GrTexture>();
167                     }
168                     refHelper->fBorrowedTexture = tex.get();
169 
170                     tex->setRelease(releaseProcHelper);
171                 }
172 
173                 return tex;
174 
175             }, desc, mipMapped, SkBackingFit::kExact, SkBudgeted::kNo);
176 
177     if (0 == origin.fX && 0 == origin.fY &&
178         info.width() == fBackendTexture.width() && info.height() == fBackendTexture.height() &&
179         (!willNeedMipMaps || GrMipMapped::kYes == proxy->mipMapped())) {
180         // If the caller wants the entire texture and we have the correct mip support, we're done
181         return proxy;
182     } else {
183         // Otherwise, make a copy of the requested subset. Make sure our temporary is renderable,
184         // because Vulkan will want to do the copy as a draw. All other copies would require a
185         // layout change in Vulkan and we do not change the layout of borrowed images.
186         GrMipMapped mipMapped = willNeedMipMaps ? GrMipMapped::kYes : GrMipMapped::kNo;
187         sk_sp<SkColorSpace> colorSpace;
188         if (GrPixelConfigIsSRGB(desc.fConfig)) {
189             colorSpace = SkColorSpace::MakeSRGB();
190         }
191 
192         sk_sp<GrRenderTargetContext> rtContext(context->makeDeferredRenderTargetContext(
193                 SkBackingFit::kExact, info.width(), info.height(), proxy->config(),
194                 std::move(colorSpace), 1, mipMapped, proxy->origin(), nullptr, SkBudgeted::kYes));
195 
196         if (!rtContext) {
197             return nullptr;
198         }
199 
200         SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, info.width(), info.height());
201         if (!rtContext->copy(proxy.get(), subset, SkIPoint::Make(0, 0))) {
202             return nullptr;
203         }
204 
205         return rtContext->asTextureProxyRef();
206     }
207 }
208 #endif
209