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