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