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