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