xref: /aosp_15_r20/external/skia/src/gpu/ganesh/image/SkImage_GaneshBase.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 "src/gpu/ganesh/image/SkImage_GaneshBase.h"
9 
10 #include "include/core/SkAlphaType.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkColorType.h"
14 #include "include/core/SkImage.h"
15 #include "include/core/SkImageInfo.h"
16 #include "include/core/SkPixmap.h"
17 #include "include/core/SkRect.h"
18 #include "include/core/SkSize.h"
19 #include "include/core/SkSurface.h"
20 #include "include/core/SkTypes.h"
21 #include "include/gpu/GpuTypes.h"
22 #include "include/gpu/ganesh/GrBackendSurface.h"
23 #include "include/gpu/ganesh/GrDirectContext.h"
24 #include "include/gpu/ganesh/GrRecordingContext.h"
25 #include "include/gpu/ganesh/GrTypes.h"
26 #include "include/gpu/ganesh/SkImageGanesh.h"
27 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
28 #include "include/private/chromium/GrPromiseImageTexture.h"
29 #include "include/private/chromium/SkImageChromium.h"
30 #include "include/private/gpu/ganesh/GrTypesPriv.h"
31 #include "src/core/SkBitmapCache.h"
32 #include "src/core/SkColorSpacePriv.h"
33 #include "src/core/SkImageFilterTypes.h"
34 #include "src/core/SkImageFilter_Base.h"
35 #include "src/core/SkImageInfoPriv.h"
36 #include "src/gpu/RefCntedCallback.h"
37 #include "src/gpu/SkBackingFit.h"
38 #include "src/gpu/ganesh/GrCaps.h"
39 #include "src/gpu/ganesh/GrColorInfo.h"
40 #include "src/gpu/ganesh/GrDirectContextPriv.h"
41 #include "src/gpu/ganesh/GrImageContextPriv.h"
42 #include "src/gpu/ganesh/GrProxyProvider.h"
43 #include "src/gpu/ganesh/GrResourceCache.h"
44 #include "src/gpu/ganesh/GrResourceProvider.h"
45 #include "src/gpu/ganesh/GrSurface.h"
46 #include "src/gpu/ganesh/GrSurfaceProxy.h"
47 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
48 #include "src/gpu/ganesh/GrTexture.h"
49 #include "src/gpu/ganesh/GrTextureProxy.h"
50 #include "src/gpu/ganesh/SurfaceContext.h"
51 #include "src/gpu/ganesh/image/GrImageUtils.h"
52 #include "src/gpu/ganesh/image/SkImage_Ganesh.h"
53 #include "src/image/SkImage_Base.h"
54 
55 #include <functional>
56 #include <memory>
57 #include <utility>
58 
59 class GrContextThreadSafeProxy;
60 class SkImageFilter;
61 struct SkIPoint;
62 
SkImage_GaneshBase(sk_sp<GrImageContext> context,SkImageInfo info,uint32_t uniqueID)63 SkImage_GaneshBase::SkImage_GaneshBase(sk_sp<GrImageContext> context,
64                                        SkImageInfo info,
65                                        uint32_t uniqueID)
66         : SkImage_Base(std::move(info), uniqueID), fContext(std::move(context)) {}
67 
68 //////////////////////////////////////////////////////////////////////////////////////////////////
69 
ValidateBackendTexture(const GrCaps * caps,const GrBackendTexture & tex,GrColorType grCT,SkColorType ct,SkAlphaType at,sk_sp<SkColorSpace> cs)70 bool SkImage_GaneshBase::ValidateBackendTexture(const GrCaps* caps,
71                                                 const GrBackendTexture& tex,
72                                                 GrColorType grCT,
73                                                 SkColorType ct,
74                                                 SkAlphaType at,
75                                                 sk_sp<SkColorSpace> cs) {
76     if (!tex.isValid()) {
77         return false;
78     }
79     SkColorInfo info(ct, at, cs);
80     if (!SkColorInfoIsValid(info)) {
81         return false;
82     }
83     GrBackendFormat backendFormat = tex.getBackendFormat();
84     if (!backendFormat.isValid()) {
85         return false;
86     }
87 
88     return caps->areColorTypeAndFormatCompatible(grCT, backendFormat);
89 }
90 
ValidateCompressedBackendTexture(const GrCaps * caps,const GrBackendTexture & tex,SkAlphaType at)91 bool SkImage_GaneshBase::ValidateCompressedBackendTexture(const GrCaps* caps,
92                                                           const GrBackendTexture& tex,
93                                                           SkAlphaType at) {
94     if (!tex.isValid() || tex.width() <= 0 || tex.height() <= 0) {
95         return false;
96     }
97 
98     if (tex.width() > caps->maxTextureSize() || tex.height() > caps->maxTextureSize()) {
99         return false;
100     }
101 
102     if (at == kUnknown_SkAlphaType) {
103         return false;
104     }
105 
106     GrBackendFormat backendFormat = tex.getBackendFormat();
107     if (!backendFormat.isValid()) {
108         return false;
109     }
110 
111     if (!caps->isFormatCompressed(backendFormat)) {
112         return false;
113     }
114 
115     return true;
116 }
117 
118 //////////////////////////////////////////////////////////////////////////////////////////////////
119 
getROPixels(GrDirectContext * dContext,SkBitmap * dst,CachingHint chint) const120 bool SkImage_GaneshBase::getROPixels(GrDirectContext* dContext,
121                                      SkBitmap* dst,
122                                      CachingHint chint) const {
123     if (!fContext->priv().matches(dContext)) {
124         return false;
125     }
126 
127     const auto desc = SkBitmapCacheDesc::Make(this);
128     if (SkBitmapCache::Find(desc, dst)) {
129         SkASSERT(dst->isImmutable());
130         SkASSERT(dst->getPixels());
131         return true;
132     }
133 
134     SkBitmapCache::RecPtr rec = nullptr;
135     SkPixmap pmap;
136     if (kAllow_CachingHint == chint) {
137         rec = SkBitmapCache::Alloc(desc, this->imageInfo(), &pmap);
138         if (!rec) {
139             return false;
140         }
141     } else {
142         if (!dst->tryAllocPixels(this->imageInfo()) || !dst->peekPixels(&pmap)) {
143             return false;
144         }
145     }
146 
147     auto [view, ct] = skgpu::ganesh::AsView(dContext, this, skgpu::Mipmapped::kNo);
148     if (!view) {
149         return false;
150     }
151 
152     GrColorInfo colorInfo(ct, this->alphaType(), this->refColorSpace());
153     auto sContext = dContext->priv().makeSC(std::move(view), std::move(colorInfo));
154     if (!sContext) {
155         return false;
156     }
157 
158     if (!sContext->readPixels(dContext, pmap, {0, 0})) {
159         return false;
160     }
161 
162     if (rec) {
163         SkBitmapCache::Add(std::move(rec), dst);
164         this->notifyAddedToRasterCache();
165     }
166     return true;
167 }
168 
makeSubset(GrDirectContext * direct,const SkIRect & subset) const169 sk_sp<SkImage> SkImage_GaneshBase::makeSubset(GrDirectContext* direct,
170                                               const SkIRect& subset) const {
171     if (!fContext->priv().matches(direct)) {
172         return nullptr;
173     }
174 
175     if (subset.isEmpty()) {
176         return nullptr;
177     }
178 
179     const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
180     if (!bounds.contains(subset)) {
181         return nullptr;
182     }
183 
184     // optimization : return self if the subset == our bounds
185     if (bounds == subset) {
186         return sk_ref_sp(const_cast<SkImage_GaneshBase*>(this));
187     }
188 
189     return this->onMakeSubset(direct, subset);
190 }
191 
onMakeSubset(GrDirectContext * direct,const SkIRect & subset) const192 sk_sp<SkImage> SkImage_GaneshBase::onMakeSubset(GrDirectContext* direct,
193                                                 const SkIRect& subset) const {
194     if (!fContext->priv().matches(direct)) {
195         return nullptr;
196     }
197     auto [view, ct] = skgpu::ganesh::AsView(direct, this, skgpu::Mipmapped::kNo);
198     SkASSERT(view);
199     SkASSERT(ct == SkColorTypeToGrColorType(this->colorType()));
200 
201     skgpu::Budgeted isBudgeted = view.proxy()->isBudgeted();
202     auto copyView = GrSurfaceProxyView::Copy(direct,
203                                              std::move(view),
204                                              skgpu::Mipmapped::kNo,
205                                              subset,
206                                              SkBackingFit::kExact,
207                                              isBudgeted,
208                                              /*label=*/"ImageGpuBase_MakeSubset");
209 
210     if (!copyView) {
211         return nullptr;
212     }
213 
214     return sk_make_sp<SkImage_Ganesh>(sk_ref_sp(direct),
215                                       kNeedNewImageUniqueID,
216                                       std::move(copyView),
217                                       this->imageInfo().colorInfo());
218 }
219 
onMakeSubset(skgpu::graphite::Recorder *,const SkIRect &,RequiredProperties) const220 sk_sp<SkImage> SkImage_GaneshBase::onMakeSubset(skgpu::graphite::Recorder*,
221                                                 const SkIRect&,
222                                                 RequiredProperties) const {
223     SkDEBUGFAIL("Cannot convert Ganesh-backed image to Graphite");
224     return nullptr;
225 }
226 
makeColorTypeAndColorSpace(skgpu::graphite::Recorder *,SkColorType,sk_sp<SkColorSpace>,RequiredProperties) const227 sk_sp<SkImage> SkImage_GaneshBase::makeColorTypeAndColorSpace(skgpu::graphite::Recorder*,
228                                                               SkColorType,
229                                                               sk_sp<SkColorSpace>,
230                                                               RequiredProperties) const {
231     SkDEBUGFAIL("Cannot convert Ganesh-backed image to Graphite");
232     return nullptr;
233 }
234 
onMakeSurface(skgpu::graphite::Recorder *,const SkImageInfo & info) const235 sk_sp<SkSurface> SkImage_GaneshBase::onMakeSurface(skgpu::graphite::Recorder*,
236                                                    const SkImageInfo& info) const {
237     if (auto ictx = this->context()) {
238         if (auto rctx = ictx->priv().asRecordingContext()) {
239             auto isBudgeted = skgpu::Budgeted::kNo;  // Assuming we're a one-shot surface
240             return SkSurfaces::RenderTarget(rctx, isBudgeted, info);
241         }
242     }
243     return nullptr;
244 }
245 
onReadPixels(GrDirectContext * dContext,const SkImageInfo & dstInfo,void * dstPixels,size_t dstRB,int srcX,int srcY,CachingHint) const246 bool SkImage_GaneshBase::onReadPixels(GrDirectContext* dContext,
247                                       const SkImageInfo& dstInfo,
248                                       void* dstPixels,
249                                       size_t dstRB,
250                                       int srcX,
251                                       int srcY,
252                                       CachingHint) const {
253     if (!fContext->priv().matches(dContext) ||
254         !SkImageInfoValidConversion(dstInfo, this->imageInfo())) {
255         return false;
256     }
257 
258     auto [view, ct] = skgpu::ganesh::AsView(dContext, this, skgpu::Mipmapped::kNo);
259     SkASSERT(view);
260 
261     GrColorInfo colorInfo(ct, this->alphaType(), this->refColorSpace());
262     auto sContext = dContext->priv().makeSC(std::move(view), colorInfo);
263     if (!sContext) {
264         return false;
265     }
266 
267     return sContext->readPixels(dContext, {dstInfo, dstPixels, dstRB}, {srcX, srcY});
268 }
269 
isValid(GrRecordingContext * context) const270 bool SkImage_GaneshBase::isValid(GrRecordingContext* context) const {
271     if (context && context->abandoned()) {
272         return false;
273     }
274     if (fContext->priv().abandoned()) {
275         return false;
276     }
277     if (context && !fContext->priv().matches(context)) {
278         return false;
279     }
280     return true;
281 }
282 
makeColorTypeAndColorSpace(GrDirectContext * dContext,SkColorType targetColorType,sk_sp<SkColorSpace> targetCS) const283 sk_sp<SkImage> SkImage_GaneshBase::makeColorTypeAndColorSpace(GrDirectContext* dContext,
284                                                               SkColorType targetColorType,
285                                                               sk_sp<SkColorSpace> targetCS) const {
286     if (kUnknown_SkColorType == targetColorType || !targetCS) {
287         return nullptr;
288     }
289 
290     auto myContext = this->context();
291     // This check is also performed in the subclass, but we do it here for the short-circuit below.
292     if (!myContext || !myContext->priv().matches(dContext)) {
293         return nullptr;
294     }
295 
296     SkColorType colorType = this->colorType();
297     SkColorSpace* colorSpace = this->colorSpace();
298     if (!colorSpace) {
299         colorSpace = sk_srgb_singleton();
300     }
301     if (colorType == targetColorType &&
302         (SkColorSpace::Equals(colorSpace, targetCS.get()) || this->isAlphaOnly())) {
303         return sk_ref_sp(const_cast<SkImage_GaneshBase*>(this));
304     }
305 
306     return this->onMakeColorTypeAndColorSpace(targetColorType, std::move(targetCS), dContext);
307 }
308 
MakePromiseImageLazyProxy(GrContextThreadSafeProxy * tsp,SkISize dimensions,const GrBackendFormat & backendFormat,skgpu::Mipmapped mipmapped,SkImages::PromiseImageTextureFulfillProc fulfillProc,sk_sp<skgpu::RefCntedCallback> releaseHelper)309 sk_sp<GrTextureProxy> SkImage_GaneshBase::MakePromiseImageLazyProxy(
310         GrContextThreadSafeProxy* tsp,
311         SkISize dimensions,
312         const GrBackendFormat& backendFormat,
313         skgpu::Mipmapped mipmapped,
314         SkImages::PromiseImageTextureFulfillProc fulfillProc,
315         sk_sp<skgpu::RefCntedCallback> releaseHelper) {
316     SkASSERT(tsp);
317     SkASSERT(!dimensions.isEmpty());
318     SkASSERT(releaseHelper);
319 
320     if (!fulfillProc) {
321         return nullptr;
322     }
323 
324     if (mipmapped == skgpu::Mipmapped::kYes &&
325         GrTextureTypeHasRestrictedSampling(backendFormat.textureType())) {
326         // It is invalid to have a GL_TEXTURE_EXTERNAL or GL_TEXTURE_RECTANGLE and have mips as
327         // well.
328         return nullptr;
329     }
330 
331     /**
332      * This class is the lazy instantiation callback for promise images. It manages calling the
333      * client's Fulfill and Release procs. It attempts to reuse a GrTexture instance in
334      * cases where the client provides the same GrPromiseImageTexture as Fulfill results for
335      * multiple SkImages. The created GrTexture is given a key based on a unique ID associated with
336      * the GrPromiseImageTexture.
337      *
338      * A key invalidation message is installed on the GrPromiseImageTexture so that the GrTexture
339      * is deleted once it can no longer be used to instantiate a proxy.
340      */
341     class PromiseLazyInstantiateCallback {
342     public:
343         PromiseLazyInstantiateCallback(SkImages::PromiseImageTextureFulfillProc fulfillProc,
344                                        sk_sp<skgpu::RefCntedCallback> releaseHelper)
345                 : fFulfillProc(fulfillProc), fReleaseHelper(std::move(releaseHelper)) {}
346         PromiseLazyInstantiateCallback(PromiseLazyInstantiateCallback&&) = default;
347         PromiseLazyInstantiateCallback(const PromiseLazyInstantiateCallback&) {
348             // Because we get wrapped in std::function we must be copyable. But we should never
349             // be copied.
350             SkASSERT(false);
351         }
352         PromiseLazyInstantiateCallback& operator=(PromiseLazyInstantiateCallback&&) = default;
353         PromiseLazyInstantiateCallback& operator=(const PromiseLazyInstantiateCallback&) {
354             SkASSERT(false);
355             return *this;
356         }
357 
358         ~PromiseLazyInstantiateCallback() {
359             // Our destructor can run on any thread. We trigger the unref of fTexture by message.
360             if (fTexture) {
361                 GrResourceCache::ReturnResourceFromThread(std::move(fTexture), fTextureContextID);
362             }
363         }
364 
365         GrSurfaceProxy::LazyCallbackResult operator()(GrResourceProvider* resourceProvider,
366                                                       const GrSurfaceProxy::LazySurfaceDesc&) {
367             // We use the unique key in a way that is unrelated to the SkImage-based key that the
368             // proxy may receive, hence kUnsynced.
369             static constexpr auto kKeySyncMode =
370                     GrSurfaceProxy::LazyInstantiationKeyMode::kUnsynced;
371 
372             // In order to make the SkImage "thread safe" we rely on holding an extra ref to the
373             // texture in the callback and signalling the unref via a message to the resource cache.
374             // We need to extend the callback's lifetime to that of the proxy.
375             static constexpr auto kReleaseCallbackOnInstantiation = false;
376 
377             // Our proxy is getting instantiated for the second+ time. We are only allowed to call
378             // Fulfill once. So return our cached result.
379             if (fTexture) {
380                 return {fTexture, kReleaseCallbackOnInstantiation, kKeySyncMode};
381             } else if (fFulfillProcFailed) {
382                 // We've already called fulfill and it failed. Our contract says that we should only
383                 // call each callback once.
384                 return {};
385             }
386 
387             SkImages::PromiseImageTextureContext textureContext = fReleaseHelper->context();
388             sk_sp<GrPromiseImageTexture> promiseTexture = fFulfillProc(textureContext);
389 
390             if (!promiseTexture) {
391                 fFulfillProcFailed = true;
392                 return {};
393             }
394 
395             const GrBackendTexture& backendTexture = promiseTexture->backendTexture();
396             if (!backendTexture.isValid()) {
397                 return {};
398             }
399 
400             fTexture = resourceProvider->wrapBackendTexture(
401                     backendTexture, kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, kRead_GrIOType);
402             if (!fTexture) {
403                 return {};
404             }
405             fTexture->setRelease(fReleaseHelper);
406             auto dContext = fTexture->getContext();
407             fTextureContextID = dContext->directContextID();
408             return {fTexture, kReleaseCallbackOnInstantiation, kKeySyncMode};
409         }
410 
411     private:
412         SkImages::PromiseImageTextureFulfillProc fFulfillProc;
413         sk_sp<skgpu::RefCntedCallback> fReleaseHelper;
414         sk_sp<GrTexture> fTexture;
415         GrDirectContext::DirectContextID fTextureContextID;
416         bool fFulfillProcFailed = false;
417     } callback(fulfillProc, std::move(releaseHelper));
418 
419     return GrProxyProvider::CreatePromiseProxy(
420             tsp, std::move(callback), backendFormat, dimensions, mipmapped);
421 }
422 
423 namespace SkImages {
SubsetTextureFrom(GrDirectContext * context,const SkImage * img,const SkIRect & subset)424 sk_sp<SkImage> SubsetTextureFrom(GrDirectContext* context,
425                                  const SkImage* img,
426                                  const SkIRect& subset) {
427     if (context == nullptr || img == nullptr) {
428         return nullptr;
429     }
430     auto subsetImg = img->makeSubset(context, subset);
431     return SkImages::TextureFromImage(context, subsetImg.get());
432 }
433 
MakeWithFilter(GrRecordingContext * rContext,sk_sp<SkImage> src,const SkImageFilter * filter,const SkIRect & subset,const SkIRect & clipBounds,SkIRect * outSubset,SkIPoint * offset)434 sk_sp<SkImage> MakeWithFilter(GrRecordingContext* rContext,
435                               sk_sp<SkImage> src,
436                               const SkImageFilter* filter,
437                               const SkIRect& subset,
438                               const SkIRect& clipBounds,
439                               SkIRect* outSubset,
440                               SkIPoint* offset) {
441     if (!rContext || !src || !filter) {
442         return nullptr;
443     }
444 
445     GrSurfaceOrigin origin = kTopLeft_GrSurfaceOrigin;
446     if (as_IB(src)->isGaneshBacked()) {
447         SkImage_GaneshBase* base = static_cast<SkImage_GaneshBase*>(src.get());
448         origin = base->origin();
449     }
450 
451     sk_sp<skif::Backend> backend =
452             skif::MakeGaneshBackend(sk_ref_sp(rContext), origin, {}, src->colorType());
453     return as_IFB(filter)->makeImageWithFilter(std::move(backend),
454                                                std::move(src),
455                                                subset,
456                                                clipBounds,
457                                                outSubset,
458                                                offset);
459 }
460 
GetContext(const SkImage * src)461 GrDirectContext* GetContext(const SkImage* src) {
462     if (!src || !as_IB(src)->isGaneshBacked()) {
463         return nullptr;
464     }
465     return as_IB(src)->directContext();
466 }
467 
468 
469 } // namespace SkImages
470