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