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