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