• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #if defined(SK_USE_LEGACY_BLUR_GRAPHITE)
112 
eval_blur(Recorder * recorder,sk_sp<SkShader> blurEffect,const SkIRect & dstRect,SkColorType colorType,sk_sp<SkColorSpace> outCS,const SkSurfaceProps & outProps)113 sk_sp<SkSpecialImage> eval_blur(Recorder* recorder,
114                                 sk_sp<SkShader> blurEffect,
115                                 const SkIRect& dstRect,
116                                 SkColorType colorType,
117                                 sk_sp<SkColorSpace> outCS,
118                                 const SkSurfaceProps& outProps) {
119     SkImageInfo outII = SkImageInfo::Make({dstRect.width(), dstRect.height()},
120                                           colorType, kPremul_SkAlphaType, std::move(outCS));
121     // Protected-ness is pulled off of the recorder
122     auto device = Device::Make(recorder,
123                                outII,
124                                Budgeted::kYes,
125                                Mipmapped::kNo,
126                                SkBackingFit::kApprox,
127                                outProps,
128                                LoadOp::kDiscard,
129                                "EvalBlurTexture");
130     if (!device) {
131         return nullptr;
132     }
133 
134     // TODO(b/294102201): This is very much like AutoSurface in SkImageFilterTypes.cpp
135     SkIRect subset = SkIRect::MakeSize(dstRect.size());
136     device->clipRect(SkRect::Make(subset), SkClipOp::kIntersect, /*aa=*/false);
137     device->setLocalToDevice(SkM44::Translate(-dstRect.left(), -dstRect.top()));
138     SkPaint paint;
139     paint.setBlendMode(SkBlendMode::kSrc);
140     paint.setShader(std::move(blurEffect));
141     device->drawPaint(paint);
142     return device->snapSpecial(subset);
143 }
144 
blur_2d(Recorder * recorder,SkSize sigma,SkISize radii,sk_sp<SkSpecialImage> input,const SkIRect & srcRect,SkTileMode tileMode,const SkIRect & dstRect,sk_sp<SkColorSpace> outCS,const SkSurfaceProps & outProps)145 sk_sp<SkSpecialImage> blur_2d(Recorder* recorder,
146                               SkSize sigma,
147                               SkISize radii,
148                               sk_sp<SkSpecialImage> input,
149                               const SkIRect& srcRect,
150                               SkTileMode tileMode,
151                               const SkIRect& dstRect,
152                               sk_sp<SkColorSpace> outCS,
153                               const SkSurfaceProps& outProps) {
154     std::array<SkV4, kMaxBlurSamples/4> kernel;
155     std::array<SkV4, kMaxBlurSamples/2> offsets;
156     Compute2DBlurKernel(sigma, radii, kernel);
157     Compute2DBlurOffsets(radii, offsets);
158 
159     SkRuntimeShaderBuilder builder{sk_ref_sp(GetBlur2DEffect(radii))};
160     builder.uniform("kernel") = kernel;
161     builder.uniform("offsets") = offsets;
162     // TODO(b/294102201): This is very much like FilterResult::asShader()...
163     builder.child("child") =
164             input->makeSubset(srcRect)->asShader(tileMode,
165                                                  SkFilterMode::kNearest,
166                                                  SkMatrix::Translate(srcRect.left(),srcRect.top()));
167 
168     return eval_blur(recorder, builder.makeShader(), dstRect,
169                      input->colorType(), std::move(outCS), outProps);
170 }
171 
blur_1d(Recorder * recorder,float sigma,int radius,SkV2 dir,sk_sp<SkSpecialImage> input,SkIRect srcRect,SkTileMode tileMode,SkIRect dstRect,sk_sp<SkColorSpace> outCS,const SkSurfaceProps & outProps)172 sk_sp<SkSpecialImage> blur_1d(Recorder* recorder,
173                               float sigma,
174                               int radius,
175                               SkV2 dir,
176                               sk_sp<SkSpecialImage> input,
177                               SkIRect srcRect,
178                               SkTileMode tileMode,
179                               SkIRect dstRect,
180                               sk_sp<SkColorSpace> outCS,
181                               const SkSurfaceProps& outProps) {
182     std::array<SkV4, kMaxBlurSamples/2> offsetsAndKernel;
183     Compute1DBlurLinearKernel(sigma, radius, offsetsAndKernel);
184 
185     SkRuntimeShaderBuilder builder{sk_ref_sp(GetLinearBlur1DEffect(radius))};
186     builder.uniform("offsetsAndKernel") = offsetsAndKernel;
187     builder.uniform("dir") = dir;
188     // TODO(b/294102201): This is very much like FilterResult::asShader()...
189     builder.child("child") =
190             input->makeSubset(srcRect)->asShader(tileMode,
191                                                  SkFilterMode::kLinear,
192                                                  SkMatrix::Translate(srcRect.left(),srcRect.top()));
193 
194     return eval_blur(recorder, builder.makeShader(), dstRect,
195                      input->colorType(), std::move(outCS), outProps);
196 }
197 
blur_impl(Recorder * recorder,SkSize sigma,sk_sp<SkSpecialImage> input,SkIRect srcRect,SkTileMode tileMode,SkIRect dstRect,sk_sp<SkColorSpace> outCS,const SkSurfaceProps & outProps)198 sk_sp<SkSpecialImage> blur_impl(Recorder* recorder,
199                                 SkSize sigma,
200                                 sk_sp<SkSpecialImage> input,
201                                 SkIRect srcRect,
202                                 SkTileMode tileMode,
203                                 SkIRect dstRect,
204                                 sk_sp<SkColorSpace> outCS,
205                                 const SkSurfaceProps& outProps) {
206     // See if we can do a blur on the original resolution image
207     if (sigma.width() <= kMaxLinearBlurSigma &&
208         sigma.height() <= kMaxLinearBlurSigma) {
209         int radiusX = BlurSigmaRadius(sigma.width());
210         int radiusY = BlurSigmaRadius(sigma.height());
211         const int kernelArea = BlurKernelWidth(radiusX) * BlurKernelWidth(radiusY);
212         if (kernelArea <= kMaxBlurSamples && radiusX > 0 && radiusY > 0) {
213             // Use a single-pass 2D kernel if it fits and isn't just 1D already
214             return blur_2d(recorder, sigma, {radiusX, radiusY}, std::move(input), srcRect, tileMode,
215                            dstRect, std::move(outCS), outProps);
216         } else {
217             // Use two passes of a 1D kernel (one per axis).
218             if (radiusX > 0) {
219                 SkIRect intermediateDstRect = dstRect;
220                 if (radiusY > 0) {
221                     // Outset the output size of dstRect by the radius required for the next Y pass
222                     intermediateDstRect.outset(0, radiusY);
223                     if (!intermediateDstRect.intersect(srcRect.makeOutset(radiusX, radiusY))) {
224                         return nullptr;
225                     }
226                 }
227 
228                 input = blur_1d(recorder, sigma.width(), radiusX, {1.f, 0.f},
229                                 std::move(input), srcRect, tileMode, intermediateDstRect,
230                                 outCS, outProps);
231                 if (!input) {
232                     return nullptr;
233                 }
234                 srcRect = SkIRect::MakeWH(input->width(), input->height());
235                 dstRect.offset(-intermediateDstRect.left(), -intermediateDstRect.top());
236             }
237 
238             if (radiusY > 0) {
239                 input = blur_1d(recorder, sigma.height(), radiusY, {0.f, 1.f},
240                                 std::move(input), srcRect, tileMode, dstRect, outCS, outProps);
241             }
242 
243             return input;
244         }
245     } else {
246         // Rescale the source image, blur that with a reduced sigma, and then upscale back to the
247         // dstRect dimensions.
248         // TODO(b/294102201): Share rescaling logic with GrBlurUtils::GaussianBlur.
249         float sx = sigma.width() > kMaxLinearBlurSigma
250                 ? (kMaxLinearBlurSigma / sigma.width()) : 1.f;
251         float sy = sigma.height() > kMaxLinearBlurSigma
252                 ? (kMaxLinearBlurSigma / sigma.height()) : 1.f;
253 
254         int targetSrcWidth = sk_float_ceil2int(srcRect.width() * sx);
255         int targetSrcHeight = sk_float_ceil2int(srcRect.height() * sy);
256 
257         auto inputImage = input->asImage();
258         // TODO(b/288902559): Support approx fit backings for the target of a rescale
259         // TODO(b/294102201): Be smarter about downscaling when there are actual tilemodes to apply
260         // to the image.
261         auto scaledInput = RescaleImage(
262                 recorder,
263                 inputImage.get(),
264                 srcRect.makeOffset(input->subset().topLeft()),
265                 inputImage->imageInfo().makeWH(targetSrcWidth, targetSrcHeight),
266                 SkImage::RescaleGamma::kLinear,
267                 SkImage::RescaleMode::kRepeatedLinear);
268         if (!scaledInput) {
269             return nullptr;
270         }
271 
272         // Calculate a scaled dstRect to match (0,0,targetSrcWidth,targetSrcHeight) as srcRect.
273         SkIRect targetDstRect = SkRect::MakeXYWH((dstRect.left() - srcRect.left()) * sx,
274                                                  (dstRect.top() - srcRect.top()) * sy,
275                                                  dstRect.width()*sx,
276                                                  dstRect.height()*sy).roundOut();
277         SkIRect targetSrcRect = SkIRect::MakeWH(targetSrcWidth, targetSrcHeight);
278         // Blur with pinned sigmas. If the sigma was less than the max, that axis of the image was
279         // not scaled so we can use the original. If it was greater than the max, the scale factor
280         // should have taken it the max supported sigma (ignoring the effect of rounding out the
281         // source bounds).
282         auto scaledOutput = blur_impl(
283                 recorder,
284                 {std::min(sigma.width(), kMaxLinearBlurSigma),
285                  std::min(sigma.height(), kMaxLinearBlurSigma)},
286                 SkSpecialImages::MakeGraphite(recorder,
287                                               targetSrcRect,
288                                               std::move(scaledInput),
289                                               outProps),
290                 targetSrcRect,
291                 tileMode,
292                 targetDstRect,
293                 outCS,
294                 outProps);
295         if (!scaledOutput) {
296             return nullptr;
297         }
298 
299         // TODO: Pass out the upscaling transform for skif::FilterResult to hold on to.
300         auto scaledOutputImage = scaledOutput->asImage();
301         auto outputImage = RescaleImage(
302                 recorder,
303                 scaledOutputImage.get(),
304                 scaledOutput->subset(),
305                 scaledOutputImage->imageInfo().makeWH(dstRect.width(), dstRect.height()),
306                 SkImage::RescaleGamma::kLinear,
307                 SkImage::RescaleMode::kLinear);
308         if (!outputImage) {
309             return nullptr;
310         }
311 
312         SkIRect outputDstRect = outputImage->bounds();
313         return SkSpecialImages::MakeGraphite(recorder,
314                                              outputDstRect,
315                                              std::move(outputImage),
316                                              outProps);
317     }
318 }
319 
320 #endif // SK_USE_LEGACY_BLUR_GRAPHITE
321 
322 // This class is the lazy instantiation callback for promise images. It manages calling the
323 // client's Fulfill, ImageRelease, and TextureRelease procs.
324 class PromiseLazyInstantiateCallback {
325 public:
PromiseLazyInstantiateCallback(sk_sp<RefCntedCallback> releaseHelper,GraphitePromiseTextureFulfillProc fulfillProc,GraphitePromiseTextureFulfillContext fulfillContext,GraphitePromiseTextureReleaseProc textureReleaseProc,std::string_view label)326     PromiseLazyInstantiateCallback(sk_sp<RefCntedCallback> releaseHelper,
327                                    GraphitePromiseTextureFulfillProc fulfillProc,
328                                    GraphitePromiseTextureFulfillContext fulfillContext,
329                                    GraphitePromiseTextureReleaseProc textureReleaseProc,
330                                    std::string_view label)
331             : fReleaseHelper(std::move(releaseHelper))
332             , fFulfillProc(fulfillProc)
333             , fFulfillContext(fulfillContext)
334             , fTextureReleaseProc(textureReleaseProc)
335             , fLabel(label) {
336     }
337     PromiseLazyInstantiateCallback(PromiseLazyInstantiateCallback&&) = default;
PromiseLazyInstantiateCallback(const PromiseLazyInstantiateCallback &)338     PromiseLazyInstantiateCallback(const PromiseLazyInstantiateCallback&) {
339         // Because we get wrapped in std::function we must be copyable. But we should never
340         // be copied.
341         SkASSERT(false);
342     }
343     PromiseLazyInstantiateCallback& operator=(PromiseLazyInstantiateCallback&&) = default;
operator =(const PromiseLazyInstantiateCallback &)344     PromiseLazyInstantiateCallback& operator=(const PromiseLazyInstantiateCallback&) {
345         SkASSERT(false);
346         return *this;
347     }
348 
operator ()(ResourceProvider * resourceProvider)349     sk_sp<Texture> operator()(ResourceProvider* resourceProvider) {
350         // Invoke the fulfill proc to get the promised backend texture.
351         auto [ backendTexture, textureReleaseCtx ] = fFulfillProc(fFulfillContext);
352         if (!backendTexture.isValid()) {
353             SKGPU_LOG_W("FulfillProc returned an invalid backend texture");
354             return nullptr;
355         }
356 
357         sk_sp<RefCntedCallback> textureReleaseCB = RefCntedCallback::Make(fTextureReleaseProc,
358                                                                           textureReleaseCtx);
359 
360         sk_sp<Texture> texture = resourceProvider->createWrappedTexture(backendTexture,
361                                                                         std::move(fLabel));
362         if (!texture) {
363             SKGPU_LOG_W("Failed to wrap BackendTexture returned by fulfill proc");
364             return nullptr;
365         }
366         texture->setReleaseCallback(std::move(textureReleaseCB));
367         return texture;
368     }
369 
370 private:
371     sk_sp<RefCntedCallback> fReleaseHelper;
372     GraphitePromiseTextureFulfillProc fFulfillProc;
373     GraphitePromiseTextureFulfillContext fFulfillContext;
374     GraphitePromiseTextureReleaseProc fTextureReleaseProc;
375     std::string fLabel;
376 };
377 
378 } // anonymous namespace
379 
MakeBitmapProxyView(Recorder * recorder,const SkBitmap & bitmap,sk_sp<SkMipmap> mipmapsIn,Mipmapped mipmapped,Budgeted budgeted,std::string_view label)380 std::tuple<TextureProxyView, SkColorType> MakeBitmapProxyView(Recorder* recorder,
381                                                               const SkBitmap& bitmap,
382                                                               sk_sp<SkMipmap> mipmapsIn,
383                                                               Mipmapped mipmapped,
384                                                               Budgeted budgeted,
385                                                               std::string_view label) {
386     // Adjust params based on input and Caps
387     const Caps* caps = recorder->priv().caps();
388     SkColorType ct = bitmap.info().colorType();
389 
390     if (bitmap.dimensions().area() <= 1) {
391         mipmapped = Mipmapped::kNo;
392     }
393 
394     Protected isProtected = recorder->priv().isProtected();
395     auto textureInfo = caps->getDefaultSampledTextureInfo(ct, mipmapped, isProtected,
396                                                           Renderable::kNo);
397     if (!textureInfo.isValid()) {
398         ct = kRGBA_8888_SkColorType;
399         textureInfo = caps->getDefaultSampledTextureInfo(ct, mipmapped, isProtected,
400                                                          Renderable::kNo);
401     }
402     SkASSERT(textureInfo.isValid());
403 
404     // Convert bitmap to texture colortype if necessary
405     SkBitmap bmpToUpload;
406     if (ct != bitmap.info().colorType()) {
407         if (!bmpToUpload.tryAllocPixels(bitmap.info().makeColorType(ct)) ||
408             !bitmap.readPixels(bmpToUpload.pixmap())) {
409             return {};
410         }
411         bmpToUpload.setImmutable();
412     } else {
413         bmpToUpload = bitmap;
414     }
415 
416     if (!SkImageInfoIsValid(bmpToUpload.info())) {
417         return {};
418     }
419 
420     int mipLevelCount = (mipmapped == Mipmapped::kYes) ?
421             SkMipmap::ComputeLevelCount(bitmap.width(), bitmap.height()) + 1 : 1;
422 
423 
424     // setup MipLevels
425     sk_sp<SkMipmap> mipmaps;
426     std::vector<MipLevel> texels;
427     if (mipLevelCount == 1) {
428         texels.resize(mipLevelCount);
429         texels[0].fPixels = bmpToUpload.getPixels();
430         texels[0].fRowBytes = bmpToUpload.rowBytes();
431     } else {
432         mipmaps = SkToBool(mipmapsIn)
433                           ? mipmapsIn
434                           : sk_sp<SkMipmap>(SkMipmap::Build(bmpToUpload.pixmap(), nullptr));
435         if (!mipmaps) {
436             return {};
437         }
438 
439         SkASSERT(mipLevelCount == mipmaps->countLevels() + 1);
440         texels.resize(mipLevelCount);
441 
442         texels[0].fPixels = bmpToUpload.getPixels();
443         texels[0].fRowBytes = bmpToUpload.rowBytes();
444 
445         for (int i = 1; i < mipLevelCount; ++i) {
446             SkMipmap::Level generatedMipLevel;
447             mipmaps->getLevel(i - 1, &generatedMipLevel);
448             texels[i].fPixels = generatedMipLevel.fPixmap.addr();
449             texels[i].fRowBytes = generatedMipLevel.fPixmap.rowBytes();
450             SkASSERT(texels[i].fPixels);
451             SkASSERT(generatedMipLevel.fPixmap.colorType() == bmpToUpload.colorType());
452         }
453     }
454 
455     // Create proxy
456     sk_sp<TextureProxy> proxy = TextureProxy::Make(caps,
457                                                    recorder->priv().resourceProvider(),
458                                                    bmpToUpload.dimensions(),
459                                                    textureInfo,
460                                                    std::move(label),
461                                                    budgeted);
462     if (!proxy) {
463         return {};
464     }
465     SkASSERT(caps->areColorTypeAndTextureInfoCompatible(ct, proxy->textureInfo()));
466     SkASSERT(mipmapped == Mipmapped::kNo || proxy->mipmapped() == Mipmapped::kYes);
467 
468     // Src and dst colorInfo are the same
469     const SkColorInfo& colorInfo = bmpToUpload.info().colorInfo();
470     // Add UploadTask to Recorder
471     UploadInstance upload = UploadInstance::Make(
472             recorder, proxy, colorInfo, colorInfo, texels,
473             SkIRect::MakeSize(bmpToUpload.dimensions()), std::make_unique<ImageUploadContext>());
474     if (!upload.isValid()) {
475         SKGPU_LOG_E("MakeBitmapProxyView: Could not create UploadInstance");
476         return {};
477     }
478     recorder->priv().add(UploadTask::Make(std::move(upload)));
479 
480     Swizzle swizzle = caps->getReadSwizzle(ct, textureInfo);
481     // If the color type is alpha-only, propagate the alpha value to the other channels.
482     if (SkColorTypeIsAlphaOnly(colorInfo.colorType())) {
483         swizzle = Swizzle::Concat(swizzle, Swizzle("aaaa"));
484     }
485     return {{std::move(proxy), swizzle}, ct};
486 }
487 
MakePromiseImageLazyProxy(const Caps * caps,SkISize dimensions,TextureInfo textureInfo,Volatile isVolatile,sk_sp<RefCntedCallback> releaseHelper,GraphitePromiseTextureFulfillProc fulfillProc,GraphitePromiseTextureFulfillContext fulfillContext,GraphitePromiseTextureReleaseProc textureReleaseProc,std::string_view label)488 sk_sp<TextureProxy> MakePromiseImageLazyProxy(
489         const Caps* caps,
490         SkISize dimensions,
491         TextureInfo textureInfo,
492         Volatile isVolatile,
493         sk_sp<RefCntedCallback> releaseHelper,
494         GraphitePromiseTextureFulfillProc fulfillProc,
495         GraphitePromiseTextureFulfillContext fulfillContext,
496         GraphitePromiseTextureReleaseProc textureReleaseProc,
497         std::string_view label) {
498     SkASSERT(!dimensions.isEmpty());
499     SkASSERT(releaseHelper);
500 
501     if (!fulfillProc) {
502         return nullptr;
503     }
504 
505     PromiseLazyInstantiateCallback callback{std::move(releaseHelper), fulfillProc,
506                                             fulfillContext, textureReleaseProc, std::move(label)};
507     // Proxies for promise images are assumed to always be destined for a client's SkImage so
508     // are never considered budgeted.
509     return TextureProxy::MakeLazy(caps, dimensions, textureInfo, Budgeted::kNo, isVolatile,
510                                   std::move(callback));
511 }
512 
MakeFromBitmap(Recorder * recorder,const SkColorInfo & colorInfo,const SkBitmap & bitmap,sk_sp<SkMipmap> mipmaps,Budgeted budgeted,SkImage::RequiredProperties requiredProps,std::string_view label)513 sk_sp<SkImage> MakeFromBitmap(Recorder* recorder,
514                               const SkColorInfo& colorInfo,
515                               const SkBitmap& bitmap,
516                               sk_sp<SkMipmap> mipmaps,
517                               Budgeted budgeted,
518                               SkImage::RequiredProperties requiredProps,
519                               std::string_view label) {
520     auto mm = requiredProps.fMipmapped ? Mipmapped::kYes : Mipmapped::kNo;
521     auto [view, ct] = MakeBitmapProxyView(recorder,
522                                           bitmap,
523                                           std::move(mipmaps),
524                                           mm,
525                                           budgeted,
526                                           std::move(label));
527     if (!view) {
528         return nullptr;
529     }
530 
531     SkASSERT(!requiredProps.fMipmapped || view.proxy()->mipmapped() == Mipmapped::kYes);
532     return sk_make_sp<skgpu::graphite::Image>(std::move(view), colorInfo.makeColorType(ct));
533 }
534 
ComputeSize(SkISize dimensions,const TextureInfo & info)535 size_t ComputeSize(SkISize dimensions,
536                    const TextureInfo& info) {
537 
538     SkTextureCompressionType compression = info.compressionType();
539 
540     size_t colorSize = 0;
541 
542     if (compression != SkTextureCompressionType::kNone) {
543         colorSize =  SkCompressedFormatDataSize(compression,
544                                                 dimensions,
545                                                 info.mipmapped() == Mipmapped::kYes);
546     } else {
547         // TODO: Should we make sure the backends return zero here if the TextureInfo is for a
548         // memoryless texture?
549         size_t bytesPerPixel = info.bytesPerPixel();
550 
551         colorSize = (size_t)dimensions.width() * dimensions.height() * bytesPerPixel;
552     }
553 
554     size_t finalSize = colorSize * info.numSamples();
555 
556     if (info.mipmapped() == Mipmapped::kYes) {
557         finalSize += colorSize/3;
558     }
559     return finalSize;
560 }
561 
CopyAsDraw(Recorder * recorder,const SkImage * image,const SkIRect & subset,const SkColorInfo & dstColorInfo,Budgeted budgeted,Mipmapped mipmapped,SkBackingFit backingFit,std::string_view label)562 sk_sp<Image> CopyAsDraw(Recorder* recorder,
563                         const SkImage* image,
564                         const SkIRect& subset,
565                         const SkColorInfo& dstColorInfo,
566                         Budgeted budgeted,
567                         Mipmapped mipmapped,
568                         SkBackingFit backingFit,
569                         std::string_view label) {
570     SkColorType ct = recorder->priv().caps()->getRenderableColorType(dstColorInfo.colorType());
571     if (ct == kUnknown_SkColorType) {
572         return nullptr;
573     }
574     SkImageInfo dstInfo = SkImageInfo::Make(subset.size(),
575                                             dstColorInfo.makeColorType(ct)
576                                                         .makeAlphaType(kPremul_SkAlphaType));
577     // The surface goes out of scope when we return, so it can be scratch, but it may or may
578     // not be budgeted depending on how the copied image is used (or returned to the client).
579     auto surface = Surface::MakeScratch(recorder,
580                                         dstInfo,
581                                         std::move(label),
582                                         budgeted,
583                                         mipmapped,
584                                         backingFit);
585     if (!surface) {
586         return nullptr;
587     }
588 
589     SkPaint paint;
590     paint.setBlendMode(SkBlendMode::kSrc);
591     surface->getCanvas()->drawImage(image, -subset.left(), -subset.top(),
592                                     SkFilterMode::kNearest, &paint);
593     // And the image draw into `surface` is flushed when it goes out of scope
594     return surface->asImage();
595 }
596 
RescaleImage(Recorder * recorder,const SkImage * srcImage,SkIRect srcIRect,const SkImageInfo & dstInfo,SkImage::RescaleGamma rescaleGamma,SkImage::RescaleMode rescaleMode)597 sk_sp<SkImage> RescaleImage(Recorder* recorder,
598                             const SkImage* srcImage,
599                             SkIRect srcIRect,
600                             const SkImageInfo& dstInfo,
601                             SkImage::RescaleGamma rescaleGamma,
602                             SkImage::RescaleMode rescaleMode) {
603     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
604     TRACE_EVENT_INSTANT2("skia.gpu", "RescaleImage Src", TRACE_EVENT_SCOPE_THREAD,
605                          "width", srcIRect.width(), "height", srcIRect.height());
606     TRACE_EVENT_INSTANT2("skia.gpu", "RescaleImage Dst", TRACE_EVENT_SCOPE_THREAD,
607                          "width", dstInfo.width(), "height", dstInfo.height());
608 
609     // RescaleImage() should only be called when we already know that srcImage is graphite-backed
610     SkASSERT(srcImage && as_IB(srcImage)->isGraphiteBacked());
611 
612     // For now this needs to be texturable because we can't depend on copies to scale.
613     // NOTE: srcView may be empty if srcImage is YUVA.
614     const TextureProxyView srcView = AsView(srcImage);
615     if (srcView && !recorder->priv().caps()->isTexturable(srcView.proxy()->textureInfo())) {
616         // With the current definition of SkImage, this shouldn't happen. If we allow non-texturable
617         // formats for compute, we'll need to copy to a texturable format.
618         SkASSERT(false);
619         return nullptr;
620     }
621 
622     // make a Surface *exactly* matching dstInfo to rescale into
623     SkSurfaceProps surfaceProps = {};
624     sk_sp<SkSurface> dst = make_renderable_scratch_surface(recorder,
625                                                            dstInfo,
626                                                            SkBackingFit::kExact,
627                                                            "RescaleDstTexture",
628                                                            &surfaceProps);
629     if (!dst) {
630         return nullptr;
631     }
632 
633     SkRect srcRect = SkRect::Make(srcIRect);
634     SkRect dstRect = SkRect::Make(dstInfo.dimensions());
635 
636     SkISize finalSize = SkISize::Make(dstRect.width(), dstRect.height());
637     if (finalSize == srcIRect.size()) {
638         rescaleGamma = Image::RescaleGamma::kSrc;
639         rescaleMode = Image::RescaleMode::kNearest;
640     }
641 
642     // Within a rescaling pass tempInput is read from and tempOutput is written to.
643     // At the end of the pass tempOutput's texture is wrapped and assigned to tempInput.
644     sk_sp<SkImage> tempInput = sk_ref_sp(srcImage);
645     sk_sp<SkSurface> tempOutput;
646 
647     // Assume we should ignore the rescale linear request if the surface has no color space since
648     // it's unclear how we'd linearize from an unknown color space.
649     const SkImageInfo& srcImageInfo = srcImage->imageInfo();
650     if (rescaleGamma == Image::RescaleGamma::kLinear &&
651         srcImageInfo.colorSpace() &&
652         !srcImageInfo.colorSpace()->gammaIsLinear()) {
653         // Draw the src image into a new surface with linear gamma, and make that the new tempInput
654         sk_sp<SkColorSpace> linearGamma = srcImageInfo.colorSpace()->makeLinearGamma();
655         SkImageInfo gammaDstInfo = SkImageInfo::Make(srcIRect.size(),
656                                                      tempInput->imageInfo().colorType(),
657                                                      kPremul_SkAlphaType,
658                                                      std::move(linearGamma));
659         tempOutput = make_renderable_scratch_surface(recorder, gammaDstInfo, SkBackingFit::kApprox,
660                                                      "RescaleLinearGammaTexture", &surfaceProps);
661         if (!tempOutput) {
662             return nullptr;
663         }
664         SkCanvas* gammaDst = tempOutput->getCanvas();
665         SkRect gammaDstRect = SkRect::Make(srcIRect.size());
666 
667         SkPaint paint;
668         paint.setBlendMode(SkBlendMode::kSrc);
669         gammaDst->drawImageRect(tempInput, srcRect, gammaDstRect,
670                                 SkSamplingOptions(SkFilterMode::kNearest), &paint,
671                                 SkCanvas::kStrict_SrcRectConstraint);
672         tempInput = SkSurfaces::AsImage(std::move(tempOutput));
673         srcRect = gammaDstRect;
674     }
675 
676     SkImageInfo outImageInfo = tempInput->imageInfo().makeAlphaType(kPremul_SkAlphaType);
677     do {
678         SkISize nextDims = finalSize;
679         if (rescaleMode != Image::RescaleMode::kNearest &&
680             rescaleMode != Image::RescaleMode::kLinear) {
681             if (srcRect.width() > finalSize.width()) {
682                 nextDims.fWidth = std::max((srcRect.width() + 1)/2, (float)finalSize.width());
683             } else if (srcRect.width() < finalSize.width()) {
684                 nextDims.fWidth = std::min(srcRect.width()*2, (float)finalSize.width());
685             }
686             if (srcRect.height() > finalSize.height()) {
687                 nextDims.fHeight = std::max((srcRect.height() + 1)/2, (float)finalSize.height());
688             } else if (srcRect.height() < finalSize.height()) {
689                 nextDims.fHeight = std::min(srcRect.height()*2, (float)finalSize.height());
690             }
691         }
692 
693         SkRect stepDstRect;
694         if (nextDims == finalSize) {
695             tempOutput = dst;
696             stepDstRect = dstRect;
697         } else {
698             SkImageInfo nextInfo = outImageInfo.makeDimensions(nextDims);
699             tempOutput = make_renderable_scratch_surface(recorder, nextInfo, SkBackingFit::kApprox,
700                                                          "RescaleImageTempTexture", &surfaceProps);
701             if (!tempOutput) {
702                 return nullptr;
703             }
704             stepDstRect = SkRect::Make(tempOutput->imageInfo().dimensions());
705         }
706 
707         SkSamplingOptions samplingOptions;
708         if (rescaleMode == Image::RescaleMode::kRepeatedCubic) {
709             samplingOptions = SkSamplingOptions(SkCubicResampler::CatmullRom());
710         } else {
711             samplingOptions = (rescaleMode == Image::RescaleMode::kNearest) ?
712                                SkSamplingOptions(SkFilterMode::kNearest) :
713                                SkSamplingOptions(SkFilterMode::kLinear);
714         }
715         SkPaint paint;
716         paint.setBlendMode(SkBlendMode::kSrc);
717         tempOutput->getCanvas()->drawImageRect(tempInput, srcRect, stepDstRect, samplingOptions,
718                                                &paint, SkCanvas::kStrict_SrcRectConstraint);
719 
720         tempInput = SkSurfaces::AsImage(std::move(tempOutput));
721         srcRect = SkRect::Make(nextDims);
722     } while (srcRect.width() != finalSize.width() || srcRect.height() != finalSize.height());
723 
724     return SkSurfaces::AsImage(std::move(dst));
725 }
726 
GenerateMipmaps(Recorder * recorder,sk_sp<TextureProxy> texture,const SkColorInfo & colorInfo)727 bool GenerateMipmaps(Recorder* recorder,
728                      sk_sp<TextureProxy> texture,
729                      const SkColorInfo& colorInfo) {
730     constexpr SkSamplingOptions kSamplingOptions = SkSamplingOptions(SkFilterMode::kLinear);
731 
732     SkASSERT(texture->mipmapped() == Mipmapped::kYes);
733 
734     // Within a rescaling pass scratchImg is read from and a scratch surface is written to.
735     // At the end of the pass the scratch surface's texture is wrapped and assigned to scratchImg.
736 
737     // The scratch surface we create below will use a write swizzle derived from SkColorType and
738     // pixel format. We have to be consistent and swizzle on the read.
739     auto imgSwizzle = recorder->priv().caps()->getReadSwizzle(colorInfo.colorType(),
740                                                               texture->textureInfo());
741     sk_sp<SkImage> scratchImg(new Image(TextureProxyView(texture, imgSwizzle), colorInfo));
742 
743     SkISize srcSize = texture->dimensions();
744     const SkColorInfo outColorInfo = colorInfo.makeAlphaType(kPremul_SkAlphaType);
745 
746     // Alternate between two scratch surfaces to avoid reading from and writing to a texture in the
747     // same pass. The dimensions of the first usages of the two scratch textures will be 1/2 and 1/4
748     // those of the original texture, respectively.
749     sk_sp<Surface> scratchSurfaces[2];
750     for (int i = 0; i < 2; ++i) {
751         scratchSurfaces[i] = make_renderable_scratch_surface(
752                 recorder,
753                 SkImageInfo::Make(SkISize::Make(std::max(1, srcSize.width() >> (i + 1)),
754                                                 std::max(1, srcSize.height() >> (i + 1))),
755                                   outColorInfo),
756                 SkBackingFit::kApprox,
757                 "GenerateMipmapsScratchTexture");
758         if (!scratchSurfaces[i]) {
759             return false;
760         }
761     }
762 
763     for (int mipLevel = 1; srcSize.width() > 1 || srcSize.height() > 1; ++mipLevel) {
764         const SkISize dstSize = SkISize::Make(std::max(srcSize.width() >> 1, 1),
765                                               std::max(srcSize.height() >> 1, 1));
766 
767         Surface* scratchSurface = scratchSurfaces[(mipLevel - 1) & 1].get();
768 
769         SkPaint paint;
770         paint.setBlendMode(SkBlendMode::kSrc);
771         scratchSurface->getCanvas()->drawImageRect(scratchImg,
772                                                    SkRect::Make(srcSize),
773                                                    SkRect::Make(dstSize),
774                                                    kSamplingOptions,
775                                                    &paint,
776                                                    SkCanvas::kStrict_SrcRectConstraint);
777 
778         // Make sure the rescaling draw finishes before copying the results.
779         Flush(scratchSurface);
780 
781         sk_sp<CopyTextureToTextureTask> copyTask = CopyTextureToTextureTask::Make(
782                 static_cast<const Surface*>(scratchSurface)->readSurfaceView().refProxy(),
783                 SkIRect::MakeSize(dstSize),
784                 texture,
785                 {0, 0},
786                 mipLevel);
787         if (!copyTask) {
788             return false;
789         }
790         recorder->priv().add(std::move(copyTask));
791 
792         scratchImg = scratchSurface->asImage();
793         srcSize = dstSize;
794     }
795 
796     return true;
797 }
798 
GetGraphiteBacked(Recorder * recorder,const SkImage * imageIn,SkSamplingOptions sampling)799 std::pair<sk_sp<SkImage>, SkSamplingOptions> GetGraphiteBacked(Recorder* recorder,
800                                                                const SkImage* imageIn,
801                                                                SkSamplingOptions sampling) {
802     Mipmapped mipmapped = (sampling.mipmap != SkMipmapMode::kNone)
803                                      ? Mipmapped::kYes : Mipmapped::kNo;
804 
805     if (imageIn->dimensions().area() <= 1 && mipmapped == Mipmapped::kYes) {
806         mipmapped = Mipmapped::kNo;
807         sampling = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone);
808     }
809 
810     sk_sp<SkImage> result;
811     if (as_IB(imageIn)->isGraphiteBacked()) {
812         result = sk_ref_sp(imageIn);
813 
814         // If the preexisting Graphite-backed image doesn't have the required mipmaps we will drop
815         // down the sampling
816         if (mipmapped == Mipmapped::kYes && !result->hasMipmaps()) {
817             mipmapped = Mipmapped::kNo;
818             sampling = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone);
819         }
820     } else {
821         auto clientImageProvider = recorder->clientImageProvider();
822         result = clientImageProvider->findOrCreate(
823                 recorder, imageIn, {mipmapped == Mipmapped::kYes});
824 
825         if (!valid_client_provided_image(
826                     result.get(), imageIn, {mipmapped == Mipmapped::kYes})) {
827             // The client did not fulfill the ImageProvider contract so drop the image.
828             result = nullptr;
829         }
830     }
831 
832     if (sampling.isAniso() && result) {
833         sampling = SkSamplingPriv::AnisoFallback(result->hasMipmaps());
834     }
835 
836     return { result, sampling };
837 }
838 
AsView(const SkImage * image)839 TextureProxyView AsView(const SkImage* image) {
840     if (!image) {
841         return {};
842     }
843     if (!as_IB(image)->isGraphiteBacked()) {
844         return {};
845     }
846     // A YUVA image (even if backed by graphite textures) is not a single texture
847     if (as_IB(image)->isYUVA()) {
848         return {};
849     }
850 
851     auto gi = reinterpret_cast<const Image*>(image);
852     return gi->textureProxyView();
853 }
854 
ComputeShaderCoverageMaskTargetFormat(const Caps * caps)855 SkColorType ComputeShaderCoverageMaskTargetFormat(const Caps* caps) {
856     // GPU compute coverage mask renderers need to bind the mask texture as a storage binding, which
857     // support a limited set of color formats. In general, we use RGBA8 if Alpha8 can't be
858     // supported.
859     if (caps->isStorage(caps->getDefaultStorageTextureInfo(kAlpha_8_SkColorType))) {
860         return kAlpha_8_SkColorType;
861     }
862     return kRGBA_8888_SkColorType;
863 }
864 
865 } // namespace skgpu::graphite
866 
867 namespace skif {
868 
869 namespace {
870 
871 // TODO(michaelludwig): The skgpu::BlurUtils effects will be migrated to src/core to implement a
872 // shader BlurEngine that can be shared by rastr, Ganesh, and Graphite. This is blocked by having
873 // skif::FilterResult handle the resizing to the max supported sigma.
874 class GraphiteBackend :
875         public Backend,
876 #if defined(SK_USE_LEGACY_BLUR_GRAPHITE)
877         private SkBlurEngine::Algorithm,
878 #else
879         private SkShaderBlurAlgorithm,
880 #endif
881         private SkBlurEngine {
882 public:
883 
GraphiteBackend(skgpu::graphite::Recorder * recorder,const SkSurfaceProps & surfaceProps,SkColorType colorType)884     GraphiteBackend(skgpu::graphite::Recorder* recorder,
885                     const SkSurfaceProps& surfaceProps,
886                     SkColorType colorType)
887             : Backend(SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize),
888                       surfaceProps, colorType)
889             , fRecorder(recorder) {}
890 
891     // Backend
makeDevice(SkISize size,sk_sp<SkColorSpace> colorSpace,const SkSurfaceProps * props) const892     sk_sp<SkDevice> makeDevice(SkISize size,
893                                sk_sp<SkColorSpace> colorSpace,
894                                const SkSurfaceProps* props) const override {
895         SkImageInfo imageInfo = SkImageInfo::Make(size,
896                                                   this->colorType(),
897                                                   kPremul_SkAlphaType,
898                                                   std::move(colorSpace));
899         return skgpu::graphite::Device::Make(fRecorder,
900                                              imageInfo,
901                                              skgpu::Budgeted::kYes,
902                                              skgpu::Mipmapped::kNo,
903                                              SkBackingFit::kApprox,
904                                              props ? *props : this->surfaceProps(),
905                                              skgpu::graphite::LoadOp::kDiscard,
906                                              "ImageFilterResult");
907     }
908 
makeImage(const SkIRect & subset,sk_sp<SkImage> image) const909     sk_sp<SkSpecialImage> makeImage(const SkIRect& subset, sk_sp<SkImage> image) const override {
910         return SkSpecialImages::MakeGraphite(fRecorder, subset, image, this->surfaceProps());
911     }
912 
getCachedBitmap(const SkBitmap & data) const913     sk_sp<SkImage> getCachedBitmap(const SkBitmap& data) const override {
914         auto proxy = skgpu::graphite::RecorderPriv::CreateCachedProxy(fRecorder, data,
915                                                                       "ImageFilterCachedBitmap");
916         if (!proxy) {
917             return nullptr;
918         }
919 
920         const SkColorInfo& colorInfo = data.info().colorInfo();
921         skgpu::Swizzle swizzle = fRecorder->priv().caps()->getReadSwizzle(colorInfo.colorType(),
922                                                                           proxy->textureInfo());
923         return sk_make_sp<skgpu::graphite::Image>(
924                 skgpu::graphite::TextureProxyView(std::move(proxy), swizzle),
925                 colorInfo);
926     }
927 
getBlurEngine() const928     const SkBlurEngine* getBlurEngine() const override { return this; }
929 
930     // SkBlurEngine
findAlgorithm(SkSize sigma,SkColorType colorType) const931     const SkBlurEngine::Algorithm* findAlgorithm(SkSize sigma,
932                                                  SkColorType colorType) const override {
933         // The runtime effect blurs handle all tilemodes and color types
934         return this;
935     }
936 
937 #if defined(SK_USE_LEGACY_BLUR_GRAPHITE)
938     // SkBlurEngine::Algorithm
maxSigma() const939     float maxSigma() const override {
940         return SK_ScalarInfinity;
941     }
942 
supportsOnlyDecalTiling() const943     bool supportsOnlyDecalTiling() const override { return false; }
944 
blur(SkSize sigma,sk_sp<SkSpecialImage> src,const SkIRect & srcRect,SkTileMode tileMode,const SkIRect & dstRect) const945     sk_sp<SkSpecialImage> blur(SkSize sigma,
946                                sk_sp<SkSpecialImage> src,
947                                const SkIRect& srcRect,
948                                SkTileMode tileMode,
949                                const SkIRect& dstRect) const override {
950         TRACE_EVENT_INSTANT2("skia.gpu", "GaussianBlur", TRACE_EVENT_SCOPE_THREAD,
951                              "sigmaX", sigma.width(), "sigmaY", sigma.height());
952 
953         SkColorSpace* cs = src->getColorSpace();
954         return skgpu::graphite::blur_impl(fRecorder, sigma, std::move(src), srcRect, tileMode,
955                                           dstRect, sk_ref_sp(cs), this->surfaceProps());
956     }
957 #else
useLegacyFilterResultBlur() const958     bool useLegacyFilterResultBlur() const override { return false; }
959 
960     // SkShaderBlurAlgorithm
makeDevice(const SkImageInfo & imageInfo) const961     sk_sp<SkDevice> makeDevice(const SkImageInfo& imageInfo) const override {
962         return skgpu::graphite::Device::Make(fRecorder,
963                                              imageInfo,
964                                              skgpu::Budgeted::kYes,
965                                              skgpu::Mipmapped::kNo,
966                                              SkBackingFit::kApprox,
967                                              this->surfaceProps(),
968                                              skgpu::graphite::LoadOp::kDiscard,
969                                              "EvalBlurTexture");
970     }
971 
972 #endif
973 
974 private:
975     skgpu::graphite::Recorder* fRecorder;
976 };
977 
978 } // anonymous namespace
979 
MakeGraphiteBackend(skgpu::graphite::Recorder * recorder,const SkSurfaceProps & surfaceProps,SkColorType colorType)980 sk_sp<Backend> MakeGraphiteBackend(skgpu::graphite::Recorder* recorder,
981                                    const SkSurfaceProps& surfaceProps,
982                                    SkColorType colorType) {
983     SkASSERT(recorder);
984     return sk_make_sp<GraphiteBackend>(recorder, surfaceProps, colorType);
985 }
986 
987 }  // namespace skif
988