xref: /aosp_15_r20/external/skia/src/gpu/graphite/TextureUtils.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2022 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/graphite/TextureUtils.h"
9 
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkPaint.h"
14 #include "include/core/SkSurface.h"
15 #include "include/effects/SkRuntimeEffect.h"
16 #include "src/core/SkBlurEngine.h"
17 #include "src/core/SkCompressedDataUtils.h"
18 #include "src/core/SkDevice.h"
19 #include "src/core/SkImageFilterCache.h"
20 #include "src/core/SkImageFilterTypes.h"
21 #include "src/core/SkMipmap.h"
22 #include "src/core/SkSamplingPriv.h"
23 #include "src/core/SkTraceEvent.h"
24 #include "src/image/SkImage_Base.h"
25 
26 #include "include/gpu/graphite/BackendTexture.h"
27 #include "include/gpu/graphite/Context.h"
28 #include "include/gpu/graphite/GraphiteTypes.h"
29 #include "include/gpu/graphite/Image.h"
30 #include "include/gpu/graphite/ImageProvider.h"
31 #include "include/gpu/graphite/Recorder.h"
32 #include "include/gpu/graphite/Recording.h"
33 #include "include/gpu/graphite/Surface.h"
34 #include "src/gpu/BlurUtils.h"
35 #include "src/gpu/RefCntedCallback.h"
36 #include "src/gpu/SkBackingFit.h"
37 #include "src/gpu/graphite/Buffer.h"
38 #include "src/gpu/graphite/Caps.h"
39 #include "src/gpu/graphite/CommandBuffer.h"
40 #include "src/gpu/graphite/Device.h"
41 #include "src/gpu/graphite/Image_Graphite.h"
42 #include "src/gpu/graphite/Log.h"
43 #include "src/gpu/graphite/RecorderPriv.h"
44 #include "src/gpu/graphite/ResourceProvider.h"
45 #include "src/gpu/graphite/ResourceTypes.h"
46 #include "src/gpu/graphite/SpecialImage_Graphite.h"
47 #include "src/gpu/graphite/Surface_Graphite.h"
48 #include "src/gpu/graphite/Texture.h"
49 #include "src/gpu/graphite/task/CopyTask.h"
50 #include "src/gpu/graphite/task/SynchronizeToCpuTask.h"
51 #include "src/gpu/graphite/task/UploadTask.h"
52 
53 #include <array>
54 
55 
56 using SkImages::GraphitePromiseTextureFulfillProc;
57 using SkImages::GraphitePromiseTextureFulfillContext;
58 using SkImages::GraphitePromiseTextureReleaseProc;
59 
60 namespace skgpu::graphite {
61 
62 namespace {
63 
make_renderable_scratch_surface(Recorder * recorder,const SkImageInfo & info,SkBackingFit backingFit,std::string_view label,const SkSurfaceProps * surfaceProps=nullptr)64 sk_sp<Surface> make_renderable_scratch_surface(
65         Recorder* recorder,
66         const SkImageInfo& info,
67         SkBackingFit backingFit,
68         std::string_view label,
69         const SkSurfaceProps* surfaceProps = nullptr) {
70     SkColorType ct = recorder->priv().caps()->getRenderableColorType(info.colorType());
71     if (ct == kUnknown_SkColorType) {
72         return nullptr;
73     }
74 
75     // TODO(b/323886870): Historically the scratch surfaces used here were exact-fit but they should
76     // be able to be approx-fit and uninstantiated.
77     return Surface::MakeScratch(recorder,
78                                 info.makeColorType(ct),
79                                 std::move(label),
80                                 Budgeted::kYes,
81                                 Mipmapped::kNo,
82                                 backingFit);
83 }
84 
valid_client_provided_image(const SkImage * clientProvided,const SkImage * original,SkImage::RequiredProperties requiredProps)85 bool valid_client_provided_image(const SkImage* clientProvided,
86                                  const SkImage* original,
87                                  SkImage::RequiredProperties requiredProps) {
88     if (!clientProvided ||
89         !as_IB(clientProvided)->isGraphiteBacked() ||
90         original->dimensions() != clientProvided->dimensions() ||
91         original->alphaType() != clientProvided->alphaType()) {
92         return false;
93     }
94 
95     uint32_t origChannels = SkColorTypeChannelFlags(original->colorType());
96     uint32_t clientChannels = SkColorTypeChannelFlags(clientProvided->colorType());
97     if ((origChannels & clientChannels) != origChannels) {
98         return false;
99     }
100 
101     // We require provided images to have a TopLeft origin
102     auto graphiteImage = static_cast<const Image*>(clientProvided);
103     if (graphiteImage->textureProxyView().origin() != Origin::kTopLeft) {
104         SKGPU_LOG_E("Client provided image must have a TopLeft origin.");
105         return false;
106     }
107 
108     return true;
109 }
110 
111 // This class is the lazy instantiation callback for promise images. It manages calling the
112 // client's Fulfill, ImageRelease, and TextureRelease procs.
113 class PromiseLazyInstantiateCallback {
114 public:
PromiseLazyInstantiateCallback(sk_sp<RefCntedCallback> releaseHelper,GraphitePromiseTextureFulfillProc fulfillProc,GraphitePromiseTextureFulfillContext fulfillContext,GraphitePromiseTextureReleaseProc textureReleaseProc,std::string_view label)115     PromiseLazyInstantiateCallback(sk_sp<RefCntedCallback> releaseHelper,
116                                    GraphitePromiseTextureFulfillProc fulfillProc,
117                                    GraphitePromiseTextureFulfillContext fulfillContext,
118                                    GraphitePromiseTextureReleaseProc textureReleaseProc,
119                                    std::string_view label)
120             : fReleaseHelper(std::move(releaseHelper))
121             , fFulfillProc(fulfillProc)
122             , fFulfillContext(fulfillContext)
123             , fTextureReleaseProc(textureReleaseProc)
124             , fLabel(label) {
125     }
126     PromiseLazyInstantiateCallback(PromiseLazyInstantiateCallback&&) = default;
PromiseLazyInstantiateCallback(const PromiseLazyInstantiateCallback &)127     PromiseLazyInstantiateCallback(const PromiseLazyInstantiateCallback&) {
128         // Because we get wrapped in std::function we must be copyable. But we should never
129         // be copied.
130         SkASSERT(false);
131     }
132     PromiseLazyInstantiateCallback& operator=(PromiseLazyInstantiateCallback&&) = default;
operator =(const PromiseLazyInstantiateCallback &)133     PromiseLazyInstantiateCallback& operator=(const PromiseLazyInstantiateCallback&) {
134         SkASSERT(false);
135         return *this;
136     }
137 
operator ()(ResourceProvider * resourceProvider)138     sk_sp<Texture> operator()(ResourceProvider* resourceProvider) {
139         // Invoke the fulfill proc to get the promised backend texture.
140         auto [ backendTexture, textureReleaseCtx ] = fFulfillProc(fFulfillContext);
141         if (!backendTexture.isValid()) {
142             SKGPU_LOG_W("FulfillProc returned an invalid backend texture");
143             return nullptr;
144         }
145 
146         sk_sp<RefCntedCallback> textureReleaseCB = RefCntedCallback::Make(fTextureReleaseProc,
147                                                                           textureReleaseCtx);
148 
149         sk_sp<Texture> texture = resourceProvider->createWrappedTexture(backendTexture,
150                                                                         std::move(fLabel));
151         if (!texture) {
152             SKGPU_LOG_W("Failed to wrap BackendTexture returned by fulfill proc");
153             return nullptr;
154         }
155         texture->setReleaseCallback(std::move(textureReleaseCB));
156         return texture;
157     }
158 
159 private:
160     sk_sp<RefCntedCallback> fReleaseHelper;
161     GraphitePromiseTextureFulfillProc fFulfillProc;
162     GraphitePromiseTextureFulfillContext fFulfillContext;
163     GraphitePromiseTextureReleaseProc fTextureReleaseProc;
164     std::string fLabel;
165 };
166 
167 } // anonymous namespace
168 
MakeBitmapProxyView(Recorder * recorder,const SkBitmap & bitmap,sk_sp<SkMipmap> mipmapsIn,Mipmapped mipmapped,Budgeted budgeted,std::string_view label)169 std::tuple<TextureProxyView, SkColorType> MakeBitmapProxyView(Recorder* recorder,
170                                                               const SkBitmap& bitmap,
171                                                               sk_sp<SkMipmap> mipmapsIn,
172                                                               Mipmapped mipmapped,
173                                                               Budgeted budgeted,
174                                                               std::string_view label) {
175     // Adjust params based on input and Caps
176     const Caps* caps = recorder->priv().caps();
177     SkColorType ct = bitmap.info().colorType();
178 
179     if (bitmap.dimensions().area() <= 1) {
180         mipmapped = Mipmapped::kNo;
181     }
182 
183     Protected isProtected = recorder->priv().isProtected();
184     auto textureInfo = caps->getDefaultSampledTextureInfo(ct, mipmapped, isProtected,
185                                                           Renderable::kNo);
186     if (!textureInfo.isValid()) {
187         ct = kRGBA_8888_SkColorType;
188         textureInfo = caps->getDefaultSampledTextureInfo(ct, mipmapped, isProtected,
189                                                          Renderable::kNo);
190     }
191     SkASSERT(textureInfo.isValid());
192 
193     // Convert bitmap to texture colortype if necessary
194     SkBitmap bmpToUpload;
195     if (ct != bitmap.info().colorType()) {
196         if (!bmpToUpload.tryAllocPixels(bitmap.info().makeColorType(ct)) ||
197             !bitmap.readPixels(bmpToUpload.pixmap())) {
198             return {};
199         }
200         bmpToUpload.setImmutable();
201     } else {
202         bmpToUpload = bitmap;
203     }
204 
205     if (!SkImageInfoIsValid(bmpToUpload.info())) {
206         return {};
207     }
208 
209     int mipLevelCount = (mipmapped == Mipmapped::kYes) ?
210             SkMipmap::ComputeLevelCount(bitmap.width(), bitmap.height()) + 1 : 1;
211 
212 
213     // setup MipLevels
214     sk_sp<SkMipmap> mipmaps;
215     std::vector<MipLevel> texels;
216     if (mipLevelCount == 1) {
217         texels.resize(mipLevelCount);
218         texels[0].fPixels = bmpToUpload.getPixels();
219         texels[0].fRowBytes = bmpToUpload.rowBytes();
220     } else {
221         mipmaps = SkToBool(mipmapsIn)
222                           ? mipmapsIn
223                           : sk_sp<SkMipmap>(SkMipmap::Build(bmpToUpload.pixmap(), nullptr));
224         if (!mipmaps) {
225             return {};
226         }
227 
228         SkASSERT(mipLevelCount == mipmaps->countLevels() + 1);
229         texels.resize(mipLevelCount);
230 
231         texels[0].fPixels = bmpToUpload.getPixels();
232         texels[0].fRowBytes = bmpToUpload.rowBytes();
233 
234         for (int i = 1; i < mipLevelCount; ++i) {
235             SkMipmap::Level generatedMipLevel;
236             mipmaps->getLevel(i - 1, &generatedMipLevel);
237             texels[i].fPixels = generatedMipLevel.fPixmap.addr();
238             texels[i].fRowBytes = generatedMipLevel.fPixmap.rowBytes();
239             SkASSERT(texels[i].fPixels);
240             SkASSERT(generatedMipLevel.fPixmap.colorType() == bmpToUpload.colorType());
241         }
242     }
243 
244     // Create proxy
245     sk_sp<TextureProxy> proxy = TextureProxy::Make(caps,
246                                                    recorder->priv().resourceProvider(),
247                                                    bmpToUpload.dimensions(),
248                                                    textureInfo,
249                                                    std::move(label),
250                                                    budgeted);
251     if (!proxy) {
252         return {};
253     }
254     SkASSERT(caps->areColorTypeAndTextureInfoCompatible(ct, proxy->textureInfo()));
255     SkASSERT(mipmapped == Mipmapped::kNo || proxy->mipmapped() == Mipmapped::kYes);
256 
257     // Src and dst colorInfo are the same
258     const SkColorInfo& colorInfo = bmpToUpload.info().colorInfo();
259     // Add upload to the root upload list. These bitmaps are uploaded to unique textures so there is
260     // no need to coordinate resource sharing. It is better to then group them into a single task
261     // at the start of the Recording.
262     if (!recorder->priv().rootUploadList()->recordUpload(
263             recorder, proxy, colorInfo, colorInfo, texels,
264             SkIRect::MakeSize(bmpToUpload.dimensions()),
265             std::make_unique<ImageUploadContext>())) {
266         SKGPU_LOG_E("MakeBitmapProxyView: Could not create UploadInstance");
267         return {};
268     }
269 
270     Swizzle swizzle = caps->getReadSwizzle(ct, textureInfo);
271     // If the color type is alpha-only, propagate the alpha value to the other channels.
272     if (SkColorTypeIsAlphaOnly(colorInfo.colorType())) {
273         swizzle = Swizzle::Concat(swizzle, Swizzle("aaaa"));
274     }
275     return {{std::move(proxy), swizzle}, ct};
276 }
277 
MakePromiseImageLazyProxy(const Caps * caps,SkISize dimensions,TextureInfo textureInfo,Volatile isVolatile,sk_sp<RefCntedCallback> releaseHelper,GraphitePromiseTextureFulfillProc fulfillProc,GraphitePromiseTextureFulfillContext fulfillContext,GraphitePromiseTextureReleaseProc textureReleaseProc,std::string_view label)278 sk_sp<TextureProxy> MakePromiseImageLazyProxy(
279         const Caps* caps,
280         SkISize dimensions,
281         TextureInfo textureInfo,
282         Volatile isVolatile,
283         sk_sp<RefCntedCallback> releaseHelper,
284         GraphitePromiseTextureFulfillProc fulfillProc,
285         GraphitePromiseTextureFulfillContext fulfillContext,
286         GraphitePromiseTextureReleaseProc textureReleaseProc,
287         std::string_view label) {
288     SkASSERT(!dimensions.isEmpty());
289     SkASSERT(releaseHelper);
290 
291     if (!fulfillProc) {
292         return nullptr;
293     }
294 
295     PromiseLazyInstantiateCallback callback{std::move(releaseHelper), fulfillProc,
296                                             fulfillContext, textureReleaseProc, std::move(label)};
297     // Proxies for promise images are assumed to always be destined for a client's SkImage so
298     // are never considered budgeted.
299     return TextureProxy::MakeLazy(caps, dimensions, textureInfo, Budgeted::kNo, isVolatile,
300                                   std::move(callback));
301 }
302 
MakeFromBitmap(Recorder * recorder,const SkColorInfo & colorInfo,const SkBitmap & bitmap,sk_sp<SkMipmap> mipmaps,Budgeted budgeted,SkImage::RequiredProperties requiredProps,std::string_view label)303 sk_sp<SkImage> MakeFromBitmap(Recorder* recorder,
304                               const SkColorInfo& colorInfo,
305                               const SkBitmap& bitmap,
306                               sk_sp<SkMipmap> mipmaps,
307                               Budgeted budgeted,
308                               SkImage::RequiredProperties requiredProps,
309                               std::string_view label) {
310     auto mm = requiredProps.fMipmapped ? Mipmapped::kYes : Mipmapped::kNo;
311     auto [view, ct] = MakeBitmapProxyView(recorder,
312                                           bitmap,
313                                           std::move(mipmaps),
314                                           mm,
315                                           budgeted,
316                                           std::move(label));
317     if (!view) {
318         return nullptr;
319     }
320 
321     SkASSERT(!requiredProps.fMipmapped || view.proxy()->mipmapped() == Mipmapped::kYes);
322     return sk_make_sp<skgpu::graphite::Image>(std::move(view), colorInfo.makeColorType(ct));
323 }
324 
ComputeSize(SkISize dimensions,const TextureInfo & info)325 size_t ComputeSize(SkISize dimensions, const TextureInfo& info) {
326     if (info.isMemoryless()) {
327         return 0;
328     }
329 
330     SkTextureCompressionType compression = info.compressionType();
331 
332     size_t colorSize = 0;
333 
334     if (compression != SkTextureCompressionType::kNone) {
335         colorSize =  SkCompressedFormatDataSize(compression,
336                                                 dimensions,
337                                                 info.mipmapped() == Mipmapped::kYes);
338     } else {
339         // TODO: Should we make sure the backends return zero here if the TextureInfo is for a
340         // memoryless texture?
341         size_t bytesPerPixel = info.bytesPerPixel();
342 
343         colorSize = (size_t)dimensions.width() * dimensions.height() * bytesPerPixel;
344     }
345 
346     size_t finalSize = colorSize * info.numSamples();
347 
348     if (info.mipmapped() == Mipmapped::kYes) {
349         finalSize += colorSize/3;
350     }
351     return finalSize;
352 }
353 
CopyAsDraw(Recorder * recorder,const SkImage * image,const SkIRect & subset,const SkColorInfo & dstColorInfo,Budgeted budgeted,Mipmapped mipmapped,SkBackingFit backingFit,std::string_view label)354 sk_sp<Image> CopyAsDraw(Recorder* recorder,
355                         const SkImage* image,
356                         const SkIRect& subset,
357                         const SkColorInfo& dstColorInfo,
358                         Budgeted budgeted,
359                         Mipmapped mipmapped,
360                         SkBackingFit backingFit,
361                         std::string_view label) {
362     SkColorType ct = recorder->priv().caps()->getRenderableColorType(dstColorInfo.colorType());
363     if (ct == kUnknown_SkColorType) {
364         return nullptr;
365     }
366     SkImageInfo dstInfo = SkImageInfo::Make(subset.size(),
367                                             dstColorInfo.makeColorType(ct)
368                                                         .makeAlphaType(kPremul_SkAlphaType));
369     // The surface goes out of scope when we return, so it can be scratch, but it may or may
370     // not be budgeted depending on how the copied image is used (or returned to the client).
371     auto surface = Surface::MakeScratch(recorder,
372                                         dstInfo,
373                                         std::move(label),
374                                         budgeted,
375                                         mipmapped,
376                                         backingFit);
377     if (!surface) {
378         return nullptr;
379     }
380 
381     SkPaint paint;
382     paint.setBlendMode(SkBlendMode::kSrc);
383     surface->getCanvas()->drawImage(image, -subset.left(), -subset.top(),
384                                     SkFilterMode::kNearest, &paint);
385     // And the image draw into `surface` is flushed when it goes out of scope
386     return surface->asImage();
387 }
388 
RescaleImage(Recorder * recorder,const SkImage * srcImage,SkIRect srcIRect,const SkImageInfo & dstInfo,SkImage::RescaleGamma rescaleGamma,SkImage::RescaleMode rescaleMode)389 sk_sp<SkImage> RescaleImage(Recorder* recorder,
390                             const SkImage* srcImage,
391                             SkIRect srcIRect,
392                             const SkImageInfo& dstInfo,
393                             SkImage::RescaleGamma rescaleGamma,
394                             SkImage::RescaleMode rescaleMode) {
395     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
396     TRACE_EVENT_INSTANT2("skia.gpu", "RescaleImage Src", TRACE_EVENT_SCOPE_THREAD,
397                          "width", srcIRect.width(), "height", srcIRect.height());
398     TRACE_EVENT_INSTANT2("skia.gpu", "RescaleImage Dst", TRACE_EVENT_SCOPE_THREAD,
399                          "width", dstInfo.width(), "height", dstInfo.height());
400 
401     // RescaleImage() should only be called when we already know that srcImage is graphite-backed
402     SkASSERT(srcImage && as_IB(srcImage)->isGraphiteBacked());
403 
404     // For now this needs to be texturable because we can't depend on copies to scale.
405     // NOTE: srcView may be empty if srcImage is YUVA.
406     const TextureProxyView srcView = AsView(srcImage);
407     if (srcView && !recorder->priv().caps()->isTexturable(srcView.proxy()->textureInfo())) {
408         // With the current definition of SkImage, this shouldn't happen. If we allow non-texturable
409         // formats for compute, we'll need to copy to a texturable format.
410         SkASSERT(false);
411         return nullptr;
412     }
413 
414     // make a Surface *exactly* matching dstInfo to rescale into
415     SkSurfaceProps surfaceProps = {};
416     sk_sp<SkSurface> dst = make_renderable_scratch_surface(recorder,
417                                                            dstInfo,
418                                                            SkBackingFit::kExact,
419                                                            "RescaleDstTexture",
420                                                            &surfaceProps);
421     if (!dst) {
422         return nullptr;
423     }
424 
425     SkRect srcRect = SkRect::Make(srcIRect);
426     SkRect dstRect = SkRect::Make(dstInfo.dimensions());
427 
428     SkISize finalSize = SkISize::Make(dstRect.width(), dstRect.height());
429     if (finalSize == srcIRect.size()) {
430         rescaleGamma = Image::RescaleGamma::kSrc;
431         rescaleMode = Image::RescaleMode::kNearest;
432     }
433 
434     // Within a rescaling pass tempInput is read from and tempOutput is written to.
435     // At the end of the pass tempOutput's texture is wrapped and assigned to tempInput.
436     sk_sp<SkImage> tempInput = sk_ref_sp(srcImage);
437     sk_sp<SkSurface> tempOutput;
438 
439     // Assume we should ignore the rescale linear request if the surface has no color space since
440     // it's unclear how we'd linearize from an unknown color space.
441     const SkImageInfo& srcImageInfo = srcImage->imageInfo();
442     if (rescaleGamma == Image::RescaleGamma::kLinear &&
443         srcImageInfo.colorSpace() &&
444         !srcImageInfo.colorSpace()->gammaIsLinear()) {
445         // Draw the src image into a new surface with linear gamma, and make that the new tempInput
446         sk_sp<SkColorSpace> linearGamma = srcImageInfo.colorSpace()->makeLinearGamma();
447         SkImageInfo gammaDstInfo = SkImageInfo::Make(srcIRect.size(),
448                                                      tempInput->imageInfo().colorType(),
449                                                      kPremul_SkAlphaType,
450                                                      std::move(linearGamma));
451         tempOutput = make_renderable_scratch_surface(recorder, gammaDstInfo, SkBackingFit::kApprox,
452                                                      "RescaleLinearGammaTexture", &surfaceProps);
453         if (!tempOutput) {
454             return nullptr;
455         }
456         SkCanvas* gammaDst = tempOutput->getCanvas();
457         SkRect gammaDstRect = SkRect::Make(srcIRect.size());
458 
459         SkPaint paint;
460         paint.setBlendMode(SkBlendMode::kSrc);
461         gammaDst->drawImageRect(tempInput, srcRect, gammaDstRect,
462                                 SkSamplingOptions(SkFilterMode::kNearest), &paint,
463                                 SkCanvas::kStrict_SrcRectConstraint);
464         tempInput = SkSurfaces::AsImage(std::move(tempOutput));
465         srcRect = gammaDstRect;
466     }
467 
468     SkImageInfo outImageInfo = tempInput->imageInfo().makeAlphaType(kPremul_SkAlphaType);
469     do {
470         SkISize nextDims = finalSize;
471         if (rescaleMode != Image::RescaleMode::kNearest &&
472             rescaleMode != Image::RescaleMode::kLinear) {
473             if (srcRect.width() > finalSize.width()) {
474                 nextDims.fWidth = std::max((srcRect.width() + 1)/2, (float)finalSize.width());
475             } else if (srcRect.width() < finalSize.width()) {
476                 nextDims.fWidth = std::min(srcRect.width()*2, (float)finalSize.width());
477             }
478             if (srcRect.height() > finalSize.height()) {
479                 nextDims.fHeight = std::max((srcRect.height() + 1)/2, (float)finalSize.height());
480             } else if (srcRect.height() < finalSize.height()) {
481                 nextDims.fHeight = std::min(srcRect.height()*2, (float)finalSize.height());
482             }
483         }
484 
485         SkRect stepDstRect;
486         if (nextDims == finalSize) {
487             tempOutput = dst;
488             stepDstRect = dstRect;
489         } else {
490             SkImageInfo nextInfo = outImageInfo.makeDimensions(nextDims);
491             tempOutput = make_renderable_scratch_surface(recorder, nextInfo, SkBackingFit::kApprox,
492                                                          "RescaleImageTempTexture", &surfaceProps);
493             if (!tempOutput) {
494                 return nullptr;
495             }
496             stepDstRect = SkRect::Make(tempOutput->imageInfo().dimensions());
497         }
498 
499         SkSamplingOptions samplingOptions;
500         if (rescaleMode == Image::RescaleMode::kRepeatedCubic) {
501             samplingOptions = SkSamplingOptions(SkCubicResampler::CatmullRom());
502         } else {
503             samplingOptions = (rescaleMode == Image::RescaleMode::kNearest) ?
504                                SkSamplingOptions(SkFilterMode::kNearest) :
505                                SkSamplingOptions(SkFilterMode::kLinear);
506         }
507         SkPaint paint;
508         paint.setBlendMode(SkBlendMode::kSrc);
509         tempOutput->getCanvas()->drawImageRect(tempInput, srcRect, stepDstRect, samplingOptions,
510                                                &paint, SkCanvas::kStrict_SrcRectConstraint);
511 
512         tempInput = SkSurfaces::AsImage(std::move(tempOutput));
513         srcRect = SkRect::Make(nextDims);
514     } while (srcRect.width() != finalSize.width() || srcRect.height() != finalSize.height());
515 
516     return SkSurfaces::AsImage(std::move(dst));
517 }
518 
GenerateMipmaps(Recorder * recorder,sk_sp<TextureProxy> texture,const SkColorInfo & colorInfo)519 bool GenerateMipmaps(Recorder* recorder,
520                      sk_sp<TextureProxy> texture,
521                      const SkColorInfo& colorInfo) {
522     constexpr SkSamplingOptions kSamplingOptions = SkSamplingOptions(SkFilterMode::kLinear);
523 
524     SkASSERT(texture->mipmapped() == Mipmapped::kYes);
525 
526     // Within a rescaling pass scratchImg is read from and a scratch surface is written to.
527     // At the end of the pass the scratch surface's texture is wrapped and assigned to scratchImg.
528 
529     // The scratch surface we create below will use a write swizzle derived from SkColorType and
530     // pixel format. We have to be consistent and swizzle on the read.
531     auto imgSwizzle = recorder->priv().caps()->getReadSwizzle(colorInfo.colorType(),
532                                                               texture->textureInfo());
533     sk_sp<SkImage> scratchImg(new Image(TextureProxyView(texture, imgSwizzle), colorInfo));
534 
535     SkISize srcSize = texture->dimensions();
536     const SkColorInfo outColorInfo = colorInfo.makeAlphaType(kPremul_SkAlphaType);
537 
538     // Alternate between two scratch surfaces to avoid reading from and writing to a texture in the
539     // same pass. The dimensions of the first usages of the two scratch textures will be 1/2 and 1/4
540     // those of the original texture, respectively.
541     sk_sp<Surface> scratchSurfaces[2];
542     for (int i = 0; i < 2; ++i) {
543         scratchSurfaces[i] = make_renderable_scratch_surface(
544                 recorder,
545                 SkImageInfo::Make(SkISize::Make(std::max(1, srcSize.width() >> (i + 1)),
546                                                 std::max(1, srcSize.height() >> (i + 1))),
547                                   outColorInfo),
548                 SkBackingFit::kApprox,
549                 "GenerateMipmapsScratchTexture");
550         if (!scratchSurfaces[i]) {
551             return false;
552         }
553     }
554 
555     for (int mipLevel = 1; srcSize.width() > 1 || srcSize.height() > 1; ++mipLevel) {
556         const SkISize dstSize = SkISize::Make(std::max(srcSize.width() >> 1, 1),
557                                               std::max(srcSize.height() >> 1, 1));
558 
559         Surface* scratchSurface = scratchSurfaces[(mipLevel - 1) & 1].get();
560 
561         SkPaint paint;
562         paint.setBlendMode(SkBlendMode::kSrc);
563         scratchSurface->getCanvas()->drawImageRect(scratchImg,
564                                                    SkRect::Make(srcSize),
565                                                    SkRect::Make(dstSize),
566                                                    kSamplingOptions,
567                                                    &paint,
568                                                    SkCanvas::kStrict_SrcRectConstraint);
569 
570         // Make sure the rescaling draw finishes before copying the results.
571         Flush(scratchSurface);
572 
573         sk_sp<CopyTextureToTextureTask> copyTask = CopyTextureToTextureTask::Make(
574                 static_cast<const Surface*>(scratchSurface)->readSurfaceView().refProxy(),
575                 SkIRect::MakeSize(dstSize),
576                 texture,
577                 {0, 0},
578                 mipLevel);
579         if (!copyTask) {
580             return false;
581         }
582         recorder->priv().add(std::move(copyTask));
583 
584         scratchImg = scratchSurface->asImage();
585         srcSize = dstSize;
586     }
587 
588     return true;
589 }
590 
GetGraphiteBacked(Recorder * recorder,const SkImage * imageIn,SkSamplingOptions sampling)591 std::pair<sk_sp<SkImage>, SkSamplingOptions> GetGraphiteBacked(Recorder* recorder,
592                                                                const SkImage* imageIn,
593                                                                SkSamplingOptions sampling) {
594     Mipmapped mipmapped = (sampling.mipmap != SkMipmapMode::kNone)
595                                      ? Mipmapped::kYes : Mipmapped::kNo;
596 
597     if (imageIn->dimensions().area() <= 1 && mipmapped == Mipmapped::kYes) {
598         mipmapped = Mipmapped::kNo;
599         sampling = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone);
600     }
601 
602     sk_sp<SkImage> result;
603     if (as_IB(imageIn)->isGraphiteBacked()) {
604         result = sk_ref_sp(imageIn);
605 
606         // If the preexisting Graphite-backed image doesn't have the required mipmaps we will drop
607         // down the sampling
608         if (mipmapped == Mipmapped::kYes && !result->hasMipmaps()) {
609             mipmapped = Mipmapped::kNo;
610             sampling = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone);
611         }
612     } else {
613         auto clientImageProvider = recorder->clientImageProvider();
614         result = clientImageProvider->findOrCreate(
615                 recorder, imageIn, {mipmapped == Mipmapped::kYes});
616 
617         if (!valid_client_provided_image(
618                     result.get(), imageIn, {mipmapped == Mipmapped::kYes})) {
619             // The client did not fulfill the ImageProvider contract so drop the image.
620             result = nullptr;
621         }
622     }
623 
624     if (sampling.isAniso() && result) {
625         sampling = SkSamplingPriv::AnisoFallback(result->hasMipmaps());
626     }
627 
628     return { result, sampling };
629 }
630 
AsView(const SkImage * image)631 TextureProxyView AsView(const SkImage* image) {
632     if (!image) {
633         return {};
634     }
635     if (!as_IB(image)->isGraphiteBacked()) {
636         return {};
637     }
638     // A YUVA image (even if backed by graphite textures) is not a single texture
639     if (as_IB(image)->isYUVA()) {
640         return {};
641     }
642 
643     auto gi = reinterpret_cast<const Image*>(image);
644     return gi->textureProxyView();
645 }
646 
ComputeShaderCoverageMaskTargetFormat(const Caps * caps)647 SkColorType ComputeShaderCoverageMaskTargetFormat(const Caps* caps) {
648     // GPU compute coverage mask renderers need to bind the mask texture as a storage binding, which
649     // support a limited set of color formats. In general, we use RGBA8 if Alpha8 can't be
650     // supported.
651     if (caps->isStorage(caps->getDefaultStorageTextureInfo(kAlpha_8_SkColorType))) {
652         return kAlpha_8_SkColorType;
653     }
654     return kRGBA_8888_SkColorType;
655 }
656 
657 } // namespace skgpu::graphite
658 
659 namespace skif {
660 
661 namespace {
662 
663 // TODO(michaelludwig): The skgpu::BlurUtils effects will be migrated to src/core to implement a
664 // shader BlurEngine that can be shared by rastr, Ganesh, and Graphite. This is blocked by having
665 // skif::FilterResult handle the resizing to the max supported sigma.
666 class GraphiteBackend :
667         public Backend,
668         private SkShaderBlurAlgorithm,
669         private SkBlurEngine {
670 public:
671 
GraphiteBackend(skgpu::graphite::Recorder * recorder,const SkSurfaceProps & surfaceProps,SkColorType colorType)672     GraphiteBackend(skgpu::graphite::Recorder* recorder,
673                     const SkSurfaceProps& surfaceProps,
674                     SkColorType colorType)
675             : Backend(SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize),
676                       surfaceProps, colorType)
677             , fRecorder(recorder) {}
678 
679     // Backend
makeDevice(SkISize size,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props) const680     sk_sp<SkDevice> makeDevice(SkISize size,
681                                sk_sp<SkColorSpace> colorSpace,
682                                const SkSurfaceProps* props) const override {
683         SkImageInfo imageInfo = SkImageInfo::Make(size,
684                                                   this->colorType(),
685                                                   kPremul_SkAlphaType,
686                                                   std::move(colorSpace));
687         return skgpu::graphite::Device::Make(fRecorder,
688                                              imageInfo,
689                                              skgpu::Budgeted::kYes,
690                                              skgpu::Mipmapped::kNo,
691                                              SkBackingFit::kApprox,
692                                              props ? *props : this->surfaceProps(),
693                                              skgpu::graphite::LoadOp::kDiscard,
694                                              "ImageFilterResult");
695     }
696 
makeImage(const SkIRect & subset,sk_sp<SkImage> image) const697     sk_sp<SkSpecialImage> makeImage(const SkIRect& subset, sk_sp<SkImage> image) const override {
698         return SkSpecialImages::MakeGraphite(fRecorder, subset, image, this->surfaceProps());
699     }
700 
getCachedBitmap(const SkBitmap & data) const701     sk_sp<SkImage> getCachedBitmap(const SkBitmap& data) const override {
702         auto proxy = skgpu::graphite::RecorderPriv::CreateCachedProxy(fRecorder, data,
703                                                                       "ImageFilterCachedBitmap");
704         if (!proxy) {
705             return nullptr;
706         }
707 
708         const SkColorInfo& colorInfo = data.info().colorInfo();
709         skgpu::Swizzle swizzle = fRecorder->priv().caps()->getReadSwizzle(colorInfo.colorType(),
710                                                                           proxy->textureInfo());
711         return sk_make_sp<skgpu::graphite::Image>(
712                 skgpu::graphite::TextureProxyView(std::move(proxy), swizzle),
713                 colorInfo);
714     }
715 
getBlurEngine() const716     const SkBlurEngine* getBlurEngine() const override { return this; }
717 
718     // SkBlurEngine
findAlgorithm(SkSize sigma,SkColorType colorType) const719     const SkBlurEngine::Algorithm* findAlgorithm(SkSize sigma,
720                                                  SkColorType colorType) const override {
721         // The runtime effect blurs handle all tilemodes and color types
722         return this;
723     }
724 
useLegacyFilterResultBlur() const725     bool useLegacyFilterResultBlur() const override { return false; }
726 
727     // SkShaderBlurAlgorithm
makeDevice(const SkImageInfo & imageInfo) const728     sk_sp<SkDevice> makeDevice(const SkImageInfo& imageInfo) const override {
729         return skgpu::graphite::Device::Make(fRecorder,
730                                              imageInfo,
731                                              skgpu::Budgeted::kYes,
732                                              skgpu::Mipmapped::kNo,
733                                              SkBackingFit::kApprox,
734                                              this->surfaceProps(),
735                                              skgpu::graphite::LoadOp::kDiscard,
736                                              "EvalBlurTexture");
737     }
738 
739 private:
740     skgpu::graphite::Recorder* fRecorder;
741 };
742 
743 } // anonymous namespace
744 
MakeGraphiteBackend(skgpu::graphite::Recorder * recorder,const SkSurfaceProps & surfaceProps,SkColorType colorType)745 sk_sp<Backend> MakeGraphiteBackend(skgpu::graphite::Recorder* recorder,
746                                    const SkSurfaceProps& surfaceProps,
747                                    SkColorType colorType) {
748     SkASSERT(recorder);
749     return sk_make_sp<GraphiteBackend>(recorder, surfaceProps, colorType);
750 }
751 
752 }  // namespace skif
753