• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Google LLC
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/GrImageUtils.h"
9 
10 #include "include/core/SkAlphaType.h"
11 #include "include/core/SkBitmap.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkImage.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkPixmap.h"
16 #include "include/core/SkPoint.h"
17 #include "include/core/SkRect.h"
18 #include "include/core/SkSamplingOptions.h"
19 #include "include/core/SkScalar.h"
20 #include "include/core/SkSize.h"
21 #include "include/core/SkSurface.h"
22 #include "include/core/SkTypes.h"
23 #include "include/core/SkYUVAInfo.h"
24 #include "include/core/SkYUVAPixmaps.h"
25 #include "include/gpu/GpuTypes.h"
26 #include "include/gpu/GrBackendSurface.h"
27 #include "include/gpu/GrContextOptions.h"
28 #include "include/gpu/GrRecordingContext.h"
29 #include "include/gpu/GrTypes.h"
30 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
31 #include "include/private/SkIDChangeListener.h"
32 #include "include/private/base/SkMutex.h"
33 #include "include/private/gpu/ganesh/GrImageContext.h"
34 #include "include/private/gpu/ganesh/GrTextureGenerator.h"
35 #include "include/private/gpu/ganesh/GrTypesPriv.h"
36 #include "src/core/SkBlurEngine.h"
37 #include "src/core/SkCachedData.h"
38 #include "src/core/SkImageFilterCache.h"
39 #include "src/core/SkImageFilterTypes.h"
40 #include "src/core/SkSamplingPriv.h"
41 #include "src/core/SkSpecialImage.h"
42 #include "src/gpu/ResourceKey.h"
43 #include "src/gpu/SkBackingFit.h"
44 #include "src/gpu/Swizzle.h"
45 #include "src/gpu/ganesh/Device.h"
46 #include "src/gpu/ganesh/GrBlurUtils.h"
47 #include "src/gpu/ganesh/GrCaps.h"
48 #include "src/gpu/ganesh/GrColorSpaceXform.h"
49 #include "src/gpu/ganesh/GrFragmentProcessor.h"
50 #include "src/gpu/ganesh/GrImageInfo.h"
51 #include "src/gpu/ganesh/GrProxyProvider.h"
52 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
53 #include "src/gpu/ganesh/GrSamplerState.h"
54 #include "src/gpu/ganesh/GrSurfaceProxy.h"
55 #include "src/gpu/ganesh/GrSurfaceProxyView.h"
56 #include "src/gpu/ganesh/GrTextureProxy.h"
57 #include "src/gpu/ganesh/GrThreadSafeCache.h"
58 #include "src/gpu/ganesh/GrYUVATextureProxies.h"
59 #include "src/gpu/ganesh/SkGr.h"
60 #include "src/gpu/ganesh/SurfaceDrawContext.h"
61 #include "src/gpu/ganesh/SurfaceFillContext.h"
62 #include "src/gpu/ganesh/effects/GrBicubicEffect.h"
63 #include "src/gpu/ganesh/effects/GrTextureEffect.h"
64 #include "src/gpu/ganesh/effects/GrYUVtoRGBEffect.h"
65 #include "src/gpu/ganesh/image/SkImage_Ganesh.h"
66 #include "src/gpu/ganesh/image/SkImage_GaneshBase.h"
67 #include "src/gpu/ganesh/image/SkImage_RasterPinnable.h"
68 #include "src/gpu/ganesh/image/SkSpecialImage_Ganesh.h"
69 #include "src/image/SkImage_Base.h"
70 #include "src/image/SkImage_Lazy.h"
71 #include "src/image/SkImage_Picture.h"
72 #include "src/image/SkImage_Raster.h"
73 
74 #include <string_view>
75 #include <utility>
76 
77 class SkDevice;
78 class SkMatrix;
79 class SkSurfaceProps;
80 enum SkColorType : int;
81 
82 namespace skgpu::ganesh {
83 
CopyView(GrRecordingContext * context,GrSurfaceProxyView src,skgpu::Mipmapped mipmapped,GrImageTexGenPolicy policy,std::string_view label)84 GrSurfaceProxyView CopyView(GrRecordingContext* context,
85                             GrSurfaceProxyView src,
86                             skgpu::Mipmapped mipmapped,
87                             GrImageTexGenPolicy policy,
88                             std::string_view label) {
89     skgpu::Budgeted budgeted = policy == GrImageTexGenPolicy::kNew_Uncached_Budgeted
90                                        ? skgpu::Budgeted::kYes
91                                        : skgpu::Budgeted::kNo;
92     return GrSurfaceProxyView::Copy(context,
93                                     std::move(src),
94                                     mipmapped,
95                                     SkBackingFit::kExact,
96                                     budgeted,
97                                     /*label=*/label);
98 }
99 
RasterAsView(GrRecordingContext * rContext,const SkImage_Raster * raster,skgpu::Mipmapped mipmapped,GrImageTexGenPolicy policy)100 std::tuple<GrSurfaceProxyView, GrColorType> RasterAsView(GrRecordingContext* rContext,
101                                                          const SkImage_Raster* raster,
102                                                          skgpu::Mipmapped mipmapped,
103                                                          GrImageTexGenPolicy policy) {
104     if (policy == GrImageTexGenPolicy::kDraw) {
105         // If the draw doesn't require mipmaps but this SkImage has them go ahead and make a
106         // mipmapped texture. There are three reasons for this:
107         // 1) Avoiding another texture creation if a later draw requires mipmaps.
108         // 2) Ensuring we upload the bitmap's levels instead of generating on the GPU from the base.
109         if (raster->hasMipmaps()) {
110             mipmapped = skgpu::Mipmapped::kYes;
111         }
112         return GrMakeCachedBitmapProxyView(rContext,
113                                            raster->bitmap(),
114                                            /*label=*/"TextureForImageRasterWithPolicyEqualKDraw",
115                                            mipmapped);
116     }
117     auto budgeted = (policy == GrImageTexGenPolicy::kNew_Uncached_Unbudgeted)
118                             ? skgpu::Budgeted::kNo
119                             : skgpu::Budgeted::kYes;
120     return GrMakeUncachedBitmapProxyView(
121             rContext, raster->bitmap(), mipmapped, SkBackingFit::kExact, budgeted);
122 }
123 
124 // Returns the GrColorType to use with the GrTextureProxy returned from lockTextureProxy. This
125 // may be different from the color type on the image in the case where we need up upload CPU
126 // data to a texture but the GPU doesn't support the format of CPU data. In this case we convert
127 // the data to RGBA_8888 unorm on the CPU then upload that.
ColorTypeOfLockTextureProxy(const GrCaps * caps,SkColorType sct)128 GrColorType ColorTypeOfLockTextureProxy(const GrCaps* caps, SkColorType sct) {
129     GrColorType ct = SkColorTypeToGrColorType(sct);
130     GrBackendFormat format = caps->getDefaultBackendFormat(ct, GrRenderable::kNo);
131     if (!format.isValid()) {
132         ct = GrColorType::kRGBA_8888;
133     }
134     return ct;
135 }
136 
get_origin(const SkImage_Lazy * img)137 static GrSurfaceOrigin get_origin(const SkImage_Lazy* img) {
138     SkASSERT(img->generator());
139     if (!img->generator()->isTextureGenerator()) {
140         return kTopLeft_GrSurfaceOrigin;
141     }
142     // origin should be thread safe
143     return static_cast<const GrTextureGenerator*>(img->generator()->fGenerator.get())->origin();
144 }
145 
146 
texture_proxy_view_from_planes(GrRecordingContext * ctx,const SkImage_Lazy * img,skgpu::Budgeted budgeted)147 static GrSurfaceProxyView texture_proxy_view_from_planes(GrRecordingContext* ctx,
148                                                          const SkImage_Lazy* img,
149                                                          skgpu::Budgeted budgeted) {
150     auto supportedDataTypes = SupportedTextureFormats(*ctx);
151     SkYUVAPixmaps yuvaPixmaps;
152     sk_sp<SkCachedData> dataStorage = img->getPlanes(supportedDataTypes, &yuvaPixmaps);
153     if (!dataStorage) {
154         return {};
155     }
156 
157     GrSurfaceProxyView views[SkYUVAInfo::kMaxPlanes];
158     GrColorType pixmapColorTypes[SkYUVAInfo::kMaxPlanes];
159     for (int i = 0; i < yuvaPixmaps.numPlanes(); ++i) {
160         // If the sizes of the components are not all the same we choose to create exact-match
161         // textures for the smaller ones rather than add a texture domain to the draw.
162         // TODO: revisit this decision to improve texture reuse?
163         SkBackingFit fit = yuvaPixmaps.plane(i).dimensions() == img->dimensions()
164                                    ? SkBackingFit::kApprox
165                                    : SkBackingFit::kExact;
166 
167         // We grab a ref to cached yuv data. When the SkBitmap we create below goes away it will
168         // call releaseProc which will release this ref.
169         // DDL TODO: Currently we end up creating a lazy proxy that will hold onto a ref to the
170         // SkImage in its lambda. This means that we'll keep the ref on the YUV data around for the
171         // life time of the proxy and not just upload. For non-DDL draws we should look into
172         // releasing this SkImage after uploads (by deleting the lambda after instantiation).
173         auto releaseProc = [](void*, void* data) {
174             auto cachedData = static_cast<SkCachedData*>(data);
175             SkASSERT(cachedData);
176             cachedData->unref();
177         };
178         SkBitmap bitmap;
179         bitmap.installPixels(yuvaPixmaps.plane(i).info(),
180                              yuvaPixmaps.plane(i).writable_addr(),
181                              yuvaPixmaps.plane(i).rowBytes(),
182                              releaseProc,
183                              SkRef(dataStorage.get()));
184         bitmap.setImmutable();
185 
186         std::tie(views[i], std::ignore) =
187                 GrMakeUncachedBitmapProxyView(ctx, bitmap, skgpu::Mipmapped::kNo, fit);
188         if (!views[i]) {
189             return {};
190         }
191         pixmapColorTypes[i] = SkColorTypeToGrColorType(bitmap.colorType());
192     }
193 
194     // TODO: investigate preallocating mip maps here
195     GrImageInfo info(SkColorTypeToGrColorType(img->colorType()),
196                      kPremul_SkAlphaType,
197                      /*color space*/ nullptr,
198                      img->dimensions());
199 
200     auto sfc = ctx->priv().makeSFC(info,
201                                    "ImageLazy_TextureProxyViewFromPlanes",
202                                    SkBackingFit::kExact,
203                                    1,
204                                    skgpu::Mipmapped::kNo,
205                                    GrProtected::kNo,
206                                    kTopLeft_GrSurfaceOrigin,
207                                    budgeted);
208     if (!sfc) {
209         return {};
210     }
211 
212     GrYUVATextureProxies yuvaProxies(yuvaPixmaps.yuvaInfo(), views, pixmapColorTypes);
213     SkAssertResult(yuvaProxies.isValid());
214 
215     std::unique_ptr<GrFragmentProcessor> fp = GrYUVtoRGBEffect::Make(
216             yuvaProxies,
217             GrSamplerState::Filter::kNearest,
218             *ctx->priv().caps());
219 
220     // The pixels after yuv->rgb will be in the generator's color space.
221     // If onMakeColorTypeAndColorSpace has been called then this will not match this image's
222     // color space. To correct this, apply a color space conversion from the generator's color
223     // space to this image's color space.
224     SkColorSpace* srcColorSpace = img->generator()->getInfo().colorSpace();
225     SkColorSpace* dstColorSpace = img->colorSpace();
226 
227     // If the caller expects the pixels in a different color space than the one from the image,
228     // apply a color conversion to do this.
229     fp = GrColorSpaceXformEffect::Make(std::move(fp),
230                                        srcColorSpace, kOpaque_SkAlphaType,
231                                        dstColorSpace, kOpaque_SkAlphaType);
232     sfc->fillWithFP(std::move(fp));
233 
234     return sfc->readSurfaceView();
235 }
236 
generate_picture_texture(GrRecordingContext * ctx,const SkImage_Picture * img,skgpu::Mipmapped mipmapped,GrImageTexGenPolicy texGenPolicy)237 static GrSurfaceProxyView generate_picture_texture(GrRecordingContext* ctx,
238                                                    const SkImage_Picture* img,
239                                                    skgpu::Mipmapped mipmapped,
240                                                    GrImageTexGenPolicy texGenPolicy) {
241     SkASSERT(ctx);
242     SkASSERT(img);
243 
244     skgpu::Budgeted budgeted = texGenPolicy == GrImageTexGenPolicy::kNew_Uncached_Unbudgeted
245                                        ? skgpu::Budgeted::kNo
246                                        : skgpu::Budgeted::kYes;
247     auto surface = SkSurfaces::RenderTarget(ctx,
248                                             budgeted,
249                                             img->imageInfo(),
250                                             0,
251                                             kTopLeft_GrSurfaceOrigin,
252                                             img->props(),
253                                             mipmapped == skgpu::Mipmapped::kYes);
254     if (!surface) {
255         return {};
256     }
257 
258     img->replay(surface->getCanvas());
259 
260     sk_sp<SkImage> image(surface->makeImageSnapshot());
261     if (!image) {
262         return {};
263     }
264 
265     auto [view, ct] = AsView(ctx, image, mipmapped);
266     SkASSERT(view);
267     SkASSERT(mipmapped == skgpu::Mipmapped::kNo ||
268              view.asTextureProxy()->mipmapped() == skgpu::Mipmapped::kYes);
269     return view;
270 }
271 
272 // Returns the texture proxy. We will always cache the generated texture on success.
273 // We have 4 ways to try to return a texture (in sorted order)
274 //
275 // 1. Check the cache for a pre-existing one
276 // 2. Ask the generator to natively create one
277 // 3. Ask the generator to return YUV planes, which the GPU can convert
278 // 4. Ask the generator to return RGB(A) data, which the GPU can convert
LockTextureProxyView(GrRecordingContext * rContext,const SkImage_Lazy * img,GrImageTexGenPolicy texGenPolicy,skgpu::Mipmapped mipmapped)279 GrSurfaceProxyView LockTextureProxyView(GrRecordingContext* rContext,
280                                         const SkImage_Lazy* img,
281                                         GrImageTexGenPolicy texGenPolicy,
282                                         skgpu::Mipmapped mipmapped) {
283     // Values representing the various texture lock paths we can take. Used for logging the path
284     // taken to a histogram.
285     enum LockTexturePath {
286         kFailure_LockTexturePath,
287         kPreExisting_LockTexturePath,
288         kNative_LockTexturePath,
289         kCompressed_LockTexturePath, // Deprecated
290         kYUV_LockTexturePath,
291         kRGBA_LockTexturePath,
292     };
293 
294     enum { kLockTexturePathCount = kRGBA_LockTexturePath + 1 };
295 
296     skgpu::UniqueKey key;
297     if (texGenPolicy == GrImageTexGenPolicy::kDraw) {
298         GrMakeKeyFromImageID(&key, img->uniqueID(), SkIRect::MakeSize(img->dimensions()));
299     }
300 
301     const GrCaps* caps = rContext->priv().caps();
302     GrProxyProvider* proxyProvider = rContext->priv().proxyProvider();
303 
304     auto installKey = [&](const GrSurfaceProxyView& view) {
305         SkASSERT(view && view.asTextureProxy());
306         if (key.isValid()) {
307             auto listener = GrMakeUniqueKeyInvalidationListener(&key, rContext->priv().contextID());
308             img->addUniqueIDListener(std::move(listener));
309             proxyProvider->assignUniqueKeyToProxy(key, view.asTextureProxy());
310         }
311     };
312 
313     auto ct = ColorTypeOfLockTextureProxy(caps, img->colorType());
314 
315     // 1. Check the cache for a pre-existing one.
316     if (key.isValid()) {
317         auto proxy = proxyProvider->findOrCreateProxyByUniqueKey(key);
318         if (proxy) {
319             skgpu::Swizzle swizzle = caps->getReadSwizzle(proxy->backendFormat(), ct);
320             GrSurfaceOrigin origin = get_origin(img);
321             GrSurfaceProxyView view(std::move(proxy), origin, swizzle);
322             if (mipmapped == skgpu::Mipmapped::kNo ||
323                 view.asTextureProxy()->mipmapped() == skgpu::Mipmapped::kYes) {
324                 return view;
325             } else {
326                 // We need a mipped proxy, but we found a cached proxy that wasn't mipped. Thus we
327                 // generate a new mipped surface and copy the original proxy into the base layer. We
328                 // will then let the gpu generate the rest of the mips.
329                 auto mippedView = GrCopyBaseMipMapToView(rContext, view);
330                 if (!mippedView) {
331                     // We failed to make a mipped proxy with the base copied into it. This could
332                     // have been from failure to make the proxy or failure to do the copy. Thus we
333                     // will fall back to just using the non mipped proxy; See skbug.com/7094.
334                     return view;
335                 }
336                 proxyProvider->removeUniqueKeyFromProxy(view.asTextureProxy());
337                 installKey(mippedView);
338                 return mippedView;
339             }
340         }
341     }
342 
343     // 2. Ask the generator to natively create one (if it knows how)
344     {
345         if (img->type() == SkImage_Base::Type::kLazyPicture) {
346             if (auto view = generate_picture_texture(rContext,
347                                                      static_cast<const SkImage_Picture*>(img),
348                                                      mipmapped,
349                                                      texGenPolicy)) {
350                 installKey(view);
351                 return view;
352             }
353         } else if (img->generator()->isTextureGenerator()) {
354             auto sharedGenerator = img->generator();
355             SkAutoMutexExclusive mutex(sharedGenerator->fMutex);
356             auto textureGen = static_cast<GrTextureGenerator*>(sharedGenerator->fGenerator.get());
357             if (auto view = textureGen->generateTexture(rContext,
358                                                         img->imageInfo(),
359                                                         mipmapped,
360                                                         texGenPolicy)) {
361                 installKey(view);
362                 return view;
363             }
364         }
365     }
366 
367     // 3. Ask the generator to return YUV planes, which the GPU can convert. If we will be mipping
368     //    the texture we skip this step so the CPU generate non-planar MIP maps for us.
369     if (mipmapped == skgpu::Mipmapped::kNo &&
370         !rContext->priv().options().fDisableGpuYUVConversion) {
371         // TODO: Update to create the mipped surface in the textureProxyViewFromPlanes generator and
372         //  draw the base layer directly into the mipped surface.
373         skgpu::Budgeted budgeted = texGenPolicy == GrImageTexGenPolicy::kNew_Uncached_Unbudgeted
374                                            ? skgpu::Budgeted::kNo
375                                            : skgpu::Budgeted::kYes;
376         auto view = texture_proxy_view_from_planes(rContext, img, budgeted);
377         if (view) {
378             installKey(view);
379             return view;
380         }
381     }
382 
383     // 4. Ask the generator to return a bitmap, which the GPU can convert.
384     auto hint = texGenPolicy == GrImageTexGenPolicy::kDraw ? SkImage::CachingHint::kAllow_CachingHint
385                                                            : SkImage::CachingHint::kDisallow_CachingHint;
386     if (SkBitmap bitmap; img->getROPixels(nullptr, &bitmap, hint)) {
387         // We always make an uncached bitmap here because we will cache it based on passed in policy
388         // with *our* key, not a key derived from bitmap. We're just making the proxy here.
389         auto budgeted = texGenPolicy == GrImageTexGenPolicy::kNew_Uncached_Unbudgeted
390                                 ? skgpu::Budgeted::kNo
391                                 : skgpu::Budgeted::kYes;
392         auto view = std::get<0>(GrMakeUncachedBitmapProxyView(rContext,
393                                                               bitmap,
394                                                               mipmapped,
395                                                               SkBackingFit::kExact,
396                                                               budgeted));
397         if (view) {
398             installKey(view);
399             return view;
400         }
401     }
402 
403     return {};
404 }
405 
lazy_as_view(GrRecordingContext * context,const SkImage_Lazy * img,skgpu::Mipmapped mipmapped,GrImageTexGenPolicy policy)406 static std::tuple<GrSurfaceProxyView, GrColorType> lazy_as_view(GrRecordingContext* context,
407                                                                 const SkImage_Lazy* img,
408                                                                 skgpu::Mipmapped mipmapped,
409                                                                 GrImageTexGenPolicy policy) {
410     GrColorType ct = ColorTypeOfLockTextureProxy(context->priv().caps(), img->colorType());
411     return {LockTextureProxyView(context, img, policy, mipmapped), ct};
412 }
413 
AsView(GrRecordingContext * rContext,const SkImage * img,skgpu::Mipmapped mipmapped,GrImageTexGenPolicy policy)414 std::tuple<GrSurfaceProxyView, GrColorType> AsView(GrRecordingContext* rContext,
415                                                    const SkImage* img,
416                                                    skgpu::Mipmapped mipmapped,
417                                                    GrImageTexGenPolicy policy) {
418     SkASSERT(img);
419     if (!rContext) {
420         return {};
421     }
422     if (!rContext->priv().caps()->mipmapSupport() || img->dimensions().area() <= 1) {
423         mipmapped = skgpu::Mipmapped::kNo;
424     }
425 
426     auto ib = static_cast<const SkImage_Base*>(img);
427     if (ib->type() == SkImage_Base::Type::kRaster) {
428         return skgpu::ganesh::RasterAsView(
429                     rContext, static_cast<const SkImage_Raster*>(ib), mipmapped, policy);
430     } else if (ib->type() == SkImage_Base::Type::kRasterPinnable) {
431         auto rp = static_cast<const SkImage_RasterPinnable*>(img);
432         return rp->asView(rContext, mipmapped, policy);
433     } else if (ib->isGaneshBacked()) {
434         auto gb = static_cast<const SkImage_GaneshBase*>(img);
435         return gb->asView(rContext, mipmapped, policy);
436     } else if (ib->isLazyGenerated()) {
437         return lazy_as_view(rContext, static_cast<const SkImage_Lazy*>(ib), mipmapped, policy);
438     }
439 
440     SkDEBUGFAIL("Unsupported image type to make a View");
441     return {};
442 }
443 
make_fp_from_view(GrRecordingContext * rContext,GrSurfaceProxyView view,SkAlphaType at,SkSamplingOptions sampling,const SkTileMode tileModes[2],const SkMatrix & m,const SkRect * subset,const SkRect * domain)444 static std::unique_ptr<GrFragmentProcessor> make_fp_from_view(GrRecordingContext* rContext,
445                                                               GrSurfaceProxyView view,
446                                                               SkAlphaType at,
447                                                               SkSamplingOptions sampling,
448                                                               const SkTileMode tileModes[2],
449                                                               const SkMatrix& m,
450                                                               const SkRect* subset,
451                                                               const SkRect* domain) {
452     if (!view) {
453         return nullptr;
454     }
455     const GrCaps& caps = *rContext->priv().caps();
456     auto wmx = SkTileModeToWrapMode(tileModes[0]);
457     auto wmy = SkTileModeToWrapMode(tileModes[1]);
458     if (sampling.useCubic) {
459         if (subset) {
460             if (domain) {
461                 return GrBicubicEffect::MakeSubset(std::move(view),
462                                                    at,
463                                                    m,
464                                                    wmx,
465                                                    wmy,
466                                                    *subset,
467                                                    *domain,
468                                                    sampling.cubic,
469                                                    GrBicubicEffect::Direction::kXY,
470                                                    *rContext->priv().caps());
471             }
472             return GrBicubicEffect::MakeSubset(std::move(view),
473                                                at,
474                                                m,
475                                                wmx,
476                                                wmy,
477                                                *subset,
478                                                sampling.cubic,
479                                                GrBicubicEffect::Direction::kXY,
480                                                *rContext->priv().caps());
481         }
482         return GrBicubicEffect::Make(std::move(view),
483                                      at,
484                                      m,
485                                      wmx,
486                                      wmy,
487                                      sampling.cubic,
488                                      GrBicubicEffect::Direction::kXY,
489                                      *rContext->priv().caps());
490     }
491     if (sampling.isAniso()) {
492         if (!rContext->priv().caps()->anisoSupport()) {
493             // Fallback to linear
494             sampling = SkSamplingPriv::AnisoFallback(view.mipmapped() == skgpu::Mipmapped::kYes);
495         }
496     } else if (view.mipmapped() == skgpu::Mipmapped::kNo) {
497         sampling = SkSamplingOptions(sampling.filter);
498     }
499     GrSamplerState sampler;
500     if (sampling.isAniso()) {
501         sampler = GrSamplerState::Aniso(wmx, wmy, sampling.maxAniso, view.mipmapped());
502     } else {
503         sampler = GrSamplerState(wmx, wmy, sampling.filter, sampling.mipmap);
504     }
505     if (subset) {
506         if (domain) {
507             return GrTextureEffect::MakeSubset(
508                     std::move(view), at, m, sampler, *subset, *domain, caps);
509         }
510         return GrTextureEffect::MakeSubset(std::move(view), at, m, sampler, *subset, caps);
511     } else {
512         return GrTextureEffect::Make(std::move(view), at, m, sampler, caps);
513     }
514 }
515 
raster_as_fp(GrRecordingContext * rContext,const SkImage_Raster * img,SkSamplingOptions sampling,const SkTileMode tileModes[2],const SkMatrix & m,const SkRect * subset,const SkRect * domain)516 std::unique_ptr<GrFragmentProcessor> raster_as_fp(GrRecordingContext* rContext,
517                                                   const SkImage_Raster* img,
518                                                   SkSamplingOptions sampling,
519                                                   const SkTileMode tileModes[2],
520                                                   const SkMatrix& m,
521                                                   const SkRect* subset,
522                                                   const SkRect* domain) {
523     auto mm =
524             sampling.mipmap == SkMipmapMode::kNone ? skgpu::Mipmapped::kNo : skgpu::Mipmapped::kYes;
525     return make_fp_from_view(rContext,
526                              std::get<0>(AsView(rContext, img, mm)),
527                              img->alphaType(),
528                              sampling,
529                              tileModes,
530                              m,
531                              subset,
532                              domain);
533 }
534 
AsFragmentProcessor(GrRecordingContext * rContext,const SkImage * img,SkSamplingOptions sampling,const SkTileMode tileModes[2],const SkMatrix & m,const SkRect * subset,const SkRect * domain)535 std::unique_ptr<GrFragmentProcessor> AsFragmentProcessor(GrRecordingContext* rContext,
536                                                          const SkImage* img,
537                                                          SkSamplingOptions sampling,
538                                                          const SkTileMode tileModes[2],
539                                                          const SkMatrix& m,
540                                                          const SkRect* subset,
541                                                          const SkRect* domain) {
542     if (!rContext) {
543         return {};
544     }
545     if (sampling.useCubic && !GrValidCubicResampler(sampling.cubic)) {
546         return {};
547     }
548     if (sampling.mipmap != SkMipmapMode::kNone &&
549         (!rContext->priv().caps()->mipmapSupport() || img->dimensions().area() <= 1)) {
550         sampling = SkSamplingOptions(sampling.filter);
551     }
552 
553     auto ib = static_cast<const SkImage_Base*>(img);
554     if (ib->isRasterBacked()) {
555         return raster_as_fp(rContext,
556                             static_cast<const SkImage_Raster*>(ib),
557                             sampling,
558                             tileModes,
559                             m,
560                             subset,
561                             domain);
562     } else if (ib->isGaneshBacked()) {
563         auto gb = static_cast<const SkImage_GaneshBase*>(img);
564         return gb->asFragmentProcessor(rContext, sampling, tileModes, m, subset, domain);
565     } else if (ib->isLazyGenerated()) {
566         // TODO: If the CPU data is extracted as planes return a FP that reconstructs the image from
567         // the planes.
568         auto mm = sampling.mipmap == SkMipmapMode::kNone ? skgpu::Mipmapped::kNo : skgpu::Mipmapped::kYes;
569         return MakeFragmentProcessorFromView(rContext,
570                                              std::get<0>(AsView(rContext, img, mm)),
571                                              img->alphaType(),
572                                              sampling,
573                                              tileModes,
574                                              m,
575                                              subset,
576                                              domain);
577     }
578 
579     SkDEBUGFAIL("Unsupported image type to make a FragmentProcessor");
580     return {};
581 }
582 
MakeFragmentProcessorFromView(GrRecordingContext * rContext,GrSurfaceProxyView view,SkAlphaType at,SkSamplingOptions sampling,const SkTileMode tileModes[2],const SkMatrix & m,const SkRect * subset,const SkRect * domain)583 std::unique_ptr<GrFragmentProcessor> MakeFragmentProcessorFromView(
584         GrRecordingContext* rContext,
585         GrSurfaceProxyView view,
586         SkAlphaType at,
587         SkSamplingOptions sampling,
588         const SkTileMode tileModes[2],
589         const SkMatrix& m,
590         const SkRect* subset,
591         const SkRect* domain) {
592     if (!view) {
593         return nullptr;
594     }
595     const GrCaps& caps = *rContext->priv().caps();
596     auto wmx = SkTileModeToWrapMode(tileModes[0]);
597     auto wmy = SkTileModeToWrapMode(tileModes[1]);
598     if (sampling.useCubic) {
599         if (subset) {
600             if (domain) {
601                 return GrBicubicEffect::MakeSubset(std::move(view),
602                                                    at,
603                                                    m,
604                                                    wmx,
605                                                    wmy,
606                                                    *subset,
607                                                    *domain,
608                                                    sampling.cubic,
609                                                    GrBicubicEffect::Direction::kXY,
610                                                    *rContext->priv().caps());
611             }
612             return GrBicubicEffect::MakeSubset(std::move(view),
613                                                at,
614                                                m,
615                                                wmx,
616                                                wmy,
617                                                *subset,
618                                                sampling.cubic,
619                                                GrBicubicEffect::Direction::kXY,
620                                                *rContext->priv().caps());
621         }
622         return GrBicubicEffect::Make(std::move(view),
623                                      at,
624                                      m,
625                                      wmx,
626                                      wmy,
627                                      sampling.cubic,
628                                      GrBicubicEffect::Direction::kXY,
629                                      *rContext->priv().caps());
630     }
631     if (sampling.isAniso()) {
632         if (!rContext->priv().caps()->anisoSupport()) {
633             // Fallback to linear
634             sampling = SkSamplingPriv::AnisoFallback(view.mipmapped() == skgpu::Mipmapped::kYes);
635         }
636     } else if (view.mipmapped() == skgpu::Mipmapped::kNo) {
637         sampling = SkSamplingOptions(sampling.filter);
638     }
639     GrSamplerState sampler;
640     if (sampling.isAniso()) {
641         sampler = GrSamplerState::Aniso(wmx, wmy, sampling.maxAniso, view.mipmapped());
642     } else {
643         sampler = GrSamplerState(wmx, wmy, sampling.filter, sampling.mipmap);
644     }
645     if (subset) {
646         if (domain) {
647             return GrTextureEffect::MakeSubset(std::move(view),
648                                                at,
649                                                m,
650                                                sampler,
651                                                *subset,
652                                                *domain,
653                                                caps);
654         }
655         return GrTextureEffect::MakeSubset(std::move(view),
656                                            at,
657                                            m,
658                                            sampler,
659                                            *subset,
660                                            caps);
661     } else {
662         return GrTextureEffect::Make(std::move(view), at, m, sampler, caps);
663     }
664 }
665 
FindOrMakeCachedMipmappedView(GrRecordingContext * rContext,GrSurfaceProxyView view,uint32_t imageUniqueID)666 GrSurfaceProxyView FindOrMakeCachedMipmappedView(GrRecordingContext* rContext,
667                                                  GrSurfaceProxyView view,
668                                                  uint32_t imageUniqueID) {
669     SkASSERT(rContext);
670     SkASSERT(imageUniqueID != SK_InvalidUniqueID);
671 
672     if (!view || view.proxy()->asTextureProxy()->mipmapped() == skgpu::Mipmapped::kYes) {
673         return view;
674     }
675     GrProxyProvider* proxyProvider = rContext->priv().proxyProvider();
676 
677     skgpu::UniqueKey baseKey;
678     GrMakeKeyFromImageID(&baseKey, imageUniqueID, SkIRect::MakeSize(view.dimensions()));
679     SkASSERT(baseKey.isValid());
680     skgpu::UniqueKey mipmappedKey;
681     static const skgpu::UniqueKey::Domain kMipmappedDomain = skgpu::UniqueKey::GenerateDomain();
682     {  // No extra values beyond the domain are required. Must name the var to please
683        // clang-tidy.
684         skgpu::UniqueKey::Builder b(&mipmappedKey, baseKey, kMipmappedDomain, 0);
685     }
686     SkASSERT(mipmappedKey.isValid());
687     if (sk_sp<GrTextureProxy> cachedMippedView =
688                 proxyProvider->findOrCreateProxyByUniqueKey(mipmappedKey)) {
689         return {std::move(cachedMippedView), view.origin(), view.swizzle()};
690     }
691 
692     auto copy = GrCopyBaseMipMapToView(rContext, view);
693     if (!copy) {
694         return view;
695     }
696     // TODO: If we move listeners up from SkImage_Lazy to SkImage_Base then add one here.
697     proxyProvider->assignUniqueKeyToProxy(mipmappedKey, copy.asTextureProxy());
698     return copy;
699 }
700 
701 using DataType = SkYUVAPixmapInfo::DataType;
702 
SupportedTextureFormats(const GrImageContext & context)703 SkYUVAPixmapInfo::SupportedDataTypes SupportedTextureFormats(const GrImageContext& context) {
704     SkYUVAPixmapInfo::SupportedDataTypes dataTypes;
705     const auto isValid = [&context](DataType dt, int n) {
706         return context.defaultBackendFormat(SkYUVAPixmapInfo::DefaultColorTypeForDataType(dt, n),
707                                             GrRenderable::kNo).isValid();
708     };
709      for (int n = 1; n <= 4; ++n) {
710         if (isValid(DataType::kUnorm8, n)) {
711             dataTypes.enableDataType(DataType::kUnorm8, n);
712         }
713         if (isValid(DataType::kUnorm16, n)) {
714             dataTypes.enableDataType(DataType::kUnorm16, n);
715         }
716         if (isValid(DataType::kFloat16, n)) {
717             dataTypes.enableDataType(DataType::kFloat16, n);
718         }
719         if (isValid(DataType::kUnorm10_Unorm2, n)) {
720             dataTypes.enableDataType(DataType::kUnorm10_Unorm2, n);
721         }
722     }
723      return dataTypes;
724 }
725 
726 }  // namespace skgpu::ganesh
727 
728 namespace skif {
729 
730 namespace {
731 
732 class GaneshBackend : public Backend, private SkBlurEngine, private SkBlurEngine::Algorithm {
733 public:
734 
GaneshBackend(sk_sp<GrRecordingContext> context,GrSurfaceOrigin origin,const SkSurfaceProps & surfaceProps,SkColorType colorType)735     GaneshBackend(sk_sp<GrRecordingContext> context,
736                   GrSurfaceOrigin origin,
737                   const SkSurfaceProps& surfaceProps,
738                   SkColorType colorType)
739             : Backend(SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize),
740                       surfaceProps, colorType)
741             , fContext(std::move(context))
742             , fOrigin(origin) {}
743 
744     // Backend
makeDevice(SkISize size,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props) const745     sk_sp<SkDevice> makeDevice(SkISize size,
746                                sk_sp<SkColorSpace> colorSpace,
747                                const SkSurfaceProps* props) const override {
748         SkImageInfo imageInfo = SkImageInfo::Make(size,
749                                                   this->colorType(),
750                                                   kPremul_SkAlphaType,
751                                                   std::move(colorSpace));
752 
753         return fContext->priv().createDevice(skgpu::Budgeted::kYes,
754                                              imageInfo,
755                                              SkBackingFit::kApprox,
756                                              1,
757                                              skgpu::Mipmapped::kNo,
758                                              GrProtected::kNo,
759                                              fOrigin,
760                                              props ? *props : this->surfaceProps(),
761                                              skgpu::ganesh::Device::InitContents::kUninit);
762     }
763 
makeImage(const SkIRect & subset,sk_sp<SkImage> image) const764     sk_sp<SkSpecialImage> makeImage(const SkIRect& subset, sk_sp<SkImage> image) const override {
765         return SkSpecialImages::MakeFromTextureImage(
766                 fContext.get(), subset, image, this->surfaceProps());
767     }
768 
getCachedBitmap(const SkBitmap & data) const769     sk_sp<SkImage> getCachedBitmap(const SkBitmap& data) const override {
770         // This uses the thread safe cache (instead of GrMakeCachedBitmapProxyView) so that image
771         // filters can be evaluated on other threads with DDLs.
772         auto threadSafeCache = fContext->priv().threadSafeCache();
773 
774         skgpu::UniqueKey key;
775         SkIRect subset = SkIRect::MakePtSize(data.pixelRefOrigin(), data.dimensions());
776         GrMakeKeyFromImageID(&key, data.getGenerationID(), subset);
777 
778         auto view = threadSafeCache->find(key);
779         if (!view) {
780             view = std::get<0>(GrMakeUncachedBitmapProxyView(fContext.get(), data));
781             if (!view) {
782                 return nullptr;
783             }
784             threadSafeCache->add(key, view);
785         }
786 
787         return sk_make_sp<SkImage_Ganesh>(fContext,
788                                           data.getGenerationID(),
789                                           std::move(view),
790                                           data.info().colorInfo());
791     }
792 
getBlurEngine() const793     const SkBlurEngine* getBlurEngine() const override { return this; }
794 
795     // SkBlurEngine
findAlgorithm(SkSize sigma,SkColorType colorType) const796     const SkBlurEngine::Algorithm* findAlgorithm(SkSize sigma,
797                                                  SkColorType colorType) const override {
798         // GrBlurUtils supports all tile modes and color types
799         return this;
800     }
801 
802     // SkBlurEngine::Algorithm
maxSigma() const803     float maxSigma() const override {
804         // GrBlurUtils handles resizing at the moment
805         return SK_ScalarInfinity;
806     }
807 
supportsOnlyDecalTiling() const808     bool supportsOnlyDecalTiling() const override { return false; }
809 
blur(SkSize sigma,sk_sp<SkSpecialImage> input,const SkIRect & srcRect,SkTileMode tileMode,const SkIRect & dstRect) const810     sk_sp<SkSpecialImage> blur(SkSize sigma,
811                                sk_sp<SkSpecialImage> input,
812                                const SkIRect& srcRect,
813                                SkTileMode tileMode,
814                                const SkIRect& dstRect) const override {
815         GrSurfaceProxyView inputView = SkSpecialImages::AsView(fContext.get(), input);
816         if (!inputView.proxy()) {
817             return nullptr;
818         }
819         SkASSERT(inputView.asTextureProxy());
820 
821         // Update srcRect and dstRect to be relative to the underlying texture proxy of 'input'.
822         auto proxyOffset = input->subset().topLeft() - srcRect.topLeft();
823         auto sdc = GrBlurUtils::GaussianBlur(
824                 fContext.get(),
825                 std::move(inputView),
826                 SkColorTypeToGrColorType(input->colorType()),
827                 input->alphaType(),
828                 sk_ref_sp(input->getColorSpace()),
829                 dstRect.makeOffset(proxyOffset),
830                 srcRect.makeOffset(proxyOffset),
831                 sigma.width(),
832                 sigma.height(),
833                 tileMode);
834         if (!sdc) {
835             return nullptr;
836         }
837 
838         return SkSpecialImages::MakeDeferredFromGpu(fContext.get(),
839                                                     SkIRect::MakeSize(dstRect.size()),
840                                                     kNeedNewImageUniqueID_SpecialImage,
841                                                     sdc->readSurfaceView(),
842                                                     sdc->colorInfo(),
843                                                     this->surfaceProps());
844     }
845 
846 private:
847     sk_sp<GrRecordingContext> fContext;
848     GrSurfaceOrigin fOrigin;
849 };
850 
851 } // anonymous namespace
852 
MakeGaneshBackend(sk_sp<GrRecordingContext> context,GrSurfaceOrigin origin,const SkSurfaceProps & surfaceProps,SkColorType colorType)853 sk_sp<Backend> MakeGaneshBackend(sk_sp<GrRecordingContext> context,
854                                  GrSurfaceOrigin origin,
855                                  const SkSurfaceProps& surfaceProps,
856                                  SkColorType colorType) {
857     SkASSERT(context);
858     return sk_make_sp<GaneshBackend>(std::move(context), origin, surfaceProps, colorType);
859 }
860 
861 }  // namespace skif
862