xref: /aosp_15_r20/external/skia/src/gpu/ganesh/SurfaceContext.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2016 Google Inc.
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 #include "src/gpu/ganesh/SurfaceContext.h"
8 
9 #include "include/core/SkAlphaType.h"
10 #include "include/core/SkColorSpace.h"
11 #include "include/core/SkColorType.h"
12 #include "include/core/SkData.h"
13 #include "include/core/SkImageInfo.h"
14 #include "include/core/SkMatrix.h"
15 #include "include/core/SkSamplingOptions.h"
16 #include "include/core/SkSurface.h"
17 #include "include/gpu/GpuTypes.h"
18 #include "include/gpu/ganesh/GrBackendSurface.h"
19 #include "include/gpu/ganesh/GrDirectContext.h"
20 #include "include/gpu/ganesh/GrRecordingContext.h"
21 #include "include/gpu/ganesh/GrTypes.h"
22 #include "include/private/base/SingleOwner.h"
23 #include "include/private/base/SkAlign.h"
24 #include "include/private/base/SkAssert.h"
25 #include "include/private/base/SkPoint_impl.h"
26 #include "include/private/base/SkTemplates.h"
27 #include "include/private/base/SkTo.h"
28 #include "include/private/gpu/ganesh/GrTypesPriv.h"
29 #include "src/core/SkColorSpaceXformSteps.h"
30 #include "src/core/SkMipmap.h"
31 #include "src/core/SkTraceEvent.h"
32 #include "src/core/SkYUVMath.h"
33 #include "src/gpu/AsyncReadTypes.h"
34 #include "src/gpu/SkBackingFit.h"
35 #include "src/gpu/ganesh/GrAuditTrail.h"
36 #include "src/gpu/ganesh/GrCaps.h"
37 #include "src/gpu/ganesh/GrClientMappedBufferManager.h"
38 #include "src/gpu/ganesh/GrColorSpaceXform.h"
39 #include "src/gpu/ganesh/GrDataUtils.h"
40 #include "src/gpu/ganesh/GrDirectContextPriv.h"
41 #include "src/gpu/ganesh/GrDrawingManager.h"
42 #include "src/gpu/ganesh/GrFragmentProcessor.h"
43 #include "src/gpu/ganesh/GrGpu.h"
44 #include "src/gpu/ganesh/GrImageInfo.h"
45 #include "src/gpu/ganesh/GrProxyProvider.h"
46 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
47 #include "src/gpu/ganesh/GrRenderTargetProxy.h"
48 #include "src/gpu/ganesh/GrRenderTask.h"
49 #include "src/gpu/ganesh/GrResourceProvider.h"
50 #include "src/gpu/ganesh/GrSurface.h"
51 #include "src/gpu/ganesh/GrTextureProxy.h"
52 #include "src/gpu/ganesh/GrTracing.h"
53 #include "src/gpu/ganesh/SurfaceFillContext.h"
54 #include "src/gpu/ganesh/effects/GrBicubicEffect.h"
55 #include "src/gpu/ganesh/effects/GrTextureEffect.h"
56 #include "src/gpu/ganesh/geometry/GrRect.h"
57 
58 #include <algorithm>
59 #include <cstdint>
60 #include <memory>
61 #include <tuple>
62 
63 using namespace skia_private;
64 
65 #define ASSERT_SINGLE_OWNER         SKGPU_ASSERT_SINGLE_OWNER(this->singleOwner())
66 #define RETURN_FALSE_IF_ABANDONED   if (this->fContext->abandoned()) { return false;   }
67 #define RETURN_NULLPTR_IF_ABANDONED if (this->fContext->abandoned()) { return nullptr; }
68 
69 namespace skgpu::ganesh {
70 
SurfaceContext(GrRecordingContext * context,GrSurfaceProxyView readView,const GrColorInfo & info)71 SurfaceContext::SurfaceContext(GrRecordingContext* context,
72                                GrSurfaceProxyView readView,
73                                const GrColorInfo& info)
74         : fContext(context), fReadView(std::move(readView)), fColorInfo(info) {
75     SkASSERT(!context->abandoned());
76 }
77 
caps() const78 const GrCaps* SurfaceContext::caps() const { return fContext->priv().caps(); }
79 
drawingManager()80 GrDrawingManager* SurfaceContext::drawingManager() {
81     return fContext->priv().drawingManager();
82 }
83 
drawingManager() const84 const GrDrawingManager* SurfaceContext::drawingManager() const {
85     return fContext->priv().drawingManager();
86 }
87 
88 #ifdef SK_DEBUG
singleOwner() const89 skgpu::SingleOwner* SurfaceContext::singleOwner() const { return fContext->priv().singleOwner(); }
90 #endif
91 
alpha_types_compatible(SkAlphaType srcAlphaType,SkAlphaType dstAlphaType)92 static bool alpha_types_compatible(SkAlphaType srcAlphaType, SkAlphaType dstAlphaType) {
93     // If both alpha types are kUnknown things make sense. If not, it's too underspecified.
94     return (srcAlphaType == kUnknown_SkAlphaType) == (dstAlphaType == kUnknown_SkAlphaType);
95 }
96 
readPixels(GrDirectContext * dContext,GrPixmap dst,SkIPoint pt)97 bool SurfaceContext::readPixels(GrDirectContext* dContext, GrPixmap dst, SkIPoint pt) {
98     ASSERT_SINGLE_OWNER
99     RETURN_FALSE_IF_ABANDONED
100     SkDEBUGCODE(this->validate();)
101     GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceContext", "readPixels", fContext);
102 
103     if (!fContext->priv().matches(dContext)) {
104         return false;
105     }
106 
107     if (dst.colorType() == GrColorType::kUnknown) {
108         return false;
109     }
110 
111     if (dst.rowBytes() % dst.info().bpp()) {
112         return false;
113     }
114 
115     dst = dst.clip(this->dimensions(), &pt);
116     if (!dst.hasPixels()) {
117         return false;
118     }
119     if (!alpha_types_compatible(this->colorInfo().alphaType(), dst.alphaType())) {
120         return false;
121     }
122     // We allow unknown alpha types but only if both src and dst are unknown. Otherwise, it's too
123     // weird to reason about what should be expected.
124 
125     sk_sp<GrSurfaceProxy> srcProxy = this->asSurfaceProxyRef();
126 
127     if (srcProxy->framebufferOnly()) {
128         return false;
129     }
130 
131     // MDB TODO: delay this instantiation until later in the method
132     if (!srcProxy->instantiate(dContext->priv().resourceProvider())) {
133         return false;
134     }
135 
136     GrSurface* srcSurface = srcProxy->peekSurface();
137 
138     SkColorSpaceXformSteps::Flags flags =
139             SkColorSpaceXformSteps{this->colorInfo(), dst.info()}.flags;
140     bool unpremul            = flags.unpremul,
141          needColorConversion = flags.linearize || flags.gamut_transform || flags.encode,
142          premul              = flags.premul;
143 
144     const GrCaps* caps = dContext->priv().caps();
145     bool srcIsCompressed = caps->isFormatCompressed(srcSurface->backendFormat());
146     // This is the getImageData equivalent to the canvas2D putImageData fast path. We probably don't
147     // care so much about getImageData performance. However, in order to ensure putImageData/
148     // getImageData in "legacy" mode are round-trippable we use the GPU to do the complementary
149     // unpremul step to writeSurfacePixels's premul step (which is determined empirically in
150     // fContext->vaildaPMUPMConversionExists()).
151     GrBackendFormat defaultRGBAFormat = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888,
152                                                                       GrRenderable::kYes);
153     GrColorType srcColorType = this->colorInfo().colorType();
154     bool canvas2DFastPath = unpremul && !needColorConversion &&
155                             (GrColorType::kRGBA_8888 == dst.colorType() ||
156                              GrColorType::kBGRA_8888 == dst.colorType()) &&
157                             SkToBool(srcProxy->asTextureProxy()) &&
158                             (srcColorType == GrColorType::kRGBA_8888 ||
159                              srcColorType == GrColorType::kBGRA_8888) &&
160                             defaultRGBAFormat.isValid() &&
161                             dContext->priv().validPMUPMConversionExists();
162 
163     // Since the validPMUPMConversionExists function actually submits work to the gpu to do its
164     // tests, it is possible that during that call we have abandoned the context. Thus, we do
165     // another abandoned check here to make sure we are still valid.
166     RETURN_FALSE_IF_ABANDONED
167 
168     auto readFlag = caps->surfaceSupportsReadPixels(srcSurface);
169     if (readFlag == GrCaps::SurfaceReadPixelsSupport::kUnsupported) {
170         return false;
171     }
172 
173     if (readFlag == GrCaps::SurfaceReadPixelsSupport::kCopyToTexture2D || canvas2DFastPath) {
174         std::unique_ptr<SurfaceContext> tempCtx;
175         if (this->asTextureProxy()) {
176             GrColorType colorType = this->colorInfo().colorType();
177             if (canvas2DFastPath || srcIsCompressed) {
178                 colorType = GrColorType::kRGBA_8888;
179             } else {
180                 GrBackendFormat backendFormat =
181                         caps->getDefaultBackendFormat(colorType, GrRenderable::kYes);
182                 if (!backendFormat.isValid()) {
183                     colorType = GrColorType::kRGBA_8888;
184                 }
185             }
186 
187             SkAlphaType alphaType = canvas2DFastPath ? dst.alphaType()
188                                                      : this->colorInfo().alphaType();
189             GrImageInfo tempInfo(colorType,
190                                  alphaType,
191                                  this->colorInfo().refColorSpace(),
192                                  dst.dimensions());
193             auto sfc = dContext->priv().makeSFC(
194                     tempInfo, "SurfaceContext_ReadPixels", SkBackingFit::kApprox);
195             if (!sfc) {
196                 return false;
197             }
198 
199             std::unique_ptr<GrFragmentProcessor> fp;
200             if (canvas2DFastPath) {
201                 fp = dContext->priv().createPMToUPMEffect(GrTextureEffect::Make(
202                         this->readSurfaceView(), this->colorInfo().alphaType()));
203                 if (dst.colorType() == GrColorType::kBGRA_8888) {
204                     fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), skgpu::Swizzle::BGRA());
205                     dst = GrPixmap(dst.info().makeColorType(GrColorType::kRGBA_8888),
206                                    dst.addr(),
207                                    dst.rowBytes());
208                 }
209             } else {
210                 fp = GrTextureEffect::Make(this->readSurfaceView(), this->colorInfo().alphaType());
211             }
212             if (!fp) {
213                 return false;
214             }
215             sfc->fillRectToRectWithFP(SkIRect::MakePtSize(pt, dst.dimensions()),
216                                       SkIRect::MakeSize(dst.dimensions()),
217                                       std::move(fp));
218             pt = {0, 0};
219             tempCtx = std::move(sfc);
220         } else {
221             auto restrictions = this->caps()->getDstCopyRestrictions(this->asRenderTargetProxy(),
222                                                                      this->colorInfo().colorType());
223             sk_sp<GrSurfaceProxy> copy;
224             static constexpr auto kFit = SkBackingFit::kExact;
225             static constexpr auto kBudgeted = skgpu::Budgeted::kYes;
226             static constexpr auto kMipMapped = skgpu::Mipmapped::kNo;
227             if (restrictions.fMustCopyWholeSrc) {
228                 copy = GrSurfaceProxy::Copy(fContext,
229                                             std::move(srcProxy),
230                                             this->origin(),
231                                             kMipMapped,
232                                             kFit,
233                                             kBudgeted,
234                                             /*label=*/"SurfaceContext_ReadPixelsWithCopyWholeSrc");
235             } else {
236                 auto srcRect = SkIRect::MakePtSize(pt, dst.dimensions());
237                 copy = GrSurfaceProxy::Copy(fContext,
238                                             std::move(srcProxy),
239                                             this->origin(),
240                                             kMipMapped,
241                                             srcRect,
242                                             kFit,
243                                             kBudgeted,
244                                             /*label=*/"SurfaceContext_ReadPixels",
245                                             restrictions.fRectsMustMatch);
246                 pt = {0, 0};
247             }
248             if (!copy) {
249                 return false;
250             }
251             GrSurfaceProxyView view{std::move(copy), this->origin(), this->readSwizzle()};
252             tempCtx = dContext->priv().makeSC(std::move(view), this->colorInfo());
253             SkASSERT(tempCtx);
254         }
255         return tempCtx->readPixels(dContext, dst, pt);
256     }
257 
258     bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin;
259 
260     auto supportedRead = caps->supportedReadPixelsColorType(
261             this->colorInfo().colorType(), srcProxy->backendFormat(), dst.colorType());
262 
263     bool makeTight =
264             !caps->readPixelsRowBytesSupport() && dst.rowBytes() != dst.info().minRowBytes();
265 
266     bool convert = unpremul || premul || needColorConversion || flip || makeTight ||
267                    (dst.colorType() != supportedRead.fColorType);
268 
269     std::unique_ptr<char[]> tmpPixels;
270     GrPixmap tmp;
271     void* readDst = dst.addr();
272     size_t readRB = dst.rowBytes();
273     if (convert) {
274         GrImageInfo tmpInfo(supportedRead.fColorType,
275                             this->colorInfo().alphaType(),
276                             this->colorInfo().refColorSpace(),
277                             dst.dimensions());
278         size_t tmpRB = tmpInfo.minRowBytes();
279         size_t size = tmpRB * tmpInfo.height();
280         // Chrome MSAN bots require the data to be initialized (hence the ()).
281         tmpPixels = std::make_unique<char[]>(size);
282         tmp = {tmpInfo, tmpPixels.get(), tmpRB};
283 
284         readDst = tmpPixels.get();
285         readRB = tmpRB;
286         pt.fY = flip ? srcSurface->height() - pt.fY - dst.height() : pt.fY;
287     }
288 
289     dContext->priv().flushSurface(srcProxy.get());
290     dContext->submit();
291     if (!dContext->priv().getGpu()->readPixels(srcSurface,
292                                                SkIRect::MakePtSize(pt, dst.dimensions()),
293                                                this->colorInfo().colorType(),
294                                                supportedRead.fColorType,
295                                                readDst,
296                                                readRB)) {
297         return false;
298     }
299 
300     if (tmp.hasPixels()) {
301         return GrConvertPixels(dst, tmp, flip);
302     }
303     return true;
304 }
305 
writePixels(GrDirectContext * dContext,GrCPixmap src,SkIPoint dstPt)306 bool SurfaceContext::writePixels(GrDirectContext* dContext,
307                                  GrCPixmap src,
308                                  SkIPoint dstPt) {
309     ASSERT_SINGLE_OWNER
310     RETURN_FALSE_IF_ABANDONED
311     SkDEBUGCODE(this->validate();)
312 
313     src = src.clip(this->dimensions(), &dstPt);
314     if (!src.hasPixels()) {
315         return false;
316     }
317     if (!src.info().bpp() || src.rowBytes() % src.info().bpp()) {
318         return false;
319     }
320     return this->internalWritePixels(dContext, &src, 1, dstPt);
321 }
322 
writePixels(GrDirectContext * dContext,const GrCPixmap src[],int numLevels)323 bool SurfaceContext::writePixels(GrDirectContext* dContext,
324                                  const GrCPixmap src[],
325                                  int numLevels) {
326     ASSERT_SINGLE_OWNER
327     RETURN_FALSE_IF_ABANDONED
328     SkDEBUGCODE(this->validate();)
329 
330     SkASSERT(dContext);
331     SkASSERT(numLevels >= 1);
332     SkASSERT(src);
333 
334     if (numLevels == 1) {
335         if (src->dimensions() != this->dimensions()) {
336             return false;
337         }
338         return this->writePixels(dContext, src[0], {0, 0});
339     }
340     if (!this->asTextureProxy() ||
341         this->asTextureProxy()->proxyMipmapped() == skgpu::Mipmapped::kNo) {
342         return false;
343     }
344 
345     SkISize dims = this->dimensions();
346     if (numLevels != SkMipmap::ComputeLevelCount(dims) + 1) {
347         return false;
348     }
349     for (int i = 0; i < numLevels; ++i) {
350         if (src[i].colorInfo() != src[0].colorInfo()) {
351             return false;
352         }
353         if (dims != src[i].dimensions()) {
354             return false;
355         }
356         if (!src[i].info().bpp() || src[i].rowBytes() % src[i].info().bpp()) {
357             return false;
358         }
359         dims = {std::max(1, dims.width()/2), std::max(1, dims.height()/2)};
360     }
361     return this->internalWritePixels(dContext, src, numLevels, {0, 0});
362 }
363 
internalWritePixels(GrDirectContext * dContext,const GrCPixmap src[],int numLevels,SkIPoint pt)364 bool SurfaceContext::internalWritePixels(GrDirectContext* dContext,
365                                          const GrCPixmap src[],
366                                          int numLevels,
367                                          SkIPoint pt) {
368     GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceContext", "internalWritePixels", fContext);
369 
370     SkASSERT(numLevels >= 1);
371     SkASSERT(src);
372 
373     // We can either write to a subset or write MIP levels, but not both.
374     SkASSERT((src[0].dimensions() == this->dimensions() && pt.isZero()) || numLevels == 1);
375     SkASSERT(numLevels == 1 || (this->asTextureProxy() &&
376                                 this->asTextureProxy()->mipmapped() == skgpu::Mipmapped::kYes));
377     // Our public caller should have clipped to the bounds of the surface already.
378     SkASSERT(SkIRect::MakeSize(this->dimensions()).contains(
379             SkIRect::MakePtSize(pt, src[0].dimensions())));
380 
381     if (!dContext) {
382         return false;
383     }
384 
385     if (this->asSurfaceProxy()->readOnly()) {
386         return false;
387     }
388 
389     if (src[0].colorType() == GrColorType::kUnknown) {
390         return false;
391     }
392 
393     if (!alpha_types_compatible(src[0].alphaType(), this->colorInfo().alphaType())) {
394         return false;
395     }
396 
397     GrSurfaceProxy* dstProxy = this->asSurfaceProxy();
398 
399     if (dstProxy->framebufferOnly()) {
400         return false;
401     }
402 
403     if (!dstProxy->instantiate(dContext->priv().resourceProvider())) {
404         return false;
405     }
406 
407     GrSurface* dstSurface = dstProxy->peekSurface();
408 
409     SkColorSpaceXformSteps::Flags flags =
410             SkColorSpaceXformSteps{src[0].colorInfo(), this->colorInfo()}.flags;
411     bool unpremul            = flags.unpremul,
412          needColorConversion = flags.linearize || flags.gamut_transform || flags.encode,
413          premul              = flags.premul;
414 
415     const GrCaps* caps = dContext->priv().caps();
416 
417     auto rgbaDefaultFormat = caps->getDefaultBackendFormat(GrColorType::kRGBA_8888,
418                                                            GrRenderable::kNo);
419 
420     GrColorType dstColorType = this->colorInfo().colorType();
421     // For canvas2D putImageData performance we have a special code path for unpremul RGBA_8888 srcs
422     // that are premultiplied on the GPU. This is kept as narrow as possible for now.
423     bool canvas2DFastPath = !caps->avoidWritePixelsFastPath() && premul && !needColorConversion &&
424                             (src[0].colorType() == GrColorType::kRGBA_8888 ||
425                              src[0].colorType() == GrColorType::kBGRA_8888) &&
426                             this->asFillContext() &&
427                             (dstColorType == GrColorType::kRGBA_8888 ||
428                              dstColorType == GrColorType::kBGRA_8888) &&
429                             rgbaDefaultFormat.isValid() &&
430                             dContext->priv().validPMUPMConversionExists();
431 
432     // Since the validPMUPMConversionExists function actually submits work to the gpu to do its
433     // tests, it is possible that during that call we have abanoned the context. Thus we do an
434     // abanoned check here to make sure we are still valid.
435     RETURN_FALSE_IF_ABANDONED
436 
437     // Drawing code path doesn't support writing to levels and doesn't support inserting layout
438     // transitions.
439     if ((!caps->surfaceSupportsWritePixels(dstSurface) || canvas2DFastPath) && numLevels == 1) {
440         GrColorInfo tempColorInfo;
441         GrBackendFormat format;
442         skgpu::Swizzle tempReadSwizzle;
443         if (canvas2DFastPath) {
444             tempColorInfo = {GrColorType::kRGBA_8888,
445                              kUnpremul_SkAlphaType,
446                              this->colorInfo().refColorSpace()};
447             format = rgbaDefaultFormat;
448         } else {
449             tempColorInfo = this->colorInfo();
450             format = dstProxy->backendFormat().makeTexture2D();
451             if (!format.isValid()) {
452                 return false;
453             }
454             tempReadSwizzle = this->readSwizzle();
455         }
456 
457         // It is more efficient for us to write pixels into a top left origin so we prefer that.
458         // However, if the final proxy isn't a render target then we must use a copy to move the
459         // data into it which requires the origins to match. If the final proxy is a render target
460         // we can use a draw instead which doesn't have this origin restriction. Thus for render
461         // targets we will use top left and otherwise we will make the origins match.
462         GrSurfaceOrigin tempOrigin =
463                 this->asFillContext() ? kTopLeft_GrSurfaceOrigin : this->origin();
464         auto tempProxy = dContext->priv().proxyProvider()->createProxy(
465                 format,
466                 src[0].dimensions(),
467                 GrRenderable::kNo,
468                 1,
469                 skgpu::Mipmapped::kNo,
470                 SkBackingFit::kApprox,
471                 skgpu::Budgeted::kYes,
472                 GrProtected::kNo,
473                 /*label=*/"SurfaceContext_InternalWritePixels");
474         if (!tempProxy) {
475             return false;
476         }
477         GrSurfaceProxyView tempView(tempProxy, tempOrigin, tempReadSwizzle);
478         SurfaceContext tempCtx(dContext, tempView, tempColorInfo);
479 
480         // In the fast path we always write the srcData to the temp context as though it were RGBA.
481         // When the data is really BGRA the write will cause the R and B channels to be swapped in
482         // the intermediate surface which gets corrected by a swizzle effect when drawing to the
483         // dst.
484         GrCPixmap origSrcBase = src[0];
485         GrCPixmap srcBase = origSrcBase;
486         if (canvas2DFastPath) {
487             srcBase = GrCPixmap(origSrcBase.info().makeColorType(GrColorType::kRGBA_8888),
488                                 origSrcBase.addr(),
489                                 origSrcBase.rowBytes());
490         }
491         if (!tempCtx.writePixels(dContext, srcBase, {0, 0})) {
492             return false;
493         }
494 
495         if (this->asFillContext()) {
496             std::unique_ptr<GrFragmentProcessor> fp;
497             if (canvas2DFastPath) {
498                 fp = dContext->priv().createUPMToPMEffect(
499                         GrTextureEffect::Make(std::move(tempView), tempColorInfo.alphaType()));
500                 // Important: check the original src color type here!
501                 if (origSrcBase.colorType() == GrColorType::kBGRA_8888) {
502                     fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), skgpu::Swizzle::BGRA());
503                 }
504             } else {
505                 fp = GrTextureEffect::Make(std::move(tempView), tempColorInfo.alphaType());
506             }
507             if (!fp) {
508                 return false;
509             }
510             this->asFillContext()->fillRectToRectWithFP(
511                     SkIRect::MakeSize(srcBase.dimensions()),
512                     SkIRect::MakePtSize(pt, srcBase.dimensions()),
513                     std::move(fp));
514         } else {
515             SkIRect srcRect = SkIRect::MakeSize(srcBase.dimensions());
516             SkIPoint dstPoint = SkIPoint::Make(pt.fX, pt.fY);
517             if (!this->copy(std::move(tempProxy), srcRect, dstPoint)) {
518                 return false;
519             }
520         }
521         return true;
522     }
523 
524     GrColorType srcColorType = src[0].colorType();
525     auto [allowedColorType, _] =
526             caps->supportedWritePixelsColorType(this->colorInfo().colorType(),
527                                                 dstProxy->backendFormat(),
528                                                 srcColorType);
529     bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin;
530 
531     bool convertAll = premul              ||
532                       unpremul            ||
533                       needColorConversion ||
534                       flip                ||
535                       (srcColorType != allowedColorType);
536     bool mustBeTight = !caps->writePixelsRowBytesSupport();
537     size_t tmpSize = 0;
538     if (mustBeTight || convertAll) {
539         for (int i = 0; i < numLevels; ++i) {
540             if (convertAll || (mustBeTight && src[i].rowBytes() != src[i].info().minRowBytes())) {
541                 tmpSize += src[i].info().makeColorType(allowedColorType).minRowBytes()*
542                            src[i].height();
543             }
544         }
545     }
546 
547     auto tmpData = tmpSize ? SkData::MakeUninitialized(tmpSize) : nullptr;
548     void*    tmp = tmpSize ? tmpData->writable_data()           : nullptr;
549     AutoSTArray<15, GrMipLevel> srcLevels(numLevels);
550     bool ownAllStorage = true;
551     for (int i = 0; i < numLevels; ++i) {
552         if (convertAll || (mustBeTight && src[i].rowBytes() != src[i].info().minRowBytes())) {
553             GrImageInfo tmpInfo(allowedColorType,
554                                 this->colorInfo().alphaType(),
555                                 this->colorInfo().refColorSpace(),
556                                 src[i].dimensions());
557             auto tmpRB = tmpInfo.minRowBytes();
558             GrPixmap tmpPM(tmpInfo, tmp, tmpRB);
559             SkAssertResult(GrConvertPixels(tmpPM, src[i], flip));
560             srcLevels[i] = {tmpPM.addr(), tmpPM.rowBytes(), tmpData};
561             tmp = SkTAddOffset<void>(tmp, tmpRB*tmpPM.height());
562         } else {
563             srcLevels[i] = {src[i].addr(), src[i].rowBytes(), src[i].pixelStorage()};
564             ownAllStorage &= src[i].ownsPixels();
565         }
566     }
567     pt.fY = flip ? dstSurface->height() - pt.fY - src[0].height() : pt.fY;
568 
569     if (!dContext->priv().drawingManager()->newWritePixelsTask(
570                 sk_ref_sp(dstProxy),
571                 SkIRect::MakePtSize(pt, src[0].dimensions()),
572                 allowedColorType,
573                 this->colorInfo().colorType(),
574                 srcLevels.begin(),
575                 numLevels)) {
576         return false;
577     }
578     if (numLevels > 1) {
579         dstProxy->asTextureProxy()->markMipmapsClean();
580     }
581     if (!ownAllStorage) {
582         // If any pixmap doesn't own its pixels then we must flush so that the pixels are pushed to
583         // the GPU before we return.
584         dContext->priv().flushSurface(dstProxy);
585     }
586     return true;
587 }
588 
asyncRescaleAndReadPixels(GrDirectContext * dContext,const SkImageInfo & info,const SkIRect & srcRect,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext callbackContext)589 void SurfaceContext::asyncRescaleAndReadPixels(GrDirectContext* dContext,
590                                                const SkImageInfo& info,
591                                                const SkIRect& srcRect,
592                                                RescaleGamma rescaleGamma,
593                                                RescaleMode rescaleMode,
594                                                ReadPixelsCallback callback,
595                                                ReadPixelsContext callbackContext) {
596     if (!dContext) {
597         callback(callbackContext, nullptr);
598         return;
599     }
600     auto rt = this->asRenderTargetProxy();
601     if (rt && rt->wrapsVkSecondaryCB()) {
602         callback(callbackContext, nullptr);
603         return;
604     }
605     if (rt && rt->framebufferOnly()) {
606         callback(callbackContext, nullptr);
607         return;
608     }
609     auto dstCT = SkColorTypeToGrColorType(info.colorType());
610     if (dstCT == GrColorType::kUnknown) {
611         callback(callbackContext, nullptr);
612         return;
613     }
614     bool needsRescale = srcRect.size() != info.dimensions()               ||
615                         this->origin() == kBottomLeft_GrSurfaceOrigin     ||
616                         this->colorInfo().alphaType() != info.alphaType() ||
617                         !SkColorSpace::Equals(this->colorInfo().colorSpace(), info.colorSpace());
618     auto surfaceBackendFormat = this->asSurfaceProxy()->backendFormat();
619     auto readInfo = this->caps()->supportedReadPixelsColorType(this->colorInfo().colorType(),
620                                                                surfaceBackendFormat,
621                                                                dstCT);
622     // Fail if we can't read from the source surface's color type.
623     if (readInfo.fColorType == GrColorType::kUnknown) {
624         callback(callbackContext, nullptr);
625         return;
626     }
627     // Fail if read color type does not have all of dstCT's color channels and those missing color
628     // channels are in the src.
629     uint32_t dstChannels = GrColorTypeChannelFlags(dstCT);
630     uint32_t legalReadChannels = GrColorTypeChannelFlags(readInfo.fColorType);
631     uint32_t srcChannels = GrColorTypeChannelFlags(this->colorInfo().colorType());
632     if ((~legalReadChannels & dstChannels) & srcChannels) {
633         callback(callbackContext, nullptr);
634         return;
635     }
636 
637     std::unique_ptr<SurfaceFillContext> tempFC;
638     int x = srcRect.fLeft;
639     int y = srcRect.fTop;
640     if (needsRescale) {
641         auto tempInfo = GrImageInfo(info).makeColorType(this->colorInfo().colorType());
642         tempFC = this->rescale(tempInfo, kTopLeft_GrSurfaceOrigin, srcRect,
643                                rescaleGamma, rescaleMode);
644         if (!tempFC) {
645             callback(callbackContext, nullptr);
646             return;
647         }
648         SkASSERT(SkColorSpace::Equals(tempFC->colorInfo().colorSpace(), info.colorSpace()));
649         SkASSERT(tempFC->origin() == kTopLeft_GrSurfaceOrigin);
650         x = y = 0;
651     }
652     auto srcCtx = tempFC ? tempFC.get() : this;
653     return srcCtx->asyncReadPixels(dContext,
654                                    SkIRect::MakePtSize({x, y}, info.dimensions()),
655                                    info.colorType(),
656                                    callback,
657                                    callbackContext);
658 }
659 
asyncReadPixels(GrDirectContext * dContext,const SkIRect & rect,SkColorType colorType,ReadPixelsCallback callback,ReadPixelsContext callbackContext)660 void SurfaceContext::asyncReadPixels(GrDirectContext* dContext,
661                                      const SkIRect& rect,
662                                      SkColorType colorType,
663                                      ReadPixelsCallback callback,
664                                      ReadPixelsContext callbackContext) {
665     using AsyncReadResult = skgpu::TAsyncReadResult<GrGpuBuffer, GrDirectContext::DirectContextID,
666                                                     PixelTransferResult>;
667 
668     SkASSERT(rect.fLeft >= 0 && rect.fRight <= this->width());
669     SkASSERT(rect.fTop >= 0 && rect.fBottom <= this->height());
670 
671     if (!dContext || this->asSurfaceProxy()->isProtected() == GrProtected::kYes) {
672         callback(callbackContext, nullptr);
673         return;
674     }
675 
676     auto mappedBufferManager = dContext->priv().clientMappedBufferManager();
677 
678     auto transferResult = this->transferPixels(SkColorTypeToGrColorType(colorType), rect);
679 
680     if (!transferResult.fTransferBuffer) {
681         auto ii = SkImageInfo::Make(rect.size(), colorType, this->colorInfo().alphaType(),
682                                     this->colorInfo().refColorSpace());
683         static const GrDirectContext::DirectContextID kInvalid;
684         auto result = std::make_unique<AsyncReadResult>(kInvalid);
685         GrPixmap pm = GrPixmap::Allocate(ii);
686         result->addCpuPlane(pm.pixelStorage(), pm.rowBytes());
687 
688         SkIPoint pt{rect.fLeft, rect.fTop};
689         if (!this->readPixels(dContext, pm, pt)) {
690             callback(callbackContext, nullptr);
691             return;
692         }
693         callback(callbackContext, std::move(result));
694         return;
695     }
696 
697     struct FinishContext {
698         ReadPixelsCallback* fClientCallback;
699         ReadPixelsContext fClientContext;
700         SkISize fSize;
701         GrClientMappedBufferManager* fMappedBufferManager;
702         PixelTransferResult fTransferResult;
703     };
704     // Assumption is that the caller would like to flush. We could take a parameter or require an
705     // explicit flush from the caller. We'd have to have a way to defer attaching the finish
706     // callback to GrGpu until after the next flush that flushes our op list, though.
707     auto* finishContext = new FinishContext{callback,
708                                             callbackContext,
709                                             rect.size(),
710                                             mappedBufferManager,
711                                             std::move(transferResult)};
712     auto finishCallback = [](GrGpuFinishedContext c) {
713         const auto* context = reinterpret_cast<const FinishContext*>(c);
714         auto manager = context->fMappedBufferManager;
715         auto result = std::make_unique<AsyncReadResult>(manager->ownerID());
716         if (!result->addTransferResult(context->fTransferResult,
717                                        context->fSize,
718                                        context->fTransferResult.fRowBytes,
719                                        manager)) {
720             result.reset();
721         }
722         (*context->fClientCallback)(context->fClientContext, std::move(result));
723         delete context;
724     };
725     GrFlushInfo flushInfo;
726     flushInfo.fFinishedContext = finishContext;
727     flushInfo.fFinishedProc = finishCallback;
728 
729     dContext->priv().flushSurface(
730             this->asSurfaceProxy(), SkSurfaces::BackendSurfaceAccess::kNoAccess, flushInfo);
731 }
732 
asyncRescaleAndReadPixelsYUV420(GrDirectContext * dContext,SkYUVColorSpace yuvColorSpace,bool readAlpha,sk_sp<SkColorSpace> dstColorSpace,const SkIRect & srcRect,SkISize dstSize,RescaleGamma rescaleGamma,RescaleMode rescaleMode,ReadPixelsCallback callback,ReadPixelsContext callbackContext)733 void SurfaceContext::asyncRescaleAndReadPixelsYUV420(GrDirectContext* dContext,
734                                                      SkYUVColorSpace yuvColorSpace,
735                                                      bool readAlpha,
736                                                      sk_sp<SkColorSpace> dstColorSpace,
737                                                      const SkIRect& srcRect,
738                                                      SkISize dstSize,
739                                                      RescaleGamma rescaleGamma,
740                                                      RescaleMode rescaleMode,
741                                                      ReadPixelsCallback callback,
742                                                      ReadPixelsContext callbackContext) {
743     using AsyncReadResult = skgpu::TAsyncReadResult<GrGpuBuffer, GrDirectContext::DirectContextID,
744                                                     PixelTransferResult>;
745 
746     SkASSERT(srcRect.fLeft >= 0 && srcRect.fRight <= this->width());
747     SkASSERT(srcRect.fTop >= 0 && srcRect.fBottom <= this->height());
748     SkASSERT(!dstSize.isZero());
749     SkASSERT((dstSize.width() % 2 == 0) && (dstSize.height() % 2 == 0));
750 
751     if (!dContext) {
752         callback(callbackContext, nullptr);
753         return;
754     }
755     auto rt = this->asRenderTargetProxy();
756     if (rt && rt->wrapsVkSecondaryCB()) {
757         callback(callbackContext, nullptr);
758         return;
759     }
760     if (rt && rt->framebufferOnly()) {
761         callback(callbackContext, nullptr);
762         return;
763     }
764     if (this->asSurfaceProxy()->isProtected() == GrProtected::kYes) {
765         callback(callbackContext, nullptr);
766         return;
767     }
768     int x = srcRect.fLeft;
769     int y = srcRect.fTop;
770     bool needsRescale = srcRect.size() != dstSize ||
771                         !SkColorSpace::Equals(this->colorInfo().colorSpace(), dstColorSpace.get());
772     GrSurfaceProxyView srcView = this->readSurfaceView();
773     if (needsRescale) {
774         auto info = SkImageInfo::Make(dstSize,
775                                       kRGBA_8888_SkColorType,
776                                       this->colorInfo().alphaType(),
777                                       dstColorSpace);
778         // TODO: Incorporate the YUV conversion into last pass of rescaling.
779         auto tempFC = this->rescale(info,
780                                     kTopLeft_GrSurfaceOrigin,
781                                     srcRect,
782                                     rescaleGamma,
783                                     rescaleMode);
784         if (!tempFC) {
785             callback(callbackContext, nullptr);
786             return;
787         }
788         SkASSERT(SkColorSpace::Equals(tempFC->colorInfo().colorSpace(), info.colorSpace()));
789         SkASSERT(tempFC->origin() == kTopLeft_GrSurfaceOrigin);
790         x = y = 0;
791         srcView = tempFC->readSurfaceView();
792     } else if (!srcView.asTextureProxy()) {
793         srcView = GrSurfaceProxyView::Copy(
794                 fContext,
795                 std::move(srcView),
796                 skgpu::Mipmapped::kNo,
797                 srcRect,
798                 SkBackingFit::kApprox,
799                 skgpu::Budgeted::kYes,
800                 /*label=*/"SurfaceContext_AsyncRescaleAndReadPixelsYUV420");
801         if (!srcView) {
802             // If we can't get a texture copy of the contents then give up.
803             callback(callbackContext, nullptr);
804             return;
805         }
806         SkASSERT(srcView.asTextureProxy());
807         x = y = 0;
808     }
809 
810     auto yaInfo = SkImageInfo::MakeA8(dstSize);
811     auto yFC = dContext->priv().makeSFCWithFallback(yaInfo, SkBackingFit::kApprox,
812                                                     /* sampleCount= */ 1,
813                                                     skgpu::Mipmapped::kNo, skgpu::Protected::kNo);
814     std::unique_ptr<SurfaceFillContext> aFC;
815     if (readAlpha) {
816         aFC = dContext->priv().makeSFCWithFallback(yaInfo, SkBackingFit::kApprox,
817                                                    /* sampleCount= */ 1,
818                                                    skgpu::Mipmapped::kNo, skgpu::Protected::kNo);
819     }
820 
821     auto uvInfo = yaInfo.makeWH(yaInfo.width()/2, yaInfo.height()/2);
822     auto uFC = dContext->priv().makeSFCWithFallback(uvInfo, SkBackingFit::kApprox,
823                                                     /* sampleCount= */ 1,
824                                                     skgpu::Mipmapped::kNo, skgpu::Protected::kNo);
825     auto vFC = dContext->priv().makeSFCWithFallback(uvInfo, SkBackingFit::kApprox,
826                                                     /* sampleCount= */ 1,
827                                                     skgpu::Mipmapped::kNo, skgpu::Protected::kNo);
828 
829     if (!yFC || !uFC || !vFC || (readAlpha && !aFC)) {
830         callback(callbackContext, nullptr);
831         return;
832     }
833 
834     float baseM[20];
835     SkColorMatrix_RGB2YUV(yuvColorSpace, baseM);
836 
837     // TODO: Use one transfer buffer for all three planes to reduce map/unmap cost?
838 
839     auto texMatrix = SkMatrix::Translate(x, y);
840 
841     auto [readCT, offsetAlignment] =
842             this->caps()->supportedReadPixelsColorType(yFC->colorInfo().colorType(),
843                                                        yFC->asSurfaceProxy()->backendFormat(),
844                                                        GrColorType::kAlpha_8);
845     if (readCT == GrColorType::kUnknown) {
846         callback(callbackContext, nullptr);
847         return;
848     }
849     bool doSynchronousRead = !this->caps()->transferFromSurfaceToBufferSupport() ||
850                              !offsetAlignment;
851     PixelTransferResult yTransfer, aTransfer, uTransfer, vTransfer;
852 
853     // This matrix generates (r,g,b,a) = (0, 0, 0, y)
854     float yM[20];
855     std::fill_n(yM, 15, 0.f);
856     std::copy_n(baseM + 0, 5, yM + 15);
857 
858     auto yFP = GrTextureEffect::Make(srcView, this->colorInfo().alphaType(), texMatrix);
859     yFP = GrFragmentProcessor::ColorMatrix(std::move(yFP),
860                                            yM,
861                                            /*unpremulInput=*/false,
862                                            /*clampRGBOutput=*/true,
863                                            /*premulOutput=*/false);
864     yFC->fillWithFP(std::move(yFP));
865     if (!doSynchronousRead) {
866         yTransfer = yFC->transferPixels(GrColorType::kAlpha_8,
867                                         SkIRect::MakeSize(yFC->dimensions()));
868         if (!yTransfer.fTransferBuffer) {
869             callback(callbackContext, nullptr);
870             return;
871         }
872     }
873 
874     if (readAlpha) {
875         auto aFP = GrTextureEffect::Make(srcView, this->colorInfo().alphaType(), texMatrix);
876         SkASSERT(baseM[15] == 0 &&
877                  baseM[16] == 0 &&
878                  baseM[17] == 0 &&
879                  baseM[18] == 1 &&
880                  baseM[19] == 0);
881         aFC->fillWithFP(std::move(aFP));
882         if (!doSynchronousRead) {
883             aTransfer = aFC->transferPixels(GrColorType::kAlpha_8,
884                                             SkIRect::MakeSize(aFC->dimensions()));
885             if (!aTransfer.fTransferBuffer) {
886                 callback(callbackContext, nullptr);
887                 return;
888             }
889         }
890     }
891 
892     texMatrix.preScale(2.f, 2.f);
893     // This matrix generates (r,g,b,a) = (0, 0, 0, u)
894     float uM[20];
895     std::fill_n(uM, 15, 0.f);
896     std::copy_n(baseM + 5, 5, uM + 15);
897 
898     auto uFP = GrTextureEffect::Make(srcView,
899                                      this->colorInfo().alphaType(),
900                                      texMatrix,
901                                      GrSamplerState::Filter::kLinear);
902     uFP = GrFragmentProcessor::ColorMatrix(std::move(uFP),
903                                            uM,
904                                            /*unpremulInput=*/false,
905                                            /*clampRGBOutput=*/true,
906                                            /*premulOutput=*/false);
907     uFC->fillWithFP(std::move(uFP));
908     if (!doSynchronousRead) {
909         uTransfer = uFC->transferPixels(GrColorType::kAlpha_8,
910                                         SkIRect::MakeSize(uFC->dimensions()));
911         if (!uTransfer.fTransferBuffer) {
912             callback(callbackContext, nullptr);
913             return;
914         }
915     }
916 
917     // This matrix generates (r,g,b,a) = (0, 0, 0, v)
918     float vM[20];
919     std::fill_n(vM, 15, 0.f);
920     std::copy_n(baseM + 10, 5, vM + 15);
921     auto vFP = GrTextureEffect::Make(std::move(srcView),
922                                      this->colorInfo().alphaType(),
923                                      texMatrix,
924                                      GrSamplerState::Filter::kLinear);
925     vFP = GrFragmentProcessor::ColorMatrix(std::move(vFP),
926                                            vM,
927                                            /*unpremulInput=*/false,
928                                            /*clampRGBOutput=*/true,
929                                            /*premulOutput=*/false);
930     vFC->fillWithFP(std::move(vFP));
931 
932     if (!doSynchronousRead) {
933         vTransfer = vFC->transferPixels(GrColorType::kAlpha_8,
934                                          SkIRect::MakeSize(vFC->dimensions()));
935         if (!vTransfer.fTransferBuffer) {
936             callback(callbackContext, nullptr);
937             return;
938         }
939     }
940 
941     if (doSynchronousRead) {
942         GrPixmap yPmp = GrPixmap::Allocate(yaInfo);
943         GrPixmap uPmp = GrPixmap::Allocate(uvInfo);
944         GrPixmap vPmp = GrPixmap::Allocate(uvInfo);
945         GrPixmap aPmp;
946         if (readAlpha) {
947             aPmp = GrPixmap::Allocate(yaInfo);
948         }
949         if (!yFC->readPixels(dContext, yPmp, {0, 0}) ||
950             !uFC->readPixels(dContext, uPmp, {0, 0}) ||
951             !vFC->readPixels(dContext, vPmp, {0, 0}) ||
952             (readAlpha && !aFC->readPixels(dContext, aPmp, {0, 0}))) {
953             callback(callbackContext, nullptr);
954             return;
955         }
956         auto result = std::make_unique<AsyncReadResult>(dContext->directContextID());
957         result->addCpuPlane(yPmp.pixelStorage(), yPmp.rowBytes());
958         result->addCpuPlane(uPmp.pixelStorage(), uPmp.rowBytes());
959         result->addCpuPlane(vPmp.pixelStorage(), vPmp.rowBytes());
960         if (readAlpha) {
961             result->addCpuPlane(aPmp.pixelStorage(), aPmp.rowBytes());
962         }
963         callback(callbackContext, std::move(result));
964         return;
965     }
966 
967     struct FinishContext {
968         ReadPixelsCallback* fClientCallback;
969         ReadPixelsContext fClientContext;
970         GrClientMappedBufferManager* fMappedBufferManager;
971         SkISize fSize;
972         PixelTransferResult fYTransfer;
973         PixelTransferResult fUTransfer;
974         PixelTransferResult fVTransfer;
975         PixelTransferResult fATransfer;
976     };
977     // Assumption is that the caller would like to flush. We could take a parameter or require an
978     // explicit flush from the caller. We'd have to have a way to defer attaching the finish
979     // callback to GrGpu until after the next flush that flushes our op list, though.
980     auto* finishContext = new FinishContext{callback,
981                                             callbackContext,
982                                             dContext->priv().clientMappedBufferManager(),
983                                             dstSize,
984                                             std::move(yTransfer),
985                                             std::move(uTransfer),
986                                             std::move(vTransfer),
987                                             std::move(aTransfer)};
988     auto finishCallback = [](GrGpuFinishedContext c) {
989         const auto* context = reinterpret_cast<const FinishContext*>(c);
990         auto manager = context->fMappedBufferManager;
991         auto result = std::make_unique<AsyncReadResult>(manager->ownerID());
992         if (!result->addTransferResult(context->fYTransfer,
993                                        context->fSize,
994                                        context->fYTransfer.fRowBytes,
995                                        manager)) {
996             (*context->fClientCallback)(context->fClientContext, nullptr);
997             delete context;
998             return;
999         }
1000         SkISize uvSize = {context->fSize.width() / 2, context->fSize.height() / 2};
1001         if (!result->addTransferResult(context->fUTransfer,
1002                                        uvSize,
1003                                        context->fUTransfer.fRowBytes,
1004                                        manager)) {
1005             (*context->fClientCallback)(context->fClientContext, nullptr);
1006             delete context;
1007             return;
1008         }
1009         if (!result->addTransferResult(context->fVTransfer,
1010                                        uvSize,
1011                                        context->fVTransfer.fRowBytes,
1012                                        manager)) {
1013             (*context->fClientCallback)(context->fClientContext, nullptr);
1014             delete context;
1015             return;
1016         }
1017         if (context->fATransfer.fTransferBuffer &&
1018             !result->addTransferResult(context->fATransfer,
1019                                        context->fSize,
1020                                        context->fATransfer.fRowBytes,
1021                                        manager)) {
1022             (*context->fClientCallback)(context->fClientContext, nullptr);
1023             delete context;
1024             return;
1025         }
1026         (*context->fClientCallback)(context->fClientContext, std::move(result));
1027         delete context;
1028     };
1029     GrFlushInfo flushInfo;
1030     flushInfo.fFinishedContext = finishContext;
1031     flushInfo.fFinishedProc = finishCallback;
1032     dContext->priv().flushSurface(
1033             this->asSurfaceProxy(), SkSurfaces::BackendSurfaceAccess::kNoAccess, flushInfo);
1034 }
1035 
copy(sk_sp<GrSurfaceProxy> src,SkIRect srcRect,SkIPoint dstPoint)1036 sk_sp<GrRenderTask> SurfaceContext::copy(sk_sp<GrSurfaceProxy> src,
1037                                          SkIRect srcRect,
1038                                          SkIPoint dstPoint) {
1039     if (!GrClipSrcRectAndDstPoint(this->dimensions(), &dstPoint,
1040                                   src->dimensions(), &srcRect)) {
1041         return nullptr;
1042     }
1043 
1044     SkIRect dstRect = SkIRect::MakePtSize(dstPoint, srcRect.size());
1045     return this->copyScaled(src, srcRect, dstRect, GrSamplerState::Filter::kNearest);
1046 }
1047 
copyScaled(sk_sp<GrSurfaceProxy> src,SkIRect srcRect,SkIRect dstRect,GrSamplerState::Filter filter)1048 sk_sp<GrRenderTask> SurfaceContext::copyScaled(sk_sp<GrSurfaceProxy> src,
1049                                                SkIRect srcRect,
1050                                                SkIRect dstRect,
1051                                                GrSamplerState::Filter filter) {
1052     ASSERT_SINGLE_OWNER
1053     RETURN_NULLPTR_IF_ABANDONED
1054     SkDEBUGCODE(this->validate();)
1055     GR_CREATE_TRACE_MARKER_CONTEXT("SurfaceContext", "copyScaled", fContext);
1056 
1057     const GrCaps* caps = fContext->priv().caps();
1058 
1059     if (this->asSurfaceProxy()->framebufferOnly()) {
1060         return nullptr;
1061     }
1062 
1063     // canCopySurface() verifies that src and dst rects are contained in their surfaces.
1064     if (!caps->canCopySurface(this->asSurfaceProxy(), dstRect, src.get(), srcRect)) {
1065         return nullptr;
1066     }
1067 
1068     if (filter == GrSamplerState::Filter::kLinear && !src->isFunctionallyExact()) {
1069         // If we're linear filtering an image that is approx-sized, there are cases where the filter
1070         // could sample outside the logical dimensions. Specifically if we're upscaling along an
1071         // axis where we are copying up to the logical dimension, but that dimension is less than
1072         // the actual backing store dimension, the linear filter will access one texel beyond the
1073         // logical size, potentially incorporating undefined values.
1074         // NOTE: Identity scales that sample along the logical boundary of an approxi-fit texture
1075         // can still technically access a row or column of undefined data (albeit with a weight that
1076         // *should* be zero). We also disallow copying with linear filtering in that scenario,
1077         // just in case.
1078 #if defined(SK_USE_SAFE_INSET_FOR_TEXTURE_SAMPLING)
1079         const bool upscalingXAtApproxEdge =
1080             dstRect.width() >= srcRect.width() &&
1081             srcRect.fRight == src->width() &&
1082             srcRect.fRight < src->backingStoreDimensions().width();
1083         const bool upscalingYAtApproxEdge =
1084             dstRect.height() >= srcRect.height() &&
1085             srcRect.fBottom == src->height() &&
1086             srcRect.fBottom < src->backingStoreDimensions().height();
1087 #else
1088         // In this mode we allow non-scaling copies through even if the linear filtering would
1089         // access the adjacent undefined row or column.
1090         const bool upscalingXAtApproxEdge =
1091             dstRect.width() > srcRect.width() &&
1092             srcRect.fRight == src->width() &&
1093             srcRect.fRight < src->backingStoreDimensions().width();
1094         const bool upscalingYAtApproxEdge =
1095             dstRect.height() > srcRect.height() &&
1096             srcRect.fBottom == src->height() &&
1097             srcRect.fBottom < src->backingStoreDimensions().height();
1098 #endif
1099         if (upscalingXAtApproxEdge || upscalingYAtApproxEdge) {
1100             return nullptr;
1101         }
1102 
1103         // NOTE: Any upscaling with the linear filter will include content that's 1px outside the
1104         // src rect, but as long as that's still within the logical dimensions we assume it's okay.
1105     }
1106 
1107     SkASSERT(src->backendFormat().textureType() != GrTextureType::kExternal);
1108     SkASSERT(src->backendFormat() == this->asSurfaceProxy()->backendFormat());
1109     return this->drawingManager()->newCopyRenderTask(this->asSurfaceProxyRef(),
1110                                                      dstRect,
1111                                                      std::move(src),
1112                                                      srcRect,
1113                                                      filter,
1114                                                      this->origin());
1115 }
1116 
rescale(const GrImageInfo & info,GrSurfaceOrigin origin,SkIRect srcRect,RescaleGamma rescaleGamma,RescaleMode rescaleMode)1117 std::unique_ptr<SurfaceFillContext> SurfaceContext::rescale(const GrImageInfo& info,
1118                                                             GrSurfaceOrigin origin,
1119                                                             SkIRect srcRect,
1120                                                             RescaleGamma rescaleGamma,
1121                                                             RescaleMode rescaleMode) {
1122     auto sfc = fContext->priv().makeSFCWithFallback(info,
1123                                                     SkBackingFit::kExact,
1124                                                     /* sampleCount= */ 1,
1125                                                     skgpu::Mipmapped::kNo,
1126                                                     this->asSurfaceProxy()->isProtected(),
1127                                                     origin);
1128     if (!sfc || !this->rescaleInto(sfc.get(),
1129                                    SkIRect::MakeSize(sfc->dimensions()),
1130                                    srcRect,
1131                                    rescaleGamma,
1132                                    rescaleMode)) {
1133         return nullptr;
1134     }
1135     return sfc;
1136 }
1137 
rescaleInto(SurfaceFillContext * dst,SkIRect dstRect,SkIRect srcRect,RescaleGamma rescaleGamma,RescaleMode rescaleMode)1138 bool SurfaceContext::rescaleInto(SurfaceFillContext* dst,
1139                                  SkIRect dstRect,
1140                                  SkIRect srcRect,
1141                                  RescaleGamma rescaleGamma,
1142                                  RescaleMode rescaleMode) {
1143     SkASSERT(dst);
1144     if (!SkIRect::MakeSize(dst->dimensions()).contains((dstRect))) {
1145         return false;
1146     }
1147 
1148     auto rtProxy = this->asRenderTargetProxy();
1149     if (rtProxy && rtProxy->wrapsVkSecondaryCB()) {
1150         return false;
1151     }
1152 
1153     if (this->asSurfaceProxy()->framebufferOnly()) {
1154         return false;
1155     }
1156 
1157     GrSurfaceProxyView texView = this->readSurfaceView();
1158     // If we perform scaling as draws, texView must be texturable; if it's not already, we have to
1159     // make a copy. However, if the scaling can use copyScaled(), we can avoid this copy.
1160     auto ensureTexturable = [this](GrSurfaceProxyView texView, SkIRect srcRect) {
1161         if (!texView.asTextureProxy()) {
1162             // TODO: If copying supported specifying a renderable copy then we could return the copy
1163             // when there are no other conversions.
1164             texView = GrSurfaceProxyView::Copy(fContext,
1165                                                std::move(texView),
1166                                                skgpu::Mipmapped::kNo,
1167                                                srcRect,
1168                                                SkBackingFit::kApprox,
1169                                                skgpu::Budgeted::kNo,
1170                                                "SurfaceContext_RescaleInto");
1171             if (texView) {
1172                 SkASSERT(texView.asTextureProxy());
1173                 srcRect = SkIRect::MakeSize(srcRect.size());
1174             }
1175         }
1176         return std::make_pair(std::move(texView), srcRect);
1177     };
1178 
1179     SkISize finalSize = dstRect.size();
1180     if (finalSize == srcRect.size()) {
1181         rescaleGamma = RescaleGamma::kSrc;
1182         rescaleMode = RescaleMode::kNearest;
1183     }
1184 
1185     // Within a rescaling pass A is the input (if not null) and B is the output. At the end of the
1186     // pass B is moved to A. If 'this' is the input on the first pass then tempA is null.
1187     std::unique_ptr<SurfaceFillContext> tempA;
1188     std::unique_ptr<SurfaceFillContext> tempB;
1189 
1190     // Assume we should ignore the rescale linear request if the surface has no color space since
1191     // it's unclear how we'd linearize from an unknown color space.
1192     if (rescaleGamma == RescaleGamma::kLinear && this->colorInfo().colorSpace() &&
1193         !this->colorInfo().colorSpace()->gammaIsLinear()) {
1194         // Colorspace transformations are always handled by drawing so we need to be texturable
1195         std::tie(texView, srcRect) = ensureTexturable(texView, srcRect);
1196         if (!texView) {
1197             return false;
1198         }
1199         auto cs = this->colorInfo().colorSpace()->makeLinearGamma();
1200         // We'll fall back to kRGBA_8888 if half float not supported.
1201         GrImageInfo ii(GrColorType::kRGBA_F16,
1202                        dst->colorInfo().alphaType(),
1203                        std::move(cs),
1204                        srcRect.size());
1205         auto linearRTC = fContext->priv().makeSFCWithFallback(std::move(ii),
1206                                                               SkBackingFit::kApprox,
1207                                                               /* sampleCount= */ 1,
1208                                                               skgpu::Mipmapped::kNo,
1209                                                               texView.proxy()->isProtected(),
1210                                                               dst->origin());
1211         if (!linearRTC) {
1212             return false;
1213         }
1214         auto fp = GrTextureEffect::Make(std::move(texView),
1215                                         this->colorInfo().alphaType(),
1216                                         SkMatrix::Translate(srcRect.topLeft()),
1217                                         GrSamplerState::Filter::kNearest,
1218                                         GrSamplerState::MipmapMode::kNone);
1219         fp = GrColorSpaceXformEffect::Make(std::move(fp),
1220                                            this->colorInfo(),
1221                                            linearRTC->colorInfo());
1222         linearRTC->fillWithFP(std::move(fp));
1223         texView = linearRTC->readSurfaceView();
1224         SkASSERT(texView.asTextureProxy());
1225         tempA = std::move(linearRTC);
1226         srcRect = SkIRect::MakeSize(srcRect.size());
1227     }
1228 
1229     do {
1230         SkISize nextDims = finalSize;
1231         if (rescaleMode != RescaleMode::kNearest && rescaleMode != RescaleMode::kLinear) {
1232             if (srcRect.width() > finalSize.width()) {
1233                 nextDims.fWidth = std::max((srcRect.width() + 1)/2, finalSize.width());
1234             } else if (srcRect.width() < finalSize.width()) {
1235                 nextDims.fWidth = std::min(srcRect.width()*2, finalSize.width());
1236             }
1237             if (srcRect.height() > finalSize.height()) {
1238                 nextDims.fHeight = std::max((srcRect.height() + 1)/2, finalSize.height());
1239             } else if (srcRect.height() < finalSize.height()) {
1240                 nextDims.fHeight = std::min(srcRect.height()*2, finalSize.height());
1241             }
1242         }
1243         auto input = tempA ? tempA.get() : this;
1244         sk_sp<GrColorSpaceXform> xform;
1245         SurfaceFillContext* stepDst;
1246         SkIRect stepDstRect;
1247         if (nextDims == finalSize) {
1248             stepDst = dst;
1249             stepDstRect = dstRect;
1250             xform = GrColorSpaceXform::Make(input->colorInfo(), dst->colorInfo());
1251         } else {
1252             GrImageInfo nextInfo(input->colorInfo(), nextDims);
1253 
1254             tempB = fContext->priv().makeSFCWithFallback(nextInfo, SkBackingFit::kApprox,
1255                                                          /* sampleCount= */ 1,
1256                                                          skgpu::Mipmapped::kNo,
1257                                                          texView.proxy()->isProtected());
1258             if (!tempB) {
1259                 return false;
1260             }
1261             stepDst = tempB.get();
1262             stepDstRect = SkIRect::MakeSize(tempB->dimensions());
1263         }
1264         std::unique_ptr<GrFragmentProcessor> fp;
1265         if (rescaleMode == RescaleMode::kRepeatedCubic) {
1266             // Cubic sampling is always handled by drawing with a shader, so we must be texturable
1267             std::tie(texView, srcRect) = ensureTexturable(texView, srcRect);
1268             if (!texView) {
1269                 return false;
1270             }
1271             auto dir = GrBicubicEffect::Direction::kXY;
1272             if (nextDims.width() == srcRect.width()) {
1273                 dir = GrBicubicEffect::Direction::kY;
1274             } else if (nextDims.height() == srcRect.height()) {
1275                 dir = GrBicubicEffect::Direction::kX;
1276             }
1277             static constexpr auto kWM     = GrSamplerState::WrapMode::kClamp;
1278             static constexpr auto kKernel = GrBicubicEffect::gCatmullRom;
1279             fp = GrBicubicEffect::MakeSubset(std::move(texView),
1280                                              input->colorInfo().alphaType(),
1281                                              SkMatrix::I(),
1282                                              kWM,
1283                                              kWM,
1284                                              SkRect::Make(srcRect),
1285                                              kKernel,
1286                                              dir,
1287                                              *this->caps());
1288         } else {
1289             auto filter = rescaleMode == RescaleMode::kNearest ? GrSamplerState::Filter::kNearest
1290                                                                : GrSamplerState::Filter::kLinear;
1291             if (xform ||
1292                 texView.origin() != stepDst->origin() ||
1293                 !stepDst->copyScaled(texView.refProxy(), srcRect, stepDstRect, filter)) {
1294                 // We could not or were unable to successful perform a scaling blit (which can be
1295                 // much faster if texView isn't already texturable). Scale by drawing instead.
1296                 std::tie(texView, srcRect) = ensureTexturable(texView, srcRect);
1297                 if (!texView) {
1298                     return false;
1299                 }
1300                 auto srcRectF = SkRect::Make(srcRect);
1301                 fp = GrTextureEffect::MakeSubset(std::move(texView),
1302                                                  this->colorInfo().alphaType(),
1303                                                  SkMatrix::I(),
1304                                                  {filter, GrSamplerState::MipmapMode::kNone},
1305                                                  srcRectF,
1306                                                  srcRectF,
1307                                                  *this->caps());
1308             }
1309         }
1310         if (xform) {
1311             SkASSERT(SkToBool(fp)); // shouldn't have done a copy if there was a color xform
1312             fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(xform));
1313         }
1314         if (fp) {
1315             // When fp is not null, we scale by drawing; if it is null, presumably the src has
1316             // already been copied into stepDst.
1317             stepDst->fillRectToRectWithFP(srcRect, stepDstRect, std::move(fp));
1318         }
1319         texView = stepDst->readSurfaceView();
1320         tempA = std::move(tempB);
1321         srcRect = SkIRect::MakeSize(nextDims);
1322     } while (srcRect.size() != finalSize);
1323     return true;
1324 }
1325 
transferPixels(GrColorType dstCT,const SkIRect & rect)1326 SurfaceContext::PixelTransferResult SurfaceContext::transferPixels(GrColorType dstCT,
1327                                                                    const SkIRect& rect) {
1328     SkASSERT(rect.fLeft >= 0 && rect.fRight <= this->width());
1329     SkASSERT(rect.fTop >= 0 && rect.fBottom <= this->height());
1330     auto direct = fContext->asDirectContext();
1331     if (!direct) {
1332         return {};
1333     }
1334     auto rtProxy = this->asRenderTargetProxy();
1335     if (rtProxy && rtProxy->wrapsVkSecondaryCB()) {
1336         return {};
1337     }
1338 
1339     auto proxy = this->asSurfaceProxy();
1340     auto supportedRead = this->caps()->supportedReadPixelsColorType(this->colorInfo().colorType(),
1341                                                                     proxy->backendFormat(), dstCT);
1342     // Fail if read color type does not have all of dstCT's color channels and those missing color
1343     // channels are in the src.
1344     uint32_t dstChannels = GrColorTypeChannelFlags(dstCT);
1345     uint32_t legalReadChannels = GrColorTypeChannelFlags(supportedRead.fColorType);
1346     uint32_t srcChannels = GrColorTypeChannelFlags(this->colorInfo().colorType());
1347     if ((~legalReadChannels & dstChannels) & srcChannels) {
1348         return {};
1349     }
1350 
1351     if (!this->caps()->transferFromSurfaceToBufferSupport() ||
1352         !supportedRead.fOffsetAlignmentForTransferBuffer) {
1353         return {};
1354     }
1355 
1356     size_t rowBytes = GrColorTypeBytesPerPixel(supportedRead.fColorType) * rect.width();
1357     rowBytes = SkAlignTo(rowBytes, this->caps()->transferBufferRowBytesAlignment());
1358     size_t size = rowBytes * rect.height();
1359     // By using kStream_GrAccessPattern here, we are not able to cache and reuse the buffer for
1360     // multiple reads. Switching to kDynamic_GrAccessPattern would allow for this, however doing
1361     // so causes a crash in a chromium test. See skbug.com/11297
1362     auto buffer = direct->priv().resourceProvider()->createBuffer(
1363             size,
1364             GrGpuBufferType::kXferGpuToCpu,
1365             GrAccessPattern::kStream_GrAccessPattern,
1366             GrResourceProvider::ZeroInit::kNo);
1367     if (!buffer) {
1368         return {};
1369     }
1370     auto srcRect = rect;
1371     bool flip = this->origin() == kBottomLeft_GrSurfaceOrigin;
1372     if (flip) {
1373         srcRect = SkIRect::MakeLTRB(rect.fLeft, this->height() - rect.fBottom, rect.fRight,
1374                                     this->height() - rect.fTop);
1375     }
1376     this->drawingManager()->newTransferFromRenderTask(this->asSurfaceProxyRef(), srcRect,
1377                                                       this->colorInfo().colorType(),
1378                                                       supportedRead.fColorType, buffer, 0);
1379     PixelTransferResult result;
1380     result.fTransferBuffer = std::move(buffer);
1381     auto at = this->colorInfo().alphaType();
1382     if (supportedRead.fColorType != dstCT || flip) {
1383         int w = rect.width(), h = rect.height();
1384         GrImageInfo srcInfo(supportedRead.fColorType, at, nullptr, w, h);
1385         GrImageInfo dstInfo(dstCT, at, nullptr, w, h);
1386         result.fRowBytes = dstInfo.minRowBytes();
1387         result.fPixelConverter = [dstInfo, srcInfo, rowBytes](
1388                 void* dst, const void* src) {
1389             GrConvertPixels( GrPixmap(dstInfo, dst, dstInfo.minRowBytes()),
1390                             GrCPixmap(srcInfo, src, rowBytes));
1391         };
1392     } else {
1393         result.fRowBytes = rowBytes;
1394     }
1395     return result;
1396 }
1397 
1398 #ifdef SK_DEBUG
validate() const1399 void SurfaceContext::validate() const {
1400     SkASSERT(fReadView.proxy());
1401     fReadView.proxy()->validate(fContext);
1402     if (this->colorInfo().colorType() != GrColorType::kUnknown) {
1403         SkASSERT(fContext->priv().caps()->areColorTypeAndFormatCompatible(
1404                 this->colorInfo().colorType(), fReadView.proxy()->backendFormat()));
1405     }
1406     this->onValidate();
1407 }
1408 #endif
1409 
1410 }  // namespace skgpu::ganesh
1411