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