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 "include/gpu/GrDirectContext.h" |
9 | #include "include/gpu/GrRecordingContext.h" |
10 | #include "src/core/SkMessageBus.h" |
11 | #include "src/gpu/GrBackendTextureImageGenerator.h" |
12 | #include "src/gpu/GrContextPriv.h" |
13 | #include "src/gpu/GrGpu.h" |
14 | #include "src/gpu/GrProxyProvider.h" |
15 | #include "src/gpu/GrRecordingContextPriv.h" |
16 | #include "src/gpu/GrRenderTargetContext.h" |
17 | #include "src/gpu/GrResourceCache.h" |
18 | #include "src/gpu/GrResourceProvider.h" |
19 | #include "src/gpu/GrResourceProviderPriv.h" |
20 | #include "src/gpu/GrSemaphore.h" |
21 | #include "src/gpu/GrTexture.h" |
22 | #include "src/gpu/GrTextureProxyPriv.h" |
23 | #include "src/gpu/SkGr.h" |
24 | #include "src/gpu/gl/GrGLTexture.h" |
25 | |
26 | GrBackendTextureImageGenerator::RefHelper::RefHelper(GrTexture* texture, uint32_t owningContextID, |
27 | std::unique_ptr<GrSemaphore> semaphore) |
28 | : fOriginalTexture(texture) |
29 | , fOwningContextID(owningContextID) |
30 | , fBorrowingContextReleaseProc(nullptr) |
31 | , fBorrowingContextID(SK_InvalidGenID) |
32 | , fSemaphore(std::move(semaphore)) {} |
33 | |
34 | GrBackendTextureImageGenerator::RefHelper::~RefHelper() { |
35 | SkASSERT(fBorrowingContextID == SK_InvalidUniqueID); |
36 | |
37 | // Generator has been freed, and no one is borrowing the texture. Notify the original cache |
38 | // that it can free the last ref, so it happens on the correct thread. |
39 | GrTextureFreedMessage msg { fOriginalTexture, fOwningContextID }; |
40 | SkMessageBus<GrTextureFreedMessage>::Post(msg); |
41 | } |
42 | |
43 | std::unique_ptr<SkImageGenerator> |
44 | GrBackendTextureImageGenerator::Make(sk_sp<GrTexture> texture, GrSurfaceOrigin origin, |
45 | std::unique_ptr<GrSemaphore> semaphore, SkColorType colorType, |
46 | SkAlphaType alphaType, sk_sp<SkColorSpace> colorSpace) { |
47 | GrDirectContext* context = texture->getContext(); |
48 | |
49 | // Attach our texture to this context's resource cache. This ensures that deletion will happen |
50 | // in the correct thread/context. This adds the only ref to the texture that will persist from |
51 | // this point. That ref will be released when the generator's RefHelper is freed. |
52 | context->priv().getResourceCache()->insertDelayedTextureUnref(texture.get()); |
53 | |
54 | GrBackendTexture backendTexture = texture->getBackendTexture(); |
55 | |
56 | if (!context->priv().caps()->areColorTypeAndFormatCompatible( |
57 | SkColorTypeToGrColorType(colorType), backendTexture.getBackendFormat())) { |
58 | return nullptr; |
59 | } |
60 | |
61 | SkImageInfo info = SkImageInfo::Make(texture->width(), texture->height(), colorType, alphaType, |
62 | std::move(colorSpace)); |
63 | return std::unique_ptr<SkImageGenerator>(new GrBackendTextureImageGenerator( |
64 | info, texture.get(), origin, context->priv().contextID(), |
65 | std::move(semaphore), backendTexture)); |
66 | } |
67 | |
68 | GrBackendTextureImageGenerator::GrBackendTextureImageGenerator( |
69 | const SkImageInfo& info, |
70 | GrTexture* texture, |
71 | GrSurfaceOrigin origin, |
72 | uint32_t owningContextID, |
73 | std::unique_ptr<GrSemaphore> semaphore, |
74 | const GrBackendTexture& backendTex) |
75 | : INHERITED(info) |
76 | , fRefHelper(new RefHelper(texture, owningContextID, std::move(semaphore))) |
77 | , fBackendTexture(backendTex) |
78 | , fSurfaceOrigin(origin) {} |
79 | |
80 | GrBackendTextureImageGenerator::~GrBackendTextureImageGenerator() { |
81 | fRefHelper->unref(); |
82 | } |
83 | |
84 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
85 | |
86 | void GrBackendTextureImageGenerator::ReleaseRefHelper_TextureReleaseProc(void* ctx) { |
87 | RefHelper* refHelper = static_cast<RefHelper*>(ctx); |
88 | SkASSERT(refHelper); |
89 | |
90 | refHelper->fBorrowingContextReleaseProc = nullptr; |
91 | refHelper->fBorrowingContextID = SK_InvalidGenID; |
92 | refHelper->unref(); |
93 | } |
94 | |
95 | GrSurfaceProxyView GrBackendTextureImageGenerator::onGenerateTexture( |
96 | GrRecordingContext* context, |
97 | const SkImageInfo& info, |
98 | const SkIPoint& origin, |
99 | GrMipmapped mipMapped, |
100 | GrImageTexGenPolicy texGenPolicy) { |
101 | SkASSERT(context); |
102 | |
103 | if (context->backend() != fBackendTexture.backend()) { |
104 | return {}; |
105 | } |
106 | if (info.colorType() != this->getInfo().colorType()) { |
107 | return {}; |
108 | } |
109 | |
110 | auto proxyProvider = context->priv().proxyProvider(); |
111 | |
112 | fBorrowingMutex.acquire(); |
113 | sk_sp<GrRefCntedCallback> releaseProcHelper; |
114 | if (SK_InvalidGenID != fRefHelper->fBorrowingContextID) { |
115 | if (fRefHelper->fBorrowingContextID != context->priv().contextID()) { |
116 | fBorrowingMutex.release(); |
117 | context->priv().printWarningMessage( |
118 | "GrBackendTextureImageGenerator: Trying to use texture on two GrContexts!\n" ); |
119 | return {}; |
120 | } else { |
121 | SkASSERT(fRefHelper->fBorrowingContextReleaseProc); |
122 | // Ref the release proc to be held by the proxy we make below |
123 | releaseProcHelper = sk_ref_sp(fRefHelper->fBorrowingContextReleaseProc); |
124 | } |
125 | } else { |
126 | SkASSERT(!fRefHelper->fBorrowingContextReleaseProc); |
127 | // The ref we add to fRefHelper here will be passed into and owned by the |
128 | // GrRefCntedCallback. |
129 | fRefHelper->ref(); |
130 | releaseProcHelper.reset( |
131 | new GrRefCntedCallback(ReleaseRefHelper_TextureReleaseProc, fRefHelper)); |
132 | fRefHelper->fBorrowingContextReleaseProc = releaseProcHelper.get(); |
133 | } |
134 | fRefHelper->fBorrowingContextID = context->priv().contextID(); |
135 | if (!fRefHelper->fBorrowedTextureKey.isValid()) { |
136 | static const auto kDomain = GrUniqueKey::GenerateDomain(); |
137 | GrUniqueKey::Builder builder(&fRefHelper->fBorrowedTextureKey, kDomain, 1); |
138 | builder[0] = this->uniqueID(); |
139 | } |
140 | fBorrowingMutex.release(); |
141 | |
142 | SkASSERT(fRefHelper->fBorrowingContextID == context->priv().contextID()); |
143 | |
144 | GrBackendFormat backendFormat = fBackendTexture.getBackendFormat(); |
145 | SkASSERT(backendFormat.isValid()); |
146 | |
147 | GrColorType grColorType = SkColorTypeToGrColorType(info.colorType()); |
148 | |
149 | GrMipmapped textureIsMipMapped = fBackendTexture.hasMipmaps() ? GrMipmapped::kYes |
150 | : GrMipmapped::kNo; |
151 | |
152 | // Ganesh assumes that, when wrapping a mipmapped backend texture from a client, that its |
153 | // mipmaps are fully fleshed out. |
154 | GrMipmapStatus mipmapStatus = fBackendTexture.hasMipmaps() |
155 | ? GrMipmapStatus::kValid : GrMipmapStatus::kNotAllocated; |
156 | |
157 | GrSwizzle readSwizzle = context->priv().caps()->getReadSwizzle(backendFormat, grColorType); |
158 | |
159 | // Must make copies of member variables to capture in the lambda since this image generator may |
160 | // be deleted before we actually execute the lambda. |
161 | sk_sp<GrTextureProxy> proxy = proxyProvider->createLazyProxy( |
162 | [refHelper = fRefHelper, releaseProcHelper, backendTexture = fBackendTexture]( |
163 | GrResourceProvider* resourceProvider, |
164 | const GrSurfaceProxy::LazySurfaceDesc&) -> GrSurfaceProxy::LazyCallbackResult { |
165 | if (refHelper->fSemaphore) { |
166 | resourceProvider->priv().gpu()->waitSemaphore(refHelper->fSemaphore.get()); |
167 | } |
168 | |
169 | // If a client re-draws the same image multiple times, the texture we return |
170 | // will be cached and re-used. If they draw a subset, though, we may be |
171 | // re-called. In that case, we want to re-use the borrowed texture we've |
172 | // previously created. |
173 | sk_sp<GrTexture> tex; |
174 | SkASSERT(refHelper->fBorrowedTextureKey.isValid()); |
175 | auto surf = resourceProvider->findByUniqueKey<GrSurface>( |
176 | refHelper->fBorrowedTextureKey); |
177 | if (surf) { |
178 | SkASSERT(surf->asTexture()); |
179 | tex = sk_ref_sp(surf->asTexture()); |
180 | } else { |
181 | // We just gained access to the texture. If we're on the original |
182 | // context, we could use the original texture, but we'd have no way of |
183 | // detecting that it's no longer in-use. So we always make a wrapped |
184 | // copy, where the release proc informs us that the context is done with |
185 | // it. This is unfortunate - we'll have two texture objects referencing |
186 | // the same GPU object. However, no client can ever see the original |
187 | // texture, so this should be safe. We make the texture uncacheable so |
188 | // that the release proc is called ASAP. |
189 | tex = resourceProvider->wrapBackendTexture( |
190 | backendTexture, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, |
191 | kRead_GrIOType); |
192 | if (!tex) { |
193 | return {}; |
194 | } |
195 | tex->setRelease(releaseProcHelper); |
196 | tex->resourcePriv().setUniqueKey(refHelper->fBorrowedTextureKey); |
197 | } |
198 | // We use keys to avoid re-wrapping the GrBackendTexture in a GrTexture. |
199 | // This is unrelated to the whatever SkImage key may be assigned to the |
200 | // proxy. |
201 | return {std::move(tex), true, GrSurfaceProxy::LazyInstantiationKeyMode::kUnsynced}; |
202 | }, |
203 | backendFormat, fBackendTexture.dimensions(), GrRenderable::kNo, 1, textureIsMipMapped, |
204 | mipmapStatus, GrInternalSurfaceFlags::kReadOnly, SkBackingFit::kExact, SkBudgeted::kNo, |
205 | GrProtected::kNo, GrSurfaceProxy::UseAllocator::kYes); |
206 | if (!proxy) { |
207 | return {}; |
208 | } |
209 | |
210 | if (texGenPolicy == GrImageTexGenPolicy::kDraw && origin.isZero() && |
211 | info.dimensions() == fBackendTexture.dimensions() && |
212 | (mipMapped == GrMipmapped::kNo || proxy->mipmapped() == GrMipmapped::kYes)) { |
213 | // If the caller wants the entire texture and we have the correct mip support, we're done |
214 | return GrSurfaceProxyView(std::move(proxy), fSurfaceOrigin, readSwizzle); |
215 | } else { |
216 | SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, info.width(), info.height()); |
217 | |
218 | SkBudgeted budgeted = texGenPolicy == GrImageTexGenPolicy::kNew_Uncached_Unbudgeted |
219 | ? SkBudgeted::kNo |
220 | : SkBudgeted::kYes; |
221 | |
222 | auto copy = GrSurfaceProxy::Copy(context, proxy.get(), fSurfaceOrigin, mipMapped, subset, |
223 | SkBackingFit::kExact, budgeted); |
224 | return {std::move(copy), fSurfaceOrigin, readSwizzle}; |
225 | } |
226 | } |
227 | |