• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
8 #include "include/core/SkPromiseImageTexture.h"
9 #include "include/gpu/GrBackendSurface.h"
10 #include "include/gpu/GrContext.h"
11 #include "include/gpu/GrTexture.h"
12 #include "include/private/GrRecordingContext.h"
13 #include "src/core/SkBitmapCache.h"
14 #include "src/core/SkTLList.h"
15 #include "src/gpu/GrClip.h"
16 #include "src/gpu/GrContextPriv.h"
17 #include "src/gpu/GrRecordingContextPriv.h"
18 #include "src/gpu/GrRenderTargetContext.h"
19 #include "src/gpu/GrTextureAdjuster.h"
20 #include "src/gpu/effects/GrYUVtoRGBEffect.h"
21 #include "src/image/SkImage_Gpu.h"
22 #include "src/image/SkImage_GpuBase.h"
23 #include "src/image/SkReadPixelsRec.h"
24 
SkImage_GpuBase(sk_sp<GrContext> context,int width,int height,uint32_t uniqueID,SkColorType ct,SkAlphaType at,sk_sp<SkColorSpace> cs)25 SkImage_GpuBase::SkImage_GpuBase(sk_sp<GrContext> context, int width, int height, uint32_t uniqueID,
26                                  SkColorType ct, SkAlphaType at, sk_sp<SkColorSpace> cs)
27         : INHERITED(SkImageInfo::Make(width, height, ct, at, std::move(cs)), uniqueID)
28         , fContext(std::move(context)) {}
29 
~SkImage_GpuBase()30 SkImage_GpuBase::~SkImage_GpuBase() {}
31 
32 //////////////////////////////////////////////////////////////////////////////////////////////////
33 
34 #if GR_TEST_UTILS
resetContext(sk_sp<GrContext> newContext)35 void SkImage_GpuBase::resetContext(sk_sp<GrContext> newContext) {
36     SkASSERT(fContext->priv().matches(newContext.get()));
37     fContext = newContext;
38 }
39 #endif
40 
ValidateBackendTexture(const GrCaps * caps,const GrBackendTexture & tex,GrColorType grCT,SkColorType ct,SkAlphaType at,sk_sp<SkColorSpace> cs)41 bool SkImage_GpuBase::ValidateBackendTexture(const GrCaps* caps, const GrBackendTexture& tex,
42                                              GrColorType grCT, SkColorType ct, SkAlphaType at,
43                                              sk_sp<SkColorSpace> cs) {
44     if (!tex.isValid()) {
45         return false;
46     }
47     // TODO: Create a SkImageColorInfo struct for color, alpha, and color space so we don't need to
48     // create a fake image info here.
49     SkImageInfo info = SkImageInfo::Make(1, 1, ct, at, cs);
50     if (!SkImageInfoIsValid(info)) {
51         return false;
52     }
53     GrBackendFormat backendFormat = tex.getBackendFormat();
54     if (!backendFormat.isValid()) {
55         return false;
56     }
57 
58     return caps->areColorTypeAndFormatCompatible(grCT, backendFormat);
59 }
60 
61 //////////////////////////////////////////////////////////////////////////////////////////////////
62 
getROPixels(SkBitmap * dst,CachingHint chint) const63 bool SkImage_GpuBase::getROPixels(SkBitmap* dst, CachingHint chint) const {
64     auto direct = fContext->priv().asDirectContext();
65     if (!direct) {
66         // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
67         return false;
68     }
69 
70     const auto desc = SkBitmapCacheDesc::Make(this);
71     if (SkBitmapCache::Find(desc, dst)) {
72         SkASSERT(dst->isImmutable());
73         SkASSERT(dst->getPixels());
74         return true;
75     }
76 
77     SkBitmapCache::RecPtr rec = nullptr;
78     SkPixmap pmap;
79     if (kAllow_CachingHint == chint) {
80         rec = SkBitmapCache::Alloc(desc, this->imageInfo(), &pmap);
81         if (!rec) {
82             return false;
83         }
84     } else {
85         if (!dst->tryAllocPixels(this->imageInfo()) || !dst->peekPixels(&pmap)) {
86             return false;
87         }
88     }
89 
90     sk_sp<GrTextureProxy> texProxy = this->asTextureProxyRef(direct);
91     GrColorType grColorType = SkColorTypeAndFormatToGrColorType(fContext->priv().caps(),
92                                                                 this->colorType(),
93                                                                 texProxy->backendFormat());
94 
95     sk_sp<GrSurfaceContext> sContext =
96             direct->priv().makeWrappedSurfaceContext(std::move(texProxy),
97                                                      grColorType,
98                                                      this->alphaType(),
99                                                      this->refColorSpace());
100     if (!sContext) {
101         return false;
102     }
103 
104     if (!sContext->readPixels(pmap.info(), pmap.writable_addr(), pmap.rowBytes(), {0, 0})) {
105         return false;
106     }
107 
108     if (rec) {
109         SkBitmapCache::Add(std::move(rec), dst);
110         this->notifyAddedToRasterCache();
111     }
112     return true;
113 }
114 
onMakeSubset(GrRecordingContext * context,const SkIRect & subset) const115 sk_sp<SkImage> SkImage_GpuBase::onMakeSubset(GrRecordingContext* context,
116                                              const SkIRect& subset) const {
117     if (!context || !fContext->priv().matches(context)) {
118         return nullptr;
119     }
120 
121     sk_sp<GrSurfaceProxy> proxy = this->asTextureProxyRef(context);
122 
123     sk_sp<GrTextureProxy> copyProxy = GrSurfaceProxy::Copy(
124             context, proxy.get(), GrMipMapped::kNo, subset, SkBackingFit::kExact,
125             proxy->isBudgeted());
126 
127     if (!copyProxy) {
128         return nullptr;
129     }
130 
131     // MDB: this call is okay bc we know 'sContext' was kExact
132     return sk_make_sp<SkImage_Gpu>(fContext, kNeedNewImageUniqueID, this->alphaType(),
133                                    std::move(copyProxy), this->refColorSpace());
134 }
135 
onReadPixels(const SkImageInfo & dstInfo,void * dstPixels,size_t dstRB,int srcX,int srcY,CachingHint) const136 bool SkImage_GpuBase::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRB,
137                                    int srcX, int srcY, CachingHint) const {
138     auto direct = fContext->priv().asDirectContext();
139     if (!direct) {
140         // DDL TODO: buffer up the readback so it occurs when the DDL is drawn?
141         return false;
142     }
143 
144     if (!SkImageInfoValidConversion(dstInfo, this->imageInfo())) {
145         return false;
146     }
147 
148     sk_sp<GrTextureProxy> texProxy = this->asTextureProxyRef(direct);
149     GrColorType grColorType = SkColorTypeAndFormatToGrColorType(fContext->priv().caps(),
150                                                                 this->colorType(),
151                                                                 texProxy->backendFormat());
152 
153     sk_sp<GrSurfaceContext> sContext = direct->priv().makeWrappedSurfaceContext(
154             std::move(texProxy), grColorType, this->alphaType(), this->refColorSpace());
155     if (!sContext) {
156         return false;
157     }
158 
159     return sContext->readPixels(dstInfo, dstPixels, dstRB, {srcX, srcY});
160 }
161 
asTextureProxyRef(GrRecordingContext * context,const GrSamplerState & params,SkScalar scaleAdjust[2]) const162 sk_sp<GrTextureProxy> SkImage_GpuBase::asTextureProxyRef(GrRecordingContext* context,
163                                                          const GrSamplerState& params,
164                                                          SkScalar scaleAdjust[2]) const {
165     if (!context || !fContext->priv().matches(context)) {
166         SkASSERT(0);
167         return nullptr;
168     }
169 
170     GrTextureAdjuster adjuster(fContext.get(), this->asTextureProxyRef(context),
171                                SkColorTypeToGrColorType(this->colorType()), this->alphaType(),
172                                this->uniqueID(), this->colorSpace());
173     return adjuster.refTextureProxyForParams(params, scaleAdjust);
174 }
175 
onGetBackendTexture(bool flushPendingGrContextIO,GrSurfaceOrigin * origin) const176 GrBackendTexture SkImage_GpuBase::onGetBackendTexture(bool flushPendingGrContextIO,
177                                                       GrSurfaceOrigin* origin) const {
178     auto direct = fContext->priv().asDirectContext();
179     if (!direct) {
180         // This image was created with a DDL context and cannot be instantiated.
181         return GrBackendTexture(); // invalid
182     }
183 
184     sk_sp<GrTextureProxy> proxy = this->asTextureProxyRef(direct);
185     SkASSERT(proxy);
186 
187     if (!proxy->isInstantiated()) {
188         auto resourceProvider = direct->priv().resourceProvider();
189 
190         if (!proxy->instantiate(resourceProvider)) {
191             return GrBackendTexture(); // invalid
192         }
193     }
194 
195     GrTexture* texture = proxy->peekTexture();
196     if (texture) {
197         if (flushPendingGrContextIO) {
198             direct->priv().flushSurface(proxy.get());
199         }
200         if (origin) {
201             *origin = proxy->origin();
202         }
203         return texture->getBackendTexture();
204     }
205     return GrBackendTexture(); // invalid
206 }
207 
onGetTexture() const208 GrTexture* SkImage_GpuBase::onGetTexture() const {
209     GrTextureProxy* proxy = this->peekProxy();
210     if (proxy && proxy->isInstantiated()) {
211         return proxy->peekTexture();
212     }
213 
214     auto direct = fContext->priv().asDirectContext();
215     if (!direct) {
216         // This image was created with a DDL context and cannot be instantiated.
217         return nullptr;
218     }
219 
220     sk_sp<GrTextureProxy> proxyRef = this->asTextureProxyRef(direct);
221     SkASSERT(proxyRef && !proxyRef->isInstantiated());
222 
223     if (!proxyRef->instantiate(direct->priv().resourceProvider())) {
224         return nullptr;
225     }
226 
227     return proxyRef->peekTexture();
228 }
229 
onIsValid(GrContext * context) const230 bool SkImage_GpuBase::onIsValid(GrContext* context) const {
231     // The base class has already checked that context isn't abandoned (if it's not nullptr)
232     if (fContext->priv().abandoned()) {
233         return false;
234     }
235 
236     if (context && !fContext->priv().matches(context)) {
237         return false;
238     }
239 
240     return true;
241 }
242 
MakeTempTextureProxies(GrContext * ctx,const GrBackendTexture yuvaTextures[],int numTextures,const SkYUVAIndex yuvaIndices[4],GrSurfaceOrigin imageOrigin,sk_sp<GrTextureProxy> tempTextureProxies[4])243 bool SkImage_GpuBase::MakeTempTextureProxies(GrContext* ctx, const GrBackendTexture yuvaTextures[],
244                                              int numTextures, const SkYUVAIndex yuvaIndices[4],
245                                              GrSurfaceOrigin imageOrigin,
246                                              sk_sp<GrTextureProxy> tempTextureProxies[4]) {
247     GrProxyProvider* proxyProvider = ctx->priv().proxyProvider();
248     const GrCaps* caps = ctx->priv().caps();
249 
250     for (int textureIndex = 0; textureIndex < numTextures; ++textureIndex) {
251         GrBackendFormat backendFormat = yuvaTextures[textureIndex].getBackendFormat();
252         if (!backendFormat.isValid()) {
253             return false;
254         }
255 
256         GrColorType grColorType = caps->getYUVAColorTypeFromBackendFormat(
257                                                         backendFormat,
258                                                         yuvaIndices[3].fIndex == textureIndex);
259         if (GrColorType::kUnknown == grColorType) {
260             return false;
261         }
262 
263         SkASSERT(yuvaTextures[textureIndex].isValid());
264 
265         tempTextureProxies[textureIndex] = proxyProvider->wrapBackendTexture(
266                 yuvaTextures[textureIndex], grColorType, imageOrigin, kBorrow_GrWrapOwnership,
267                 GrWrapCacheable::kNo, kRead_GrIOType);
268         if (!tempTextureProxies[textureIndex]) {
269             return false;
270         }
271 
272         // Check that each texture contains the channel data for the corresponding YUVA index
273         auto componentFlags = GrColorTypeComponentFlags(grColorType);
274         for (int yuvaIndex = 0; yuvaIndex < SkYUVAIndex::kIndexCount; ++yuvaIndex) {
275             if (yuvaIndices[yuvaIndex].fIndex == textureIndex) {
276                 switch (yuvaIndices[yuvaIndex].fChannel) {
277                     case SkColorChannel::kR:
278                           // TODO: Chrome needs to be patched before this can be
279                           // enforced.
280 //                        if (!(kRed_SkColorTypeComponentFlag & componentFlags)) {
281 //                            return false;
282 //                        }
283                         break;
284                     case SkColorChannel::kG:
285                         if (!(kGreen_SkColorTypeComponentFlag & componentFlags)) {
286                             return false;
287                         }
288                         break;
289                     case SkColorChannel::kB:
290                         if (!(kBlue_SkColorTypeComponentFlag & componentFlags)) {
291                             return false;
292                         }
293                         break;
294                     case SkColorChannel::kA:
295                         if (!(kAlpha_SkColorTypeComponentFlag & componentFlags)) {
296                             return false;
297                         }
298                         break;
299                 }
300             }
301         }
302     }
303 
304     return true;
305 }
306 
RenderYUVAToRGBA(GrContext * ctx,GrRenderTargetContext * renderTargetContext,const SkRect & rect,SkYUVColorSpace yuvColorSpace,sk_sp<GrColorSpaceXform> colorSpaceXform,const sk_sp<GrTextureProxy> proxies[4],const SkYUVAIndex yuvaIndices[4])307 bool SkImage_GpuBase::RenderYUVAToRGBA(GrContext* ctx, GrRenderTargetContext* renderTargetContext,
308                                        const SkRect& rect, SkYUVColorSpace yuvColorSpace,
309                                        sk_sp<GrColorSpaceXform> colorSpaceXform,
310                                        const sk_sp<GrTextureProxy> proxies[4],
311                                        const SkYUVAIndex yuvaIndices[4]) {
312     SkASSERT(renderTargetContext);
313     if (!renderTargetContext->asSurfaceProxy()) {
314         return false;
315     }
316 
317     GrPaint paint;
318     paint.setPorterDuffXPFactory(SkBlendMode::kSrc);
319 
320     auto fp = GrYUVtoRGBEffect::Make(proxies, yuvaIndices, yuvColorSpace,
321                                      GrSamplerState::Filter::kNearest);
322     if (colorSpaceXform) {
323         fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(colorSpaceXform));
324     }
325     paint.addColorFragmentProcessor(std::move(fp));
326 
327     renderTargetContext->drawRect(GrNoClip(), std::move(paint), GrAA::kNo, SkMatrix::I(), rect);
328     return true;
329 }
330 
MakePromiseImageLazyProxy(GrContext * context,int width,int height,GrSurfaceOrigin origin,GrColorType colorType,GrBackendFormat backendFormat,GrMipMapped mipMapped,PromiseImageTextureFulfillProc fulfillProc,PromiseImageTextureReleaseProc releaseProc,PromiseImageTextureDoneProc doneProc,PromiseImageTextureContext textureContext,PromiseImageApiVersion version)331 sk_sp<GrTextureProxy> SkImage_GpuBase::MakePromiseImageLazyProxy(
332         GrContext* context, int width, int height, GrSurfaceOrigin origin, GrColorType colorType,
333         GrBackendFormat backendFormat, GrMipMapped mipMapped,
334         PromiseImageTextureFulfillProc fulfillProc,
335         PromiseImageTextureReleaseProc releaseProc,
336         PromiseImageTextureDoneProc doneProc,
337         PromiseImageTextureContext textureContext,
338         PromiseImageApiVersion version) {
339     SkASSERT(context);
340     SkASSERT(width > 0 && height > 0);
341     SkASSERT(doneProc);
342     SkASSERT(colorType != GrColorType::kUnknown);
343 
344     if (!fulfillProc || !releaseProc) {
345         doneProc(textureContext);
346         return nullptr;
347     }
348 
349     if (mipMapped == GrMipMapped::kYes &&
350         GrTextureTypeHasRestrictedSampling(backendFormat.textureType())) {
351         // It is invalid to have a GL_TEXTURE_EXTERNAL or GL_TEXTURE_RECTANGLE and have mips as
352         // well.
353         doneProc(textureContext);
354         return nullptr;
355     }
356 
357     /**
358      * This class is the lazy instantiation callback for promise images. It manages calling the
359      * client's Fulfill, Release, and Done procs. It attempts to reuse a GrTexture instance in
360      * cases where the client provides the same SkPromiseImageTexture as Fulfill results for
361      * multiple SkImages. The created GrTexture is given a key based on a unique ID associated with
362      * the SkPromiseImageTexture.
363      *
364      * The GrTexutre idle proc mechanism is used to call the Release and Done procs. We use this
365      * instead of the GrSurface release proc because the GrTexture is cached and therefore may
366      * outlive the proxy into which this callback is installed.
367      *
368      * A key invalidation message is installed on the SkPromiseImageTexture so that the GrTexture
369      * is deleted once it can no longer be used to instantiate a proxy.
370      */
371     class PromiseLazyInstantiateCallback {
372     public:
373         PromiseLazyInstantiateCallback(PromiseImageTextureFulfillProc fulfillProc,
374                                        PromiseImageTextureReleaseProc releaseProc,
375                                        PromiseImageTextureDoneProc doneProc,
376                                        PromiseImageTextureContext context,
377                                        GrColorType colorType,
378                                        PromiseImageApiVersion version)
379                 : fFulfillProc(fulfillProc)
380                 , fReleaseProc(releaseProc)
381                 , fColorType(colorType)
382                 , fVersion(version) {
383             fDoneCallback = sk_make_sp<GrRefCntedCallback>(doneProc, context);
384         }
385         PromiseLazyInstantiateCallback(PromiseLazyInstantiateCallback&&) = default;
386         PromiseLazyInstantiateCallback(const PromiseLazyInstantiateCallback&) {
387             // Because we get wrapped in std::function we must be copyable. But we should never
388             // be copied.
389             SkASSERT(false);
390         }
391         PromiseLazyInstantiateCallback& operator=(PromiseLazyInstantiateCallback&&) = default;
392         PromiseLazyInstantiateCallback& operator=(const PromiseLazyInstantiateCallback&) {
393             SkASSERT(false);
394             return *this;
395         }
396 
397         ~PromiseLazyInstantiateCallback() {
398             // Our destructor can run on any thread. We trigger the unref of fTexture by message.
399             if (fTexture) {
400                 SkMessageBus<GrGpuResourceFreedMessage>::Post({fTexture, fTextureContextID});
401             }
402         }
403 
404         GrSurfaceProxy::LazyInstantiationResult operator()(GrResourceProvider* resourceProvider) {
405             // We use the unique key in a way that is unrelated to the SkImage-based key that the
406             // proxy may receive, hence kUnsynced.
407             static constexpr auto kKeySyncMode =
408                     GrSurfaceProxy::LazyInstantiationKeyMode::kUnsynced;
409 
410             // Our proxy is getting instantiated for the second+ time. We are only allowed to call
411             // Fulfill once. So return our cached result.
412             if (fTexture) {
413                 return {sk_ref_sp(fTexture), kKeySyncMode};
414             } else if (fColorType == GrColorType::kUnknown) {
415                 // We've already called fulfill and it failed. Our contract says that we should only
416                 // call each callback once.
417                 return {};
418             }
419             SkASSERT(fDoneCallback);
420             PromiseImageTextureContext textureContext = fDoneCallback->context();
421             sk_sp<SkPromiseImageTexture> promiseTexture = fFulfillProc(textureContext);
422             // From here on out our contract is that the release proc must be called, even if
423             // the return from fulfill was invalid or we fail for some other reason.
424             auto releaseCallback = sk_make_sp<GrRefCntedCallback>(fReleaseProc, textureContext);
425             if (!promiseTexture) {
426                 // This records that we have failed.
427                 fColorType = GrColorType::kUnknown;
428                 return {};
429             }
430 
431             const GrBackendTexture& backendTexture = promiseTexture->backendTexture();
432             if (!backendTexture.isValid()) {
433                 return {};
434             }
435 
436             sk_sp<GrTexture> tex;
437             static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
438             GrUniqueKey key;
439             GrUniqueKey::Builder builder(&key, kDomain, 2, "promise");
440             builder[0] = promiseTexture->uniqueID();
441             builder[1] = (uint32_t) fColorType;
442             builder.finish();
443             // A texture with this key may already exist from a different instance of this lazy
444             // callback. This could happen if the client fulfills a promise image with a texture
445             // that was previously used to fulfill a different promise image.
446             if (auto surf = resourceProvider->findByUniqueKey<GrSurface>(key)) {
447                 tex = sk_ref_sp(surf->asTexture());
448                 SkASSERT(tex);
449             } else {
450                 if ((tex = resourceProvider->wrapBackendTexture(
451                              backendTexture, fColorType, kBorrow_GrWrapOwnership,
452                              GrWrapCacheable::kYes, kRead_GrIOType))) {
453                     tex->resourcePriv().setUniqueKey(key);
454                 } else {
455                     return {};
456                 }
457             }
458             auto releaseIdleState = fVersion == PromiseImageApiVersion::kLegacy
459                                             ? GrTexture::IdleState::kFinished
460                                             : GrTexture::IdleState::kFlushed;
461             tex->addIdleProc(std::move(releaseCallback), releaseIdleState);
462             tex->addIdleProc(std::move(fDoneCallback), GrTexture::IdleState::kFinished);
463             promiseTexture->addKeyToInvalidate(tex->getContext()->priv().contextID(), key);
464             fTexture = tex.get();
465             // We need to hold on to the GrTexture in case our proxy gets reinstantiated. However,
466             // we can't unref in our destructor because we may be on another thread then. So we
467             // let the cache know it is waiting on an unref message. We will send that message from
468             // our destructor.
469             GrContext* context = fTexture->getContext();
470             context->priv().getResourceCache()->insertDelayedResourceUnref(fTexture);
471             fTextureContextID = context->priv().contextID();
472             return {std::move(tex), kKeySyncMode};
473         }
474 
475     private:
476         PromiseImageTextureFulfillProc fFulfillProc;
477         PromiseImageTextureReleaseProc fReleaseProc;
478         sk_sp<GrRefCntedCallback> fDoneCallback;
479         GrTexture* fTexture = nullptr;
480         uint32_t fTextureContextID = SK_InvalidUniqueID;
481         GrColorType fColorType;
482         PromiseImageApiVersion fVersion;
483     } callback(fulfillProc, releaseProc, doneProc, textureContext, colorType, version);
484 
485     GrProxyProvider* proxyProvider = context->priv().proxyProvider();
486 
487     GrPixelConfig config = context->priv().caps()->getConfigFromBackendFormat(
488                                                                      backendFormat,
489                                                                      colorType);
490 
491     GrSurfaceDesc desc;
492     desc.fWidth = width;
493     desc.fHeight = height;
494     desc.fConfig = config;
495 
496     // Ganesh assumes that, when wrapping a mipmapped backend texture from a client, that its
497     // mipmaps are fully fleshed out.
498     GrMipMapsStatus mipMapsStatus = (GrMipMapped::kYes == mipMapped)
499             ? GrMipMapsStatus::kValid : GrMipMapsStatus::kNotAllocated;
500 
501     // We pass kReadOnly here since we should treat content of the client's texture as immutable.
502     // The promise API provides no way for the client to indicated that the texture is protected.
503     return proxyProvider->createLazyProxy(
504             std::move(callback), backendFormat, desc, GrRenderable::kNo, 1, origin, mipMapped,
505             mipMapsStatus, GrInternalSurfaceFlags::kReadOnly, SkBackingFit::kExact, SkBudgeted::kNo,
506             GrProtected::kNo, GrSurfaceProxy::LazyInstantiationType::kDeinstantiate);
507 }
508