blob: e4aabb709509dd8ce8fece4d292068af59c24167 [file] [log] [blame]
Jim Van Verth8026ccc2018-10-04 13:10:39 -04001/*
2 * Copyright 2018 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
Brian Salomoncdd8a0a2019-01-10 12:09:52 -05008#include "SkImage_GpuBase.h"
Jim Van Verth8026ccc2018-10-04 13:10:39 -04009#include "GrBackendSurface.h"
10#include "GrClip.h"
11#include "GrContext.h"
12#include "GrContextPriv.h"
13#include "GrRenderTargetContext.h"
14#include "GrTexture.h"
15#include "GrTextureAdjuster.h"
16#include "SkBitmapCache.h"
17#include "SkImage_Gpu.h"
Brian Salomoncdd8a0a2019-01-10 12:09:52 -050018#include "SkPromiseImageTexture.h"
Jim Van Verth8bbce0e2018-10-08 14:34:52 -040019#include "SkReadPixelsRec.h"
Brian Salomon1bf0ed82019-01-16 13:51:35 -050020#include "SkTLList.h"
Brian Salomoncdd8a0a2019-01-10 12:09:52 -050021#include "effects/GrYUVtoRGBEffect.h"
Jim Van Verth8026ccc2018-10-04 13:10:39 -040022
23SkImage_GpuBase::SkImage_GpuBase(sk_sp<GrContext> context, int width, int height, uint32_t uniqueID,
Brian Salomonf05e6d32018-12-20 08:41:41 -050024 SkAlphaType at, sk_sp<SkColorSpace> cs)
Jim Van Verth8026ccc2018-10-04 13:10:39 -040025 : INHERITED(width, height, uniqueID)
26 , fContext(std::move(context))
27 , fAlphaType(at)
Jim Van Verth8026ccc2018-10-04 13:10:39 -040028 , fColorSpace(std::move(cs)) {}
29
30SkImage_GpuBase::~SkImage_GpuBase() {}
31
32//////////////////////////////////////////////////////////////////////////////////////////////////
33
34bool SkImage_GpuBase::ValidateBackendTexture(GrContext* ctx, const GrBackendTexture& tex,
35 GrPixelConfig* config, SkColorType ct, SkAlphaType at,
36 sk_sp<SkColorSpace> cs) {
37 if (!tex.isValid()) {
38 return false;
39 }
40 // TODO: Create a SkImageColorInfo struct for color, alpha, and color space so we don't need to
41 // create a fake image info here.
42 SkImageInfo info = SkImageInfo::Make(1, 1, ct, at, cs);
43 if (!SkImageInfoIsValid(info)) {
44 return false;
45 }
Brian Salomonf391d0f2018-12-14 09:18:50 -050046 GrBackendFormat backendFormat = tex.getBackendFormat();
47 if (!backendFormat.isValid()) {
48 return false;
49 }
50 *config = ctx->contextPriv().caps()->getConfigFromBackendFormat(backendFormat, ct);
51 return *config != kUnknown_GrPixelConfig;
Jim Van Verth8026ccc2018-10-04 13:10:39 -040052}
53
54//////////////////////////////////////////////////////////////////////////////////////////////////
55
Brian Osmane50cdf02018-10-19 13:02:14 -040056bool SkImage_GpuBase::getROPixels(SkBitmap* dst, CachingHint chint) const {
Jim Van Verth8026ccc2018-10-04 13:10:39 -040057 if (!fContext->contextPriv().resourceProvider()) {
58 // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
59 return false;
60 }
61
Jim Van Verth8026ccc2018-10-04 13:10:39 -040062 const auto desc = SkBitmapCacheDesc::Make(this);
63 if (SkBitmapCache::Find(desc, dst)) {
Jim Van Verth8026ccc2018-10-04 13:10:39 -040064 SkASSERT(dst->isImmutable());
65 SkASSERT(dst->getPixels());
66 return true;
67 }
68
69 SkBitmapCache::RecPtr rec = nullptr;
70 SkPixmap pmap;
71 if (kAllow_CachingHint == chint) {
72 rec = SkBitmapCache::Alloc(desc, this->onImageInfo(), &pmap);
73 if (!rec) {
74 return false;
75 }
76 } else {
77 if (!dst->tryAllocPixels(this->onImageInfo()) || !dst->peekPixels(&pmap)) {
78 return false;
79 }
80 }
81
82 sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
83 this->asTextureProxyRef(),
84 fColorSpace);
85 if (!sContext) {
86 return false;
87 }
88
89 if (!sContext->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), 0, 0)) {
90 return false;
91 }
92
93 if (rec) {
94 SkBitmapCache::Add(std::move(rec), dst);
95 this->notifyAddedToRasterCache();
96 }
97 return true;
98}
99
100sk_sp<SkImage> SkImage_GpuBase::onMakeSubset(const SkIRect& subset) const {
101 sk_sp<GrSurfaceProxy> proxy = this->asTextureProxyRef();
102
103 GrSurfaceDesc desc;
104 desc.fWidth = subset.width();
105 desc.fHeight = subset.height();
106 desc.fConfig = proxy->config();
107
Greg Daniel4065d452018-11-16 15:43:41 -0500108 GrBackendFormat format = proxy->backendFormat().makeTexture2D();
109 if (!format.isValid()) {
110 return nullptr;
111 }
112
Brian Salomonf05e6d32018-12-20 08:41:41 -0500113 // TODO: Should this inherit our proxy's budgeted status?
Jim Van Verth8026ccc2018-10-04 13:10:39 -0400114 sk_sp<GrSurfaceContext> sContext(fContext->contextPriv().makeDeferredSurfaceContext(
Brian Salomonf05e6d32018-12-20 08:41:41 -0500115 format, desc, proxy->origin(), GrMipMapped::kNo, SkBackingFit::kExact,
116 proxy->isBudgeted()));
Jim Van Verth8026ccc2018-10-04 13:10:39 -0400117 if (!sContext) {
118 return nullptr;
119 }
120
121 if (!sContext->copy(proxy.get(), subset, SkIPoint::Make(0, 0))) {
122 return nullptr;
123 }
124
125 // MDB: this call is okay bc we know 'sContext' was kExact
Brian Salomonf05e6d32018-12-20 08:41:41 -0500126 return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID, fAlphaType,
127 sContext->asTextureProxyRef(), fColorSpace);
Jim Van Verth8026ccc2018-10-04 13:10:39 -0400128}
129
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400130static void apply_premul(const SkImageInfo& info, void* pixels, size_t rowBytes) {
131 switch (info.colorType()) {
132 case kRGBA_8888_SkColorType:
133 case kBGRA_8888_SkColorType:
134 break;
135 default:
136 return; // nothing to do
137 }
138
Brian Salomon3f4cd772019-01-11 16:03:19 -0500139 // SkColor is not necessarily RGBA or BGRA, but it is one of them on little-endian,
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400140 // and in either case, the alpha-byte is always in the same place, so we can safely call
141 // SkPreMultiplyColor()
142 //
143 SkColor* row = (SkColor*)pixels;
144 for (int y = 0; y < info.height(); ++y) {
145 for (int x = 0; x < info.width(); ++x) {
146 row[x] = SkPreMultiplyColor(row[x]);
147 }
148 row = (SkColor*)((char*)(row)+rowBytes);
149 }
150}
151
152bool SkImage_GpuBase::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
153 int srcX, int srcY, CachingHint) const {
154 if (!fContext->contextPriv().resourceProvider()) {
155 // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
156 return false;
157 }
158
159 if (!SkImageInfoValidConversion(dstInfo, this->onImageInfo())) {
160 return false;
161 }
162
163 SkReadPixelsRec rec(dstInfo, dstPixels, dstRB, srcX, srcY);
164 if (!rec.trim(this->width(), this->height())) {
165 return false;
166 }
167
168 // TODO: this seems to duplicate code in GrTextureContext::onReadPixels and
169 // GrRenderTargetContext::onReadPixels
170 uint32_t flags = 0;
171 if (kUnpremul_SkAlphaType == rec.fInfo.alphaType() && kPremul_SkAlphaType == fAlphaType) {
172 // let the GPU perform this transformation for us
173 flags = GrContextPriv::kUnpremul_PixelOpsFlag;
174 }
175
176 sk_sp<GrSurfaceContext> sContext = fContext->contextPriv().makeWrappedSurfaceContext(
177 this->asTextureProxyRef(), fColorSpace);
178 if (!sContext) {
179 return false;
180 }
181
182 if (!sContext->readPixels(rec.fInfo, rec.fPixels, rec.fRowBytes, rec.fX, rec.fY, flags)) {
183 return false;
184 }
185
186 // do we have to manually fix-up the alpha channel?
187 // src dst
188 // unpremul premul fix manually
189 // premul unpremul done by kUnpremul_PixelOpsFlag
190 // all other combos need to change.
191 //
192 // Should this be handled by Ganesh? todo:?
193 //
194 if (kPremul_SkAlphaType == rec.fInfo.alphaType() && kUnpremul_SkAlphaType == fAlphaType) {
195 apply_premul(rec.fInfo, rec.fPixels, rec.fRowBytes);
196 }
197 return true;
198}
199
Jim Van Verth8026ccc2018-10-04 13:10:39 -0400200sk_sp<GrTextureProxy> SkImage_GpuBase::asTextureProxyRef(GrContext* context,
201 const GrSamplerState& params,
Jim Van Verth8026ccc2018-10-04 13:10:39 -0400202 SkScalar scaleAdjust[2]) const {
203 if (context->uniqueID() != fContext->uniqueID()) {
204 SkASSERT(0);
205 return nullptr;
206 }
207
208 GrTextureAdjuster adjuster(fContext.get(), this->asTextureProxyRef(), fAlphaType,
209 this->uniqueID(), fColorSpace.get());
Brian Osman6064e1c2018-10-19 14:27:54 -0400210 return adjuster.refTextureProxyForParams(params, scaleAdjust);
Jim Van Verth8026ccc2018-10-04 13:10:39 -0400211}
212
213GrBackendTexture SkImage_GpuBase::onGetBackendTexture(bool flushPendingGrContextIO,
214 GrSurfaceOrigin* origin) const {
215 sk_sp<GrTextureProxy> proxy = this->asTextureProxyRef();
216 SkASSERT(proxy);
217
218 if (!fContext->contextPriv().resourceProvider() && !proxy->isInstantiated()) {
219 // This image was created with a DDL context and cannot be instantiated.
220 return GrBackendTexture();
221}
222
223 if (!proxy->instantiate(fContext->contextPriv().resourceProvider())) {
224 return GrBackendTexture(); // invalid
225 }
226
227 GrTexture* texture = proxy->peekTexture();
228
229 if (texture) {
230 if (flushPendingGrContextIO) {
231 fContext->contextPriv().prepareSurfaceForExternalIO(proxy.get());
232 }
233 if (origin) {
234 *origin = proxy->origin();
235 }
236 return texture->getBackendTexture();
237 }
238 return GrBackendTexture(); // invalid
239}
240
241GrTexture* SkImage_GpuBase::onGetTexture() const {
242 GrTextureProxy* proxy = this->peekProxy();
243 if (!proxy) {
244 return nullptr;
245 }
246
247 sk_sp<GrTextureProxy> proxyRef = this->asTextureProxyRef();
248 if (!fContext->contextPriv().resourceProvider() && !proxyRef->isInstantiated()) {
249 // This image was created with a DDL context and cannot be instantiated.
250 return nullptr;
251 }
252
253 if (!proxy->instantiate(fContext->contextPriv().resourceProvider())) {
254 return nullptr;
255 }
256
257 return proxy->peekTexture();
258}
259
Jim Van Verth8026ccc2018-10-04 13:10:39 -0400260bool SkImage_GpuBase::onIsValid(GrContext* context) const {
261 // The base class has already checked that context isn't abandoned (if it's not nullptr)
262 if (fContext->abandoned()) {
263 return false;
264 }
265
266 if (context && context != fContext.get()) {
267 return false;
268 }
269
270 return true;
271}
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400272
Jim Van Verth0e671942018-11-09 12:03:57 -0500273bool SkImage_GpuBase::MakeTempTextureProxies(GrContext* ctx, const GrBackendTexture yuvaTextures[],
Jim Van Verth53275362018-11-09 15:42:35 -0500274 int numTextures, const SkYUVAIndex yuvaIndices[4],
275 GrSurfaceOrigin imageOrigin,
Jim Van Verth0e671942018-11-09 12:03:57 -0500276 sk_sp<GrTextureProxy> tempTextureProxies[4]) {
277 GrProxyProvider* proxyProvider = ctx->contextPriv().proxyProvider();
278
279 // We need to make a copy of the input backend textures because we need to preserve the result
280 // of validate_backend_texture.
281 GrBackendTexture yuvaTexturesCopy[4];
282 for (int textureIndex = 0; textureIndex < numTextures; ++textureIndex) {
283 yuvaTexturesCopy[textureIndex] = yuvaTextures[textureIndex];
Brian Salomonf391d0f2018-12-14 09:18:50 -0500284 GrBackendFormat backendFormat = yuvaTexturesCopy[textureIndex].getBackendFormat();
285 if (!backendFormat.isValid()) {
286 return false;
287 }
288 yuvaTexturesCopy[textureIndex].fConfig =
289 ctx->contextPriv().caps()->getYUVAConfigFromBackendFormat(backendFormat);
290 if (yuvaTexturesCopy[textureIndex].fConfig == kUnknown_GrPixelConfig) {
Jim Van Verth0e671942018-11-09 12:03:57 -0500291 return false;
292 }
293 SkASSERT(yuvaTexturesCopy[textureIndex].isValid());
294
Brian Salomonaa6ca0a2019-01-24 16:03:07 -0500295 tempTextureProxies[textureIndex] = proxyProvider->wrapBackendTexture(
296 yuvaTexturesCopy[textureIndex], imageOrigin, kBorrow_GrWrapOwnership,
297 GrWrapCacheable::kNo, kRead_GrIOType);
Jim Van Verth0e671942018-11-09 12:03:57 -0500298 if (!tempTextureProxies[textureIndex]) {
299 return false;
300 }
Jim Van Verth53275362018-11-09 15:42:35 -0500301
302 // Check that each texture contains the channel data for the corresponding YUVA index
303 GrPixelConfig config = yuvaTexturesCopy[textureIndex].fConfig;
304 for (int yuvaIndex = 0; yuvaIndex < SkYUVAIndex::kIndexCount; ++yuvaIndex) {
305 if (yuvaIndices[yuvaIndex].fIndex == textureIndex) {
306 switch (yuvaIndices[yuvaIndex].fChannel) {
307 case SkColorChannel::kR:
308 if (kAlpha_8_as_Alpha_GrPixelConfig == config) {
309 return false;
310 }
311 break;
312 case SkColorChannel::kG:
313 case SkColorChannel::kB:
314 if (kAlpha_8_as_Alpha_GrPixelConfig == config ||
315 kAlpha_8_as_Red_GrPixelConfig == config) {
316 return false;
317 }
318 break;
319 case SkColorChannel::kA:
320 default:
321 if (kRGB_888_GrPixelConfig == config) {
322 return false;
323 }
324 break;
325 }
326 }
327 }
Jim Van Verth0e671942018-11-09 12:03:57 -0500328 }
329
330 return true;
331}
332
333bool SkImage_GpuBase::RenderYUVAToRGBA(GrContext* ctx, GrRenderTargetContext* renderTargetContext,
334 const SkRect& rect, SkYUVColorSpace yuvColorSpace,
335 const sk_sp<GrTextureProxy> proxies[4],
336 const SkYUVAIndex yuvaIndices[4]) {
337 SkASSERT(renderTargetContext);
338 if (!renderTargetContext->asSurfaceProxy()) {
339 return false;
340 }
341
342 GrPaint paint;
343 paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
344
Jim Van Verth0e671942018-11-09 12:03:57 -0500345 paint.addColorFragmentProcessor(GrYUVtoRGBEffect::Make(proxies, yuvaIndices,
346 yuvColorSpace,
347 GrSamplerState::Filter::kNearest));
348
349 renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
350
351 // DDL TODO: in the promise image version we must not flush here
352 ctx->contextPriv().flushSurfaceWrites(renderTargetContext->asSurfaceProxy());
353
354 return true;
355}
356
Brian Salomonbe5a0932018-12-10 10:03:26 -0500357sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
358 GrContext* context, int width, int height, GrSurfaceOrigin origin, GrPixelConfig config,
359 GrBackendFormat backendFormat, GrMipMapped mipMapped,
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500360 PromiseImageTextureFulfillProc fulfillProc,
361 PromiseImageTextureReleaseProc releaseProc,
362 PromiseImageTextureDoneProc doneProc,
Brian Salomonf55e8d52019-01-30 17:28:20 -0500363 PromiseImageTextureContext textureContext,
364 DelayReleaseCallback delayReleaseCallback) {
Brian Salomonbe5a0932018-12-10 10:03:26 -0500365 SkASSERT(context);
366 SkASSERT(width > 0 && height > 0);
367 SkASSERT(doneProc);
Brian Salomonf391d0f2018-12-14 09:18:50 -0500368 SkASSERT(config != kUnknown_GrPixelConfig);
Brian Salomonbe5a0932018-12-10 10:03:26 -0500369
370 if (!fulfillProc || !releaseProc) {
371 doneProc(textureContext);
372 return nullptr;
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400373 }
374
Brian Salomonbe5a0932018-12-10 10:03:26 -0500375 if (mipMapped == GrMipMapped::kYes &&
376 GrTextureTypeHasRestrictedSampling(backendFormat.textureType())) {
377 // It is invalid to have a GL_TEXTURE_EXTERNAL or GL_TEXTURE_RECTANGLE and have mips as
378 // well.
379 doneProc(textureContext);
380 return nullptr;
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400381 }
Brian Salomonbe5a0932018-12-10 10:03:26 -0500382
383 /**
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500384 * This class is the lazy instantiation callback for promise images. It manages calling the
385 * client's Fulfill, Release, and Done procs. It attempts to reuse a GrTexture instance in
386 * cases where the client provides the same SkPromiseImageTexture for successive Fulfill calls.
387 * The created GrTexture is given a key based on a unique ID associated with the
388 * SkPromiseImageTexture. When the texture enters "idle" state (meaning it is not being used by
389 * the GPU and is at rest in the resource cache) the client's Release proc is called
390 * using GrTexture's idle proc mechanism. If the same SkPromiseImageTexture is provided for
391 * another fulfill we find the cached GrTexture. If the proxy, and therefore this object,
392 * is destroyed, we invalidate the GrTexture's key. Also if the client overwrites or
393 * destroys their SkPromiseImageTexture we invalidate the key.
394 *
395 * Currently a GrTexture is only reused for a given SkPromiseImageTexture if the
396 * SkPromiseImageTexture is reused in Fulfill for the same promise SkImage. However, we'd
397 * like to relax that so that a SkPromiseImageTexture can be reused with different promise
398 * SkImages that will reuse a single GrTexture.
Brian Salomonbe5a0932018-12-10 10:03:26 -0500399 */
400 class PromiseLazyInstantiateCallback {
401 public:
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500402 PromiseLazyInstantiateCallback(PromiseImageTextureFulfillProc fulfillProc,
403 PromiseImageTextureReleaseProc releaseProc,
404 PromiseImageTextureDoneProc doneProc,
405 PromiseImageTextureContext context,
Brian Salomonf55e8d52019-01-30 17:28:20 -0500406 DelayReleaseCallback delayReleaseCallback,
Brian Salomonbe5a0932018-12-10 10:03:26 -0500407 GrPixelConfig config)
408 : fFulfillProc(fulfillProc)
Brian Salomonf55e8d52019-01-30 17:28:20 -0500409 , fConfig(config)
410 , fDelayReleaseCallback(delayReleaseCallback) {
Brian Salomon553610d2019-01-14 17:34:12 -0500411 auto doneHelper = sk_make_sp<GrReleaseProcHelper>(doneProc, context);
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500412 fReleaseContext = sk_make_sp<IdleContext::PromiseImageReleaseContext>(
413 releaseProc, context, std::move(doneHelper));
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500414 }
Brian Salomon553610d2019-01-14 17:34:12 -0500415
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500416 ~PromiseLazyInstantiateCallback() = default;
Brian Salomonbe5a0932018-12-10 10:03:26 -0500417
418 sk_sp<GrSurface> operator()(GrResourceProvider* resourceProvider) {
419 if (!resourceProvider) {
Brian Salomonf55e8d52019-01-30 17:28:20 -0500420 if (fDelayedReleaseTexture) {
421 fDelayedReleaseTexture.reset();
422 }
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500423 return nullptr;
424 }
Brian Salomonf55e8d52019-01-30 17:28:20 -0500425 if (fDelayedReleaseTexture) {
426 return fDelayedReleaseTexture;
427 }
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500428
429 sk_sp<GrTexture> cachedTexture;
430 SkASSERT(fLastFulfilledKey.isValid() == (fLastFulfillID > 0));
431 if (fLastFulfilledKey.isValid()) {
432 auto surf = resourceProvider->findByUniqueKey<GrSurface>(fLastFulfilledKey);
433 if (surf) {
434 cachedTexture = sk_ref_sp(surf->asTexture());
435 SkASSERT(cachedTexture);
436 }
437 }
438 // If the release callback hasn't been called already by releasing the GrTexture
439 // then we can be sure that won't happen so long as we have a ref to the texture.
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500440 if (cachedTexture && !fReleaseContext->isReleased()) {
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500441 return std::move(cachedTexture);
442 }
443 GrBackendTexture backendTexture;
Brian Salomon553610d2019-01-14 17:34:12 -0500444 sk_sp<SkPromiseImageTexture> promiseTexture =
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500445 fFulfillProc(fReleaseContext->textureContext());
446 fReleaseContext->notifyWasFulfilled();
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500447 if (!promiseTexture) {
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500448 fReleaseContext->release();
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500449 return sk_sp<GrTexture>();
450 }
451 bool same = promiseTexture->uniqueID() == fLastFulfillID;
452 SkASSERT(!same || fLastFulfilledKey.isValid());
453 if (same && cachedTexture) {
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500454 SkASSERT(fReleaseContext->unique());
455 this->addToIdleContext(cachedTexture.get());
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500456 return std::move(cachedTexture);
457 } else if (cachedTexture) {
458 cachedTexture->resourcePriv().removeUniqueKey();
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500459 }
460 fLastFulfillID = promiseTexture->uniqueID();
461
462 backendTexture = promiseTexture->backendTexture();
463 backendTexture.fConfig = fConfig;
464 if (!backendTexture.isValid()) {
465 // Even though the GrBackendTexture is not valid, we must call the release
466 // proc to keep our contract of always calling Fulfill and Release in pairs.
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500467 fReleaseContext->release();
Brian Salomon426ba462019-01-10 16:33:06 +0000468 return sk_sp<GrTexture>();
Brian Salomon559c6172019-01-10 10:23:44 -0500469 }
470
Brian Salomonf55e8d52019-01-30 17:28:20 -0500471 sk_sp<GrTexture> tex;
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500472 static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
473 GrUniqueKey::Builder builder(&fLastFulfilledKey, kDomain, 2, "promise");
474 builder[0] = promiseTexture->uniqueID();
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500475 builder[1] = fConfig;
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500476 builder.finish();
Brian Salomon0d606762019-01-25 09:58:38 -0500477 // A texture with this key may already exist from a different instance of this lazy
478 // callback. This could happen if the client fulfills a promise image with a texture
479 // that was previously used to fulfill a different promise image.
Brian Salomon0d606762019-01-25 09:58:38 -0500480 if (auto surf = resourceProvider->findByUniqueKey<GrSurface>(fLastFulfilledKey)) {
481 tex = sk_ref_sp(surf->asTexture());
482 SkASSERT(tex);
483 } else {
484 if ((tex = resourceProvider->wrapBackendTexture(
485 backendTexture, kBorrow_GrWrapOwnership, GrWrapCacheable::kYes,
486 kRead_GrIOType))) {
487 tex->resourcePriv().setUniqueKey(fLastFulfilledKey);
488 } else {
489 // Even though we failed to wrap the backend texture, we must call the release
490 // proc to keep our contract of always calling Fulfill and Release in pairs.
491 fReleaseContext->release();
492 return sk_sp<GrTexture>();
493 }
494 }
495 this->addToIdleContext(tex.get());
Brian Salomonf55e8d52019-01-30 17:28:20 -0500496 if (fDelayReleaseCallback == DelayReleaseCallback::kYes) {
497 fDelayedReleaseTexture = tex;
498 }
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500499 tex->resourcePriv().setUniqueKey(fLastFulfilledKey);
500 SkASSERT(fContextID == SK_InvalidUniqueID ||
501 fContextID == tex->getContext()->uniqueID());
502 fContextID = tex->getContext()->uniqueID();
503 promiseTexture->addKeyToInvalidate(fContextID, fLastFulfilledKey);
Brian Salomonbe5a0932018-12-10 10:03:26 -0500504 return std::move(tex);
505 }
506
507 private:
Brian Salomon553610d2019-01-14 17:34:12 -0500508 // The GrTexture's idle callback mechanism is used to call the client's Release proc via
509 // this class. This also owns a ref counted helper that calls the client's ReleaseProc when
510 // the ref count reaches zero. The callback and any Fulfilled but un-Released texture share
511 // ownership of the IdleContext. Thus, the IdleContext is destroyed and calls the Done proc
512 // after the last fulfilled texture goes idle and calls the Release proc or the proxy's
513 // destructor destroys the lazy callback, whichever comes last.
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500514 class IdleContext {
Brian Salomon553610d2019-01-14 17:34:12 -0500515 public:
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500516 class PromiseImageReleaseContext;
Brian Salomon553610d2019-01-14 17:34:12 -0500517
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500518 IdleContext() = default;
Brian Salomon553610d2019-01-14 17:34:12 -0500519
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500520 ~IdleContext() = default;
Brian Salomon553610d2019-01-14 17:34:12 -0500521
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500522 void addImageReleaseContext(sk_sp<PromiseImageReleaseContext> context) {
523 fReleaseContexts.addToHead(std::move(context));
Brian Salomon553610d2019-01-14 17:34:12 -0500524 }
525
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500526 static void IdleProc(void* context) {
527 IdleContext* idleContext = static_cast<IdleContext*>(context);
528 for (ReleaseContextList::Iter iter = idleContext->fReleaseContexts.headIter();
529 iter.get();
530 iter.next()) {
531 (*iter.get())->release();
532 }
533 idleContext->fReleaseContexts.reset();
534 delete idleContext;
535 }
536
537 class PromiseImageReleaseContext : public SkNVRefCnt<PromiseImageReleaseContext> {
538 public:
539 PromiseImageReleaseContext(PromiseImageTextureReleaseProc releaseProc,
540 PromiseImageTextureContext textureContext,
541 sk_sp<GrReleaseProcHelper> doneHelper)
542 : fReleaseProc(releaseProc)
543 , fTextureContext(textureContext)
544 , fDoneHelper(std::move(doneHelper)) {}
545
546 ~PromiseImageReleaseContext() { SkASSERT(fIsReleased); }
547
548 void release() {
549 SkASSERT(!fIsReleased);
550 fReleaseProc(fTextureContext);
551 fIsReleased = true;
552 }
553
554 void notifyWasFulfilled() { fIsReleased = false; }
555 bool isReleased() const { return fIsReleased; }
556
557 PromiseImageTextureContext textureContext() const { return fTextureContext; }
558
559 private:
560 PromiseImageTextureReleaseProc fReleaseProc;
561 PromiseImageTextureContext fTextureContext;
562 sk_sp<GrReleaseProcHelper> fDoneHelper;
563 bool fIsReleased = true;
564 };
565
Brian Salomon553610d2019-01-14 17:34:12 -0500566 private:
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500567 using ReleaseContextList = SkTLList<sk_sp<PromiseImageReleaseContext>, 4>;
568 ReleaseContextList fReleaseContexts;
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500569 };
Brian Salomonbe5a0932018-12-10 10:03:26 -0500570
Brian Salomon1bf0ed82019-01-16 13:51:35 -0500571 void addToIdleContext(GrTexture* texture) {
572 SkASSERT(!fReleaseContext->isReleased());
573 IdleContext* idleContext = static_cast<IdleContext*>(texture->idleContext());
574 if (!idleContext) {
575 idleContext = new IdleContext();
576 texture->setIdleProc(IdleContext::IdleProc, idleContext);
577 }
578 idleContext->addImageReleaseContext(fReleaseContext);
579 }
580
581 sk_sp<IdleContext::PromiseImageReleaseContext> fReleaseContext;
Brian Salomonf55e8d52019-01-30 17:28:20 -0500582 sk_sp<GrTexture> fDelayedReleaseTexture;
Brian Salomon553610d2019-01-14 17:34:12 -0500583 PromiseImageTextureFulfillProc fFulfillProc;
584 GrPixelConfig fConfig;
Brian Salomonf55e8d52019-01-30 17:28:20 -0500585 DelayReleaseCallback fDelayReleaseCallback;
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500586
587 // ID of the last SkPromiseImageTexture given to us by the client.
588 uint32_t fLastFulfillID = 0;
589 // ID of the GrContext that we are interacting with.
590 uint32_t fContextID = SK_InvalidUniqueID;
Brian Salomoncdd8a0a2019-01-10 12:09:52 -0500591 GrUniqueKey fLastFulfilledKey;
Brian Salomonf55e8d52019-01-30 17:28:20 -0500592 } callback(fulfillProc, releaseProc, doneProc, textureContext, delayReleaseCallback, config);
Brian Salomonbe5a0932018-12-10 10:03:26 -0500593
594 GrProxyProvider* proxyProvider = context->contextPriv().proxyProvider();
595
596 GrSurfaceDesc desc;
597 desc.fWidth = width;
598 desc.fHeight = height;
599 desc.fConfig = config;
600
601 // We pass kReadOnly here since we should treat content of the client's texture as immutable.
602 return proxyProvider->createLazyProxy(std::move(callback), backendFormat, desc, origin,
603 mipMapped, GrInternalSurfaceFlags::kReadOnly,
604 SkBackingFit::kExact, SkBudgeted::kNo,
605 GrSurfaceProxy::LazyInstantiationType::kDeinstantiate);
Jim Van Verth8bbce0e2018-10-08 14:34:52 -0400606}