1 /*
2 * Copyright 2021 Google LLC
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 "include/gpu/graphite/Context.h"
9
10 #include "include/core/SkColorSpace.h"
11 #include "include/core/SkPathTypes.h"
12 #include "include/core/SkTraceMemoryDump.h"
13 #include "include/effects/SkRuntimeEffect.h"
14 #include "include/gpu/graphite/BackendTexture.h"
15 #include "include/gpu/graphite/Recorder.h"
16 #include "include/gpu/graphite/Recording.h"
17 #include "include/gpu/graphite/Surface.h"
18 #include "include/gpu/graphite/TextureInfo.h"
19 #include "src/base/SkRectMemcpy.h"
20 #include "src/core/SkAutoPixmapStorage.h"
21 #include "src/core/SkColorFilterPriv.h"
22 #include "src/core/SkConvertPixels.h"
23 #include "src/core/SkTraceEvent.h"
24 #include "src/core/SkYUVMath.h"
25 #include "src/gpu/RefCntedCallback.h"
26 #include "src/gpu/graphite/AtlasProvider.h"
27 #include "src/gpu/graphite/BufferManager.h"
28 #include "src/gpu/graphite/Caps.h"
29 #include "src/gpu/graphite/ClientMappedBufferManager.h"
30 #include "src/gpu/graphite/CommandBuffer.h"
31 #include "src/gpu/graphite/ContextPriv.h"
32 #include "src/gpu/graphite/DrawAtlas.h"
33 #include "src/gpu/graphite/GlobalCache.h"
34 #include "src/gpu/graphite/GraphicsPipeline.h"
35 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
36 #include "src/gpu/graphite/Image_Base_Graphite.h"
37 #include "src/gpu/graphite/Image_Graphite.h"
38 #include "src/gpu/graphite/KeyContext.h"
39 #include "src/gpu/graphite/Log.h"
40 #include "src/gpu/graphite/QueueManager.h"
41 #include "src/gpu/graphite/RecorderPriv.h"
42 #include "src/gpu/graphite/RecordingPriv.h"
43 #include "src/gpu/graphite/Renderer.h"
44 #include "src/gpu/graphite/RendererProvider.h"
45 #include "src/gpu/graphite/ResourceProvider.h"
46 #include "src/gpu/graphite/RuntimeEffectDictionary.h"
47 #include "src/gpu/graphite/ShaderCodeDictionary.h"
48 #include "src/gpu/graphite/SharedContext.h"
49 #include "src/gpu/graphite/Surface_Graphite.h"
50 #include "src/gpu/graphite/TextureProxyView.h"
51 #include "src/gpu/graphite/TextureUtils.h"
52 #include "src/gpu/graphite/task/CopyTask.h"
53 #include "src/gpu/graphite/task/SynchronizeToCpuTask.h"
54 #include "src/gpu/graphite/task/UploadTask.h"
55
56 #include "src/image/SkSurface_Base.h"
57
58 #if defined(GRAPHITE_TEST_UTILS)
59 #include "include/private/gpu/graphite/ContextOptionsPriv.h"
60 #if defined(SK_DAWN)
61 #include "src/gpu/graphite/dawn/DawnSharedContext.h"
62 #include "webgpu/webgpu_cpp.h" // NO_G3_REWRITE
63 #endif
64 #endif
65
66 namespace skgpu::graphite {
67
68 #define ASSERT_SINGLE_OWNER SKGPU_ASSERT_SINGLE_OWNER(this->singleOwner())
69
Next()70 Context::ContextID Context::ContextID::Next() {
71 static std::atomic<uint32_t> nextID{1};
72 uint32_t id;
73 do {
74 id = nextID.fetch_add(1, std::memory_order_relaxed);
75 } while (id == SK_InvalidUniqueID);
76 return ContextID(id);
77 }
78
79 //--------------------------------------------------------------------------------------------------
Context(sk_sp<SharedContext> sharedContext,std::unique_ptr<QueueManager> queueManager,const ContextOptions & options)80 Context::Context(sk_sp<SharedContext> sharedContext,
81 std::unique_ptr<QueueManager> queueManager,
82 const ContextOptions& options)
83 : fSharedContext(std::move(sharedContext))
84 , fQueueManager(std::move(queueManager))
85 , fContextID(ContextID::Next()) {
86 // We have to create this outside the initializer list because we need to pass in the Context's
87 // SingleOwner object and it is declared last
88 fResourceProvider = fSharedContext->makeResourceProvider(&fSingleOwner,
89 SK_InvalidGenID,
90 options.fGpuBudgetInBytes);
91 fMappedBufferManager = std::make_unique<ClientMappedBufferManager>(this->contextID());
92 #if defined(GRAPHITE_TEST_UTILS)
93 if (options.fOptionsPriv) {
94 fStoreContextRefInRecorder = options.fOptionsPriv->fStoreContextRefInRecorder;
95 }
96 #endif
97 }
98
~Context()99 Context::~Context() {
100 #if defined(GRAPHITE_TEST_UTILS)
101 ASSERT_SINGLE_OWNER
102 for (auto& recorder : fTrackedRecorders) {
103 recorder->priv().setContext(nullptr);
104 }
105 #endif
106 }
107
finishInitialization()108 bool Context::finishInitialization() {
109 SkASSERT(!fSharedContext->rendererProvider()); // Can only initialize once
110
111 StaticBufferManager bufferManager{fResourceProvider.get(), fSharedContext->caps()};
112 std::unique_ptr<RendererProvider> renderers{
113 new RendererProvider(fSharedContext->caps(), &bufferManager)};
114
115 auto result = bufferManager.finalize(this, fQueueManager.get(), fSharedContext->globalCache());
116 if (result == StaticBufferManager::FinishResult::kFailure) {
117 // If something went wrong filling out the static vertex buffers, any Renderer that would
118 // use it will draw incorrectly, so it's better to fail the Context creation.
119 return false;
120 }
121 if (result == StaticBufferManager::FinishResult::kSuccess &&
122 !fQueueManager->submitToGpu()) {
123 SKGPU_LOG_W("Failed to submit initial command buffer for Context creation.\n");
124 return false;
125 } // else result was kNoWork so skip submitting to the GPU
126 fSharedContext->setRendererProvider(std::move(renderers));
127 return true;
128 }
129
backend() const130 BackendApi Context::backend() const { return fSharedContext->backend(); }
131
makeRecorder(const RecorderOptions & options)132 std::unique_ptr<Recorder> Context::makeRecorder(const RecorderOptions& options) {
133 ASSERT_SINGLE_OWNER
134
135 // This is a client-owned Recorder so pass a null context so it creates its own ResourceProvider
136 auto recorder = std::unique_ptr<Recorder>(new Recorder(fSharedContext, options, nullptr));
137 #if defined(GRAPHITE_TEST_UTILS)
138 if (fStoreContextRefInRecorder) {
139 recorder->priv().setContext(this);
140 }
141 #endif
142 return recorder;
143 }
144
makeInternalRecorder() const145 std::unique_ptr<Recorder> Context::makeInternalRecorder() const {
146 ASSERT_SINGLE_OWNER
147
148 // Unlike makeRecorder(), this Recorder is meant to be short-lived and go
149 // away before a Context public API function returns to the caller. As such
150 // it shares the Context's resource provider (no separate budget) and does
151 // not get tracked. The internal drawing performed with an internal recorder
152 // should not require a client image provider.
153 return std::unique_ptr<Recorder>(new Recorder(fSharedContext, {}, this));
154 }
155
insertRecording(const InsertRecordingInfo & info)156 bool Context::insertRecording(const InsertRecordingInfo& info) {
157 ASSERT_SINGLE_OWNER
158
159 return fQueueManager->addRecording(info, this);
160 }
161
submit(SyncToCpu syncToCpu)162 bool Context::submit(SyncToCpu syncToCpu) {
163 ASSERT_SINGLE_OWNER
164
165 if (syncToCpu == SyncToCpu::kYes && !fSharedContext->caps()->allowCpuSync()) {
166 SKGPU_LOG_E("SyncToCpu::kYes not supported with ContextOptions::fNeverYieldToWebGPU. "
167 "The parameter is ignored and no synchronization will occur.");
168 syncToCpu = SyncToCpu::kNo;
169 }
170 bool success = fQueueManager->submitToGpu();
171 this->checkForFinishedWork(syncToCpu);
172 return success;
173 }
174
hasUnfinishedGpuWork() const175 bool Context::hasUnfinishedGpuWork() const { return fQueueManager->hasUnfinishedGpuWork(); }
176
177 template <typename SrcPixels>
178 struct Context::AsyncParams {
179 const SrcPixels* fSrcImage;
180 SkIRect fSrcRect;
181 SkImageInfo fDstImageInfo;
182
183 SkImage::ReadPixelsCallback* fCallback;
184 SkImage::ReadPixelsContext fCallbackContext;
185
186 template <typename S>
withNewSourceskgpu::graphite::Context::AsyncParams187 AsyncParams<S> withNewSource(const S* newPixels, const SkIRect& newSrcRect) const {
188 return AsyncParams<S>{newPixels, newSrcRect,
189 fDstImageInfo, fCallback, fCallbackContext};
190 }
191
failskgpu::graphite::Context::AsyncParams192 void fail() const {
193 (*fCallback)(fCallbackContext, nullptr);
194 }
195
validateskgpu::graphite::Context::AsyncParams196 bool validate() const {
197 if (!fSrcImage) {
198 return false;
199 }
200 if (fSrcImage->isProtected()) {
201 return false;
202 }
203 if (!SkIRect::MakeSize(fSrcImage->dimensions()).contains(fSrcRect)) {
204 return false;
205 }
206 if (!SkImageInfoIsValid(fDstImageInfo)) {
207 return false;
208 }
209 return true;
210 }
211 };
212
213 template <typename ReadFn, typename... ExtraArgs>
asyncRescaleAndReadImpl(ReadFn Context::* asyncRead,SkImage::RescaleGamma rescaleGamma,SkImage::RescaleMode rescaleMode,const AsyncParams<SkImage> & params,ExtraArgs...extraParams)214 void Context::asyncRescaleAndReadImpl(ReadFn Context::* asyncRead,
215 SkImage::RescaleGamma rescaleGamma,
216 SkImage::RescaleMode rescaleMode,
217 const AsyncParams<SkImage>& params,
218 ExtraArgs... extraParams) {
219 if (!params.validate()) {
220 return params.fail();
221 }
222
223 if (params.fSrcRect.size() == params.fDstImageInfo.dimensions()) {
224 // No need to rescale so do a direct readback
225 return (this->*asyncRead)(/*recorder=*/nullptr, params, extraParams...);
226 }
227
228 // Make a recorder to collect the rescale drawing commands and the copy commands
229 std::unique_ptr<Recorder> recorder = this->makeInternalRecorder();
230 sk_sp<SkImage> scaledImage = RescaleImage(recorder.get(),
231 params.fSrcImage,
232 params.fSrcRect,
233 params.fDstImageInfo,
234 rescaleGamma,
235 rescaleMode);
236 if (!scaledImage) {
237 SKGPU_LOG_W("AsyncRead failed because rescaling failed");
238 return params.fail();
239 }
240 (this->*asyncRead)(std::move(recorder),
241 params.withNewSource(scaledImage.get(), params.fDstImageInfo.bounds()),
242 extraParams...);
243 }
244
asyncRescaleAndReadPixels(const SkImage * image,const SkImageInfo & dstImageInfo,const SkIRect & srcRect,SkImage::RescaleGamma rescaleGamma,SkImage::RescaleMode rescaleMode,SkImage::ReadPixelsCallback callback,SkImage::ReadPixelsContext callbackContext)245 void Context::asyncRescaleAndReadPixels(const SkImage* image,
246 const SkImageInfo& dstImageInfo,
247 const SkIRect& srcRect,
248 SkImage::RescaleGamma rescaleGamma,
249 SkImage::RescaleMode rescaleMode,
250 SkImage::ReadPixelsCallback callback,
251 SkImage::ReadPixelsContext callbackContext) {
252 this->asyncRescaleAndReadImpl(&Context::asyncReadPixels,
253 rescaleGamma, rescaleMode,
254 {image, srcRect, dstImageInfo, callback, callbackContext});
255 }
256
asyncRescaleAndReadPixels(const SkSurface * surface,const SkImageInfo & dstImageInfo,const SkIRect & srcRect,SkImage::RescaleGamma rescaleGamma,SkImage::RescaleMode rescaleMode,SkImage::ReadPixelsCallback callback,SkImage::ReadPixelsContext callbackContext)257 void Context::asyncRescaleAndReadPixels(const SkSurface* surface,
258 const SkImageInfo& dstImageInfo,
259 const SkIRect& srcRect,
260 SkImage::RescaleGamma rescaleGamma,
261 SkImage::RescaleMode rescaleMode,
262 SkImage::ReadPixelsCallback callback,
263 SkImage::ReadPixelsContext callbackContext) {
264 sk_sp<SkImage> surfaceImage = SkSurfaces::AsImage(sk_ref_sp(surface));
265 if (!surfaceImage) {
266 // The surface is not texturable, so the only supported readback is if there's no rescaling
267 if (surface && asConstSB(surface)->isGraphiteBacked() &&
268 srcRect.size() == dstImageInfo.dimensions()) {
269 TextureProxy* proxy = static_cast<const Surface*>(surface)->backingTextureProxy();
270 return this->asyncReadTexture(/*recorder=*/nullptr,
271 {proxy, srcRect, dstImageInfo, callback, callbackContext},
272 surface->imageInfo().colorInfo());
273 }
274 // else fall through and let asyncRescaleAndReadPixels() invoke the callback when it detects
275 // the null image.
276 }
277 this->asyncRescaleAndReadPixels(surfaceImage.get(),
278 dstImageInfo,
279 srcRect,
280 rescaleGamma,
281 rescaleMode,
282 callback,
283 callbackContext);
284 }
285
asyncReadPixels(std::unique_ptr<Recorder> recorder,const AsyncParams<SkImage> & params)286 void Context::asyncReadPixels(std::unique_ptr<Recorder> recorder,
287 const AsyncParams<SkImage>& params) {
288 TRACE_EVENT2("skia.gpu", TRACE_FUNC,
289 "width", params.fSrcRect.width(),
290 "height", params.fSrcRect.height());
291 SkASSERT(params.validate());
292 SkASSERT(params.fSrcRect.size() == params.fDstImageInfo.dimensions());
293
294 const Caps* caps = fSharedContext->caps();
295 TextureProxyView view = AsView(params.fSrcImage);
296 if (!view || !caps->supportsReadPixels(view.proxy()->textureInfo())) {
297 // This is either a YUVA image (null view) or the texture can't be read directly, so
298 // perform a draw into a compatible texture format and/or flatten any YUVA planes to RGBA.
299 if (!recorder) {
300 recorder = this->makeInternalRecorder();
301 }
302 sk_sp<SkImage> flattened = CopyAsDraw(recorder.get(),
303 params.fSrcImage,
304 params.fSrcRect,
305 params.fDstImageInfo.colorInfo(),
306 Budgeted::kYes,
307 Mipmapped::kNo,
308 SkBackingFit::kApprox,
309 "AsyncReadPixelsFallbackTexture");
310 if (!flattened) {
311 SKGPU_LOG_W("AsyncRead failed because copy-as-drawing into a readable format failed");
312 return params.fail();
313 }
314 // Use the original fSrcRect and not flattened's size since it's approx-fit.
315 return this->asyncReadPixels(std::move(recorder),
316 params.withNewSource(flattened.get(),
317 SkIRect::MakeSize(params.fSrcRect.size())));
318 }
319
320 // Can copy directly from the image's texture
321 this->asyncReadTexture(std::move(recorder), params.withNewSource(view.proxy(), params.fSrcRect),
322 params.fSrcImage->imageInfo().colorInfo());
323 }
324
asyncReadTexture(std::unique_ptr<Recorder> recorder,const AsyncParams<TextureProxy> & params,const SkColorInfo & srcColorInfo)325 void Context::asyncReadTexture(std::unique_ptr<Recorder> recorder,
326 const AsyncParams<TextureProxy>& params,
327 const SkColorInfo& srcColorInfo) {
328 SkASSERT(params.fSrcRect.size() == params.fDstImageInfo.dimensions());
329
330 // We can get here directly from surface or testing-only read pixels, so re-validate
331 if (!params.validate()) {
332 return params.fail();
333 }
334 PixelTransferResult transferResult = this->transferPixels(recorder.get(),
335 params.fSrcImage,
336 srcColorInfo,
337 params.fDstImageInfo.colorInfo(),
338 params.fSrcRect);
339
340 if (!transferResult.fTransferBuffer) {
341 // TODO: try to do a synchronous readPixels instead
342 return params.fail();
343 }
344
345 this->finalizeAsyncReadPixels(std::move(recorder),
346 {&transferResult, 1},
347 params.fCallback,
348 params.fCallbackContext);
349 }
350
asyncRescaleAndReadPixelsYUV420(const SkImage * image,SkYUVColorSpace yuvColorSpace,sk_sp<SkColorSpace> dstColorSpace,const SkIRect & srcRect,const SkISize & dstSize,SkImage::RescaleGamma rescaleGamma,SkImage::RescaleMode rescaleMode,SkImage::ReadPixelsCallback callback,SkImage::ReadPixelsContext callbackContext)351 void Context::asyncRescaleAndReadPixelsYUV420(const SkImage* image,
352 SkYUVColorSpace yuvColorSpace,
353 sk_sp<SkColorSpace> dstColorSpace,
354 const SkIRect& srcRect,
355 const SkISize& dstSize,
356 SkImage::RescaleGamma rescaleGamma,
357 SkImage::RescaleMode rescaleMode,
358 SkImage::ReadPixelsCallback callback,
359 SkImage::ReadPixelsContext callbackContext) {
360 // Use kOpaque alpha type to signal that we don't read back the alpha channel
361 SkImageInfo dstImageInfo = SkImageInfo::Make(dstSize,
362 kRGBA_8888_SkColorType,
363 kOpaque_SkAlphaType,
364 std::move(dstColorSpace));
365 this->asyncRescaleAndReadImpl(&Context::asyncReadPixelsYUV420,
366 rescaleGamma, rescaleMode,
367 {image, srcRect, dstImageInfo, callback, callbackContext},
368 yuvColorSpace);
369 }
370
asyncRescaleAndReadPixelsYUV420(const SkSurface * surface,SkYUVColorSpace yuvColorSpace,sk_sp<SkColorSpace> dstColorSpace,const SkIRect & srcRect,const SkISize & dstSize,SkImage::RescaleGamma rescaleGamma,SkImage::RescaleMode rescaleMode,SkImage::ReadPixelsCallback callback,SkImage::ReadPixelsContext callbackContext)371 void Context::asyncRescaleAndReadPixelsYUV420(const SkSurface* surface,
372 SkYUVColorSpace yuvColorSpace,
373 sk_sp<SkColorSpace> dstColorSpace,
374 const SkIRect& srcRect,
375 const SkISize& dstSize,
376 SkImage::RescaleGamma rescaleGamma,
377 SkImage::RescaleMode rescaleMode,
378 SkImage::ReadPixelsCallback callback,
379 SkImage::ReadPixelsContext callbackContext) {
380 // YUV[A] readback requires the surface to be texturable since the plane conversion is performed
381 // by draws. If AsImage() returns null, the image version of asyncRescaleAndReadback will
382 // automatically fail.
383 // TODO: Is it worth performing an extra copy from 'surface' into a texture in order to succeed?
384 sk_sp<SkImage> surfaceImage = SkSurfaces::AsImage(sk_ref_sp(surface));
385 this->asyncRescaleAndReadPixelsYUV420(surfaceImage.get(),
386 yuvColorSpace,
387 dstColorSpace,
388 srcRect,
389 dstSize,
390 rescaleGamma,
391 rescaleMode,
392 callback,
393 callbackContext);
394 }
395
asyncRescaleAndReadPixelsYUVA420(const SkImage * image,SkYUVColorSpace yuvColorSpace,sk_sp<SkColorSpace> dstColorSpace,const SkIRect & srcRect,const SkISize & dstSize,SkImage::RescaleGamma rescaleGamma,SkImage::RescaleMode rescaleMode,SkImage::ReadPixelsCallback callback,SkImage::ReadPixelsContext callbackContext)396 void Context::asyncRescaleAndReadPixelsYUVA420(const SkImage* image,
397 SkYUVColorSpace yuvColorSpace,
398 sk_sp<SkColorSpace> dstColorSpace,
399 const SkIRect& srcRect,
400 const SkISize& dstSize,
401 SkImage::RescaleGamma rescaleGamma,
402 SkImage::RescaleMode rescaleMode,
403 SkImage::ReadPixelsCallback callback,
404 SkImage::ReadPixelsContext callbackContext) {
405 SkImageInfo dstImageInfo = SkImageInfo::Make(dstSize,
406 kRGBA_8888_SkColorType,
407 kPremul_SkAlphaType,
408 std::move(dstColorSpace));
409 this->asyncRescaleAndReadImpl(&Context::asyncReadPixelsYUV420,
410 rescaleGamma, rescaleMode,
411 {image, srcRect, dstImageInfo, callback, callbackContext},
412 yuvColorSpace);
413 }
414
asyncRescaleAndReadPixelsYUVA420(const SkSurface * surface,SkYUVColorSpace yuvColorSpace,sk_sp<SkColorSpace> dstColorSpace,const SkIRect & srcRect,const SkISize & dstSize,SkImage::RescaleGamma rescaleGamma,SkImage::RescaleMode rescaleMode,SkImage::ReadPixelsCallback callback,SkImage::ReadPixelsContext callbackContext)415 void Context::asyncRescaleAndReadPixelsYUVA420(const SkSurface* surface,
416 SkYUVColorSpace yuvColorSpace,
417 sk_sp<SkColorSpace> dstColorSpace,
418 const SkIRect& srcRect,
419 const SkISize& dstSize,
420 SkImage::RescaleGamma rescaleGamma,
421 SkImage::RescaleMode rescaleMode,
422 SkImage::ReadPixelsCallback callback,
423 SkImage::ReadPixelsContext callbackContext) {
424 sk_sp<SkImage> surfaceImage = SkSurfaces::AsImage(sk_ref_sp(surface));
425 this->asyncRescaleAndReadPixelsYUVA420(surfaceImage.get(),
426 yuvColorSpace,
427 dstColorSpace,
428 srcRect,
429 dstSize,
430 rescaleGamma,
431 rescaleMode,
432 callback,
433 callbackContext);
434 }
435
asyncReadPixelsYUV420(std::unique_ptr<Recorder> recorder,const AsyncParams<SkImage> & params,SkYUVColorSpace yuvColorSpace)436 void Context::asyncReadPixelsYUV420(std::unique_ptr<Recorder> recorder,
437 const AsyncParams<SkImage>& params,
438 SkYUVColorSpace yuvColorSpace) {
439 TRACE_EVENT2("skia.gpu", TRACE_FUNC,
440 "width", params.fSrcRect.width(),
441 "height", params.fSrcRect.height());
442 SkASSERT(params.fSrcRect.size() == params.fDstImageInfo.dimensions());
443
444 // The planes are always extracted via drawing, so create the Recorder if there isn't one yet.
445 if (!recorder) {
446 recorder = this->makeInternalRecorder();
447 }
448
449 // copyPlane renders the source image into an A8 image and sets up a transfer stored in 'result'
450 auto copyPlane = [&](SkImageInfo planeInfo,
451 std::string_view label,
452 float rgb2yuv[20],
453 const SkMatrix& texMatrix,
454 PixelTransferResult* result) {
455 sk_sp<Surface> dstSurface = Surface::MakeScratch(recorder.get(),
456 planeInfo,
457 std::move(label),
458 Budgeted::kYes,
459 Mipmapped::kNo,
460 SkBackingFit::kApprox);
461 if (!dstSurface) {
462 return false;
463 }
464
465 // Render the plane defined by rgb2yuv from srcImage into dstSurface
466 SkPaint paint;
467 const SkSamplingOptions sampling(SkFilterMode::kLinear, SkMipmapMode::kNone);
468 sk_sp<SkShader> imgShader = params.fSrcImage->makeShader(
469 SkTileMode::kClamp, SkTileMode::kClamp, sampling, texMatrix);
470 paint.setShader(std::move(imgShader));
471 paint.setBlendMode(SkBlendMode::kSrc);
472
473 if (rgb2yuv) {
474 // NOTE: The dstSurface's color space is set to the requested RGB dstColorSpace, so
475 // the rendered image is automatically converted to that RGB color space before the
476 // RGB->YUV color filter is evaluated, putting the plane data into the alpha channel.
477 paint.setColorFilter(SkColorFilters::Matrix(rgb2yuv));
478 }
479
480 SkCanvas* canvas = dstSurface->getCanvas();
481 canvas->drawPaint(paint);
482
483 // Manually flush the surface before transferPixels() is called to ensure the rendering
484 // operations run before the CopyTextureToBuffer task.
485 Flush(dstSurface);
486 // Must use planeInfo.bounds() for srcRect since dstSurface is kApprox-fit.
487 *result = this->transferPixels(recorder.get(),
488 dstSurface->backingTextureProxy(),
489 dstSurface->imageInfo().colorInfo(),
490 planeInfo.colorInfo(),
491 planeInfo.bounds());
492 return SkToBool(result->fTransferBuffer);
493 };
494
495 // Set up draws and transfers. This interleaves the drawing to a plane and the copy to the
496 // transfer buffer, which will allow the scratch A8 surface to be reused for each plane.
497 // TODO: Use one transfer buffer for all three planes to reduce map/unmap cost?
498 const bool readAlpha = params.fDstImageInfo.colorInfo().alphaType() != kOpaque_SkAlphaType;
499 SkImageInfo yaInfo = params.fDstImageInfo.makeColorType(kAlpha_8_SkColorType)
500 .makeAlphaType(kPremul_SkAlphaType);
501 SkImageInfo uvInfo = yaInfo.makeWH(yaInfo.width()/2, yaInfo.height()/2);
502 PixelTransferResult transfers[4];
503
504 float baseM[20];
505 SkColorMatrix_RGB2YUV(yuvColorSpace, baseM);
506 SkMatrix texMatrix = SkMatrix::Translate(params.fSrcRect.fLeft, params.fSrcRect.fTop);
507
508 // This matrix generates (r,g,b,a) = (0, 0, 0, y)
509 float yM[20];
510 std::fill_n(yM, 15, 0.f);
511 std::copy_n(baseM + 0, 5, yM + 15);
512 if (!copyPlane(yaInfo, "AsyncReadPixelsYPlane", yM, texMatrix, &transfers[0])) {
513 return params.fail();
514 }
515
516 // No matrix, straight copy of alpha channel
517 SkASSERT(baseM[15] == 0 &&
518 baseM[16] == 0 &&
519 baseM[17] == 0 &&
520 baseM[18] == 1 &&
521 baseM[19] == 0);
522 if (readAlpha &&
523 !copyPlane(yaInfo, "AsyncReadPixelsAPlane", nullptr, texMatrix, &transfers[3])) {
524 return params.fail();
525 }
526
527 // The UV planes are at half resolution compared to Y and A in 4:2:0
528 texMatrix.preScale(0.5f, 0.5f);
529
530 // This matrix generates (r,g,b,a) = (0, 0, 0, u)
531 float uM[20];
532 std::fill_n(uM, 15, 0.f);
533 std::copy_n(baseM + 5, 5, uM + 15);
534 if (!copyPlane(uvInfo, "AsyncReadPixelsUPlane", uM, texMatrix, &transfers[1])) {
535 return params.fail();
536 }
537
538 // This matrix generates (r,g,b,a) = (0, 0, 0, v)
539 float vM[20];
540 std::fill_n(vM, 15, 0.f);
541 std::copy_n(baseM + 10, 5, vM + 15);
542 if (!copyPlane(uvInfo, "AsyncReadPixelsVPlane", vM, texMatrix, &transfers[2])) {
543 return params.fail();
544 }
545
546 this->finalizeAsyncReadPixels(std::move(recorder),
547 {transfers, readAlpha ? 4 : 3},
548 params.fCallback,
549 params.fCallbackContext);
550 }
551
finalizeAsyncReadPixels(std::unique_ptr<Recorder> recorder,SkSpan<PixelTransferResult> transferResults,SkImage::ReadPixelsCallback callback,SkImage::ReadPixelsContext callbackContext)552 void Context::finalizeAsyncReadPixels(std::unique_ptr<Recorder> recorder,
553 SkSpan<PixelTransferResult> transferResults,
554 SkImage::ReadPixelsCallback callback,
555 SkImage::ReadPixelsContext callbackContext) {
556 // If the async readback work required a Recorder, insert the recording with all of the
557 // accumulated work (which includes any copies). Otherwise, for pure copy readbacks,
558 // transferPixels() already added the tasks directly to the QueueManager.
559 if (recorder) {
560 std::unique_ptr<Recording> recording = recorder->snap();
561 if (!recording) {
562 callback(callbackContext, nullptr);
563 return;
564 }
565 InsertRecordingInfo recordingInfo;
566 recordingInfo.fRecording = recording.get();
567 if (!this->insertRecording(recordingInfo)) {
568 callback(callbackContext, nullptr);
569 return;
570 }
571 }
572
573 // Set up FinishContext and add transfer commands to queue
574 struct AsyncReadFinishContext {
575 SkImage::ReadPixelsCallback* fClientCallback;
576 SkImage::ReadPixelsContext fClientContext;
577 ClientMappedBufferManager* fMappedBufferManager;
578 std::array<PixelTransferResult, 4> fTransferResults;
579 };
580
581 auto finishContext = std::make_unique<AsyncReadFinishContext>();
582 finishContext->fClientCallback = callback;
583 finishContext->fClientContext = callbackContext;
584 finishContext->fMappedBufferManager = fMappedBufferManager.get();
585
586 SkASSERT(transferResults.size() <= std::size(finishContext->fTransferResults));
587 skia_private::STArray<4, sk_sp<Buffer>> buffersToAsyncMap;
588 for (size_t i = 0; i < transferResults.size(); ++i) {
589 finishContext->fTransferResults[i] = std::move(transferResults[i]);
590 if (fSharedContext->caps()->bufferMapsAreAsync()) {
591 buffersToAsyncMap.push_back(finishContext->fTransferResults[i].fTransferBuffer);
592 }
593 }
594
595 InsertFinishInfo info;
596 info.fFinishedContext = finishContext.release();
597 info.fFinishedProc = [](GpuFinishedContext c, CallbackResult status) {
598 std::unique_ptr<const AsyncReadFinishContext> context(
599 reinterpret_cast<const AsyncReadFinishContext*>(c));
600 using AsyncReadResult = skgpu::TAsyncReadResult<Buffer, ContextID, PixelTransferResult>;
601
602 ClientMappedBufferManager* manager = context->fMappedBufferManager;
603 std::unique_ptr<AsyncReadResult> result;
604 if (status == CallbackResult::kSuccess) {
605 result = std::make_unique<AsyncReadResult>(manager->ownerID());
606 }
607 for (const auto& r : context->fTransferResults) {
608 if (!r.fTransferBuffer) {
609 break;
610 }
611 if (result && !result->addTransferResult(r, r.fSize, r.fRowBytes, manager)) {
612 result.reset();
613 }
614 // If we didn't get this buffer into the mapped buffer manager then make sure it gets
615 // unmapped if it has a pending or completed async map.
616 if (!result && r.fTransferBuffer->isUnmappable()) {
617 r.fTransferBuffer->unmap();
618 }
619 }
620 (*context->fClientCallback)(context->fClientContext, std::move(result));
621 };
622
623 // If addFinishInfo() fails, it invokes the finish callback automatically, which handles all the
624 // required clean up for us, just log an error message. The buffers will never be mapped and
625 // thus don't need an unmap.
626 if (!fQueueManager->addFinishInfo(info, fResourceProvider.get(), buffersToAsyncMap)) {
627 SKGPU_LOG_E("Failed to register finish callbacks for asyncReadPixels.");
628 return;
629 }
630 }
631
transferPixels(Recorder * recorder,const TextureProxy * srcProxy,const SkColorInfo & srcColorInfo,const SkColorInfo & dstColorInfo,const SkIRect & srcRect)632 Context::PixelTransferResult Context::transferPixels(Recorder* recorder,
633 const TextureProxy* srcProxy,
634 const SkColorInfo& srcColorInfo,
635 const SkColorInfo& dstColorInfo,
636 const SkIRect& srcRect) {
637 SkASSERT(SkIRect::MakeSize(srcProxy->dimensions()).contains(srcRect));
638 SkASSERT(SkColorInfoIsValid(dstColorInfo));
639
640 const Caps* caps = fSharedContext->caps();
641 if (!srcProxy || !caps->supportsReadPixels(srcProxy->textureInfo())) {
642 return {};
643 }
644
645 const SkColorType srcColorType = srcColorInfo.colorType();
646 SkColorType supportedColorType;
647 bool isRGB888Format;
648 std::tie(supportedColorType, isRGB888Format) =
649 caps->supportedReadPixelsColorType(srcColorType,
650 srcProxy->textureInfo(),
651 dstColorInfo.colorType());
652 if (supportedColorType == kUnknown_SkColorType) {
653 return {};
654 }
655
656 // Fail if read color type does not have all of dstCT's color channels and those missing color
657 // channels are in the src.
658 uint32_t dstChannels = SkColorTypeChannelFlags(dstColorInfo.colorType());
659 uint32_t legalReadChannels = SkColorTypeChannelFlags(supportedColorType);
660 uint32_t srcChannels = SkColorTypeChannelFlags(srcColorType);
661 if ((~legalReadChannels & dstChannels) & srcChannels) {
662 return {};
663 }
664
665 int bpp = isRGB888Format ? 3 : SkColorTypeBytesPerPixel(supportedColorType);
666 size_t rowBytes = caps->getAlignedTextureDataRowBytes(bpp * srcRect.width());
667 size_t size = SkAlignTo(rowBytes * srcRect.height(), caps->requiredTransferBufferAlignment());
668 sk_sp<Buffer> buffer = fResourceProvider->findOrCreateBuffer(
669 size, BufferType::kXferGpuToCpu, AccessPattern::kHostVisible, "TransferToCpu");
670 if (!buffer) {
671 return {};
672 }
673
674 // Set up copy task. Since we always use a new buffer the offset can be 0 and we don't need to
675 // worry about aligning it to the required transfer buffer alignment.
676 sk_sp<CopyTextureToBufferTask> copyTask = CopyTextureToBufferTask::Make(sk_ref_sp(srcProxy),
677 srcRect,
678 buffer,
679 /*bufferOffset=*/0,
680 rowBytes);
681 const bool addTasksDirectly = !SkToBool(recorder);
682
683 if (!copyTask || (addTasksDirectly && !fQueueManager->addTask(copyTask.get(), this))) {
684 return {};
685 } else if (!addTasksDirectly) {
686 // Add the task to the Recorder instead of the QueueManager if that's been required for
687 // collecting tasks to prepare the copied textures.
688 recorder->priv().add(std::move(copyTask));
689 }
690 sk_sp<SynchronizeToCpuTask> syncTask = SynchronizeToCpuTask::Make(buffer);
691 if (!syncTask || (addTasksDirectly && !fQueueManager->addTask(syncTask.get(), this))) {
692 return {};
693 } else if (!addTasksDirectly) {
694 recorder->priv().add(std::move(syncTask));
695 }
696
697 PixelTransferResult result;
698 result.fTransferBuffer = std::move(buffer);
699 result.fSize = srcRect.size();
700 // srcColorInfo describes the texture; readColorInfo describes the result of the copy-to-buffer,
701 // which may be different; dstColorInfo is what we have to transform it into when invoking the
702 // async callbacks.
703 SkColorInfo readColorInfo = srcColorInfo.makeColorType(supportedColorType);
704 if (readColorInfo != dstColorInfo || isRGB888Format) {
705 SkISize dims = srcRect.size();
706 SkImageInfo srcInfo = SkImageInfo::Make(dims, readColorInfo);
707 SkImageInfo dstInfo = SkImageInfo::Make(dims, dstColorInfo);
708 result.fRowBytes = dstInfo.minRowBytes();
709 result.fPixelConverter = [dstInfo, srcInfo, rowBytes, isRGB888Format](
710 void* dst, const void* src) {
711 SkAutoPixmapStorage temp;
712 size_t srcRowBytes = rowBytes;
713 if (isRGB888Format) {
714 temp.alloc(srcInfo);
715 size_t tRowBytes = temp.rowBytes();
716 auto* sRow = reinterpret_cast<const char*>(src);
717 auto* tRow = reinterpret_cast<char*>(temp.writable_addr());
718 for (int y = 0; y < srcInfo.height(); ++y, sRow += srcRowBytes, tRow += tRowBytes) {
719 for (int x = 0; x < srcInfo.width(); ++x) {
720 auto s = sRow + x*3;
721 auto t = tRow + x*sizeof(uint32_t);
722 memcpy(t, s, 3);
723 t[3] = static_cast<char>(0xFF);
724 }
725 }
726 src = temp.addr();
727 srcRowBytes = tRowBytes;
728 }
729 SkAssertResult(SkConvertPixels(dstInfo, dst, dstInfo.minRowBytes(),
730 srcInfo, src, srcRowBytes));
731 };
732 } else {
733 result.fRowBytes = rowBytes;
734 }
735
736 return result;
737 }
738
checkForFinishedWork(SyncToCpu syncToCpu)739 void Context::checkForFinishedWork(SyncToCpu syncToCpu) {
740 ASSERT_SINGLE_OWNER
741
742 fQueueManager->checkForFinishedWork(syncToCpu);
743 fMappedBufferManager->process();
744 }
745
checkAsyncWorkCompletion()746 void Context::checkAsyncWorkCompletion() {
747 this->checkForFinishedWork(SyncToCpu::kNo);
748 }
749
deleteBackendTexture(const BackendTexture & texture)750 void Context::deleteBackendTexture(const BackendTexture& texture) {
751 ASSERT_SINGLE_OWNER
752
753 if (!texture.isValid() || texture.backend() != this->backend()) {
754 return;
755 }
756 fResourceProvider->deleteBackendTexture(texture);
757 }
758
freeGpuResources()759 void Context::freeGpuResources() {
760 ASSERT_SINGLE_OWNER
761
762 this->checkAsyncWorkCompletion();
763
764 fResourceProvider->freeGpuResources();
765 }
766
performDeferredCleanup(std::chrono::milliseconds msNotUsed)767 void Context::performDeferredCleanup(std::chrono::milliseconds msNotUsed) {
768 ASSERT_SINGLE_OWNER
769
770 this->checkAsyncWorkCompletion();
771
772 auto purgeTime = skgpu::StdSteadyClock::now() - msNotUsed;
773 fResourceProvider->purgeResourcesNotUsedSince(purgeTime);
774 }
775
currentBudgetedBytes() const776 size_t Context::currentBudgetedBytes() const {
777 ASSERT_SINGLE_OWNER
778 return fResourceProvider->getResourceCacheCurrentBudgetedBytes();
779 }
780
maxBudgetedBytes() const781 size_t Context::maxBudgetedBytes() const {
782 ASSERT_SINGLE_OWNER
783 return fResourceProvider->getResourceCacheLimit();
784 }
785
dumpMemoryStatistics(SkTraceMemoryDump * traceMemoryDump) const786 void Context::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
787 ASSERT_SINGLE_OWNER
788 fResourceProvider->dumpMemoryStatistics(traceMemoryDump);
789 // TODO: What is the graphite equivalent for the text blob cache and how do we print out its
790 // used bytes here (see Ganesh implementation).
791 }
792
isDeviceLost() const793 bool Context::isDeviceLost() const {
794 return fSharedContext->isDeviceLost();
795 }
796
maxTextureSize() const797 int Context::maxTextureSize() const {
798 return fSharedContext->caps()->maxTextureSize();
799 }
800
supportsProtectedContent() const801 bool Context::supportsProtectedContent() const {
802 return fSharedContext->isProtected() == Protected::kYes;
803 }
804
805 ///////////////////////////////////////////////////////////////////////////////////
806
807 #if defined(GRAPHITE_TEST_UTILS)
readPixels(const SkPixmap & pm,const TextureProxy * textureProxy,const SkImageInfo & srcImageInfo,int srcX,int srcY)808 bool ContextPriv::readPixels(const SkPixmap& pm,
809 const TextureProxy* textureProxy,
810 const SkImageInfo& srcImageInfo,
811 int srcX, int srcY) {
812 auto rect = SkIRect::MakeXYWH(srcX, srcY, pm.width(), pm.height());
813 struct AsyncContext {
814 bool fCalled = false;
815 std::unique_ptr<const SkImage::AsyncReadResult> fResult;
816 } asyncContext;
817
818 auto asyncCallback = [](void* c, std::unique_ptr<const SkImage::AsyncReadResult> out) {
819 auto context = static_cast<AsyncContext*>(c);
820 context->fResult = std::move(out);
821 context->fCalled = true;
822 };
823
824 const SkColorInfo& srcColorInfo = srcImageInfo.colorInfo();
825
826 // This is roughly equivalent to the logic taken in asyncRescaleAndRead(SkSurface) to either
827 // try the image-based readback (with copy-as-draw fallbacks) or read the texture directly
828 // if it supports reading.
829 if (!fContext->fSharedContext->caps()->supportsReadPixels(textureProxy->textureInfo())) {
830 // Since this is a synchronous testing-only API, callers should have flushed any pending
831 // work that modifies this texture proxy already. This means we don't have to worry about
832 // re-wrapping the proxy in a new Image (that wouldn't tbe connected to any Device, etc.).
833 sk_sp<SkImage> image{new Image(TextureProxyView(sk_ref_sp(textureProxy)), srcColorInfo)};
834 fContext->asyncReadPixels(/*recorder=*/nullptr,
835 {image.get(), rect, pm.info(), asyncCallback, &asyncContext});
836 } else {
837 fContext->asyncReadTexture(/*recorder=*/nullptr,
838 {textureProxy, rect, pm.info(), asyncCallback, &asyncContext},
839 srcImageInfo.colorInfo());
840 }
841
842 if (fContext->fSharedContext->caps()->allowCpuSync()) {
843 fContext->submit(SyncToCpu::kYes);
844 } else {
845 fContext->submit(SyncToCpu::kNo);
846 if (fContext->fSharedContext->backend() == BackendApi::kDawn) {
847 while (!asyncContext.fCalled) {
848 #if defined(SK_DAWN)
849 auto dawnContext = static_cast<DawnSharedContext*>(fContext->fSharedContext.get());
850 dawnContext->device().Tick();
851 fContext->checkAsyncWorkCompletion();
852 #endif
853 }
854 } else {
855 SK_ABORT("Only Dawn supports non-synching contexts.");
856 }
857 }
858 SkASSERT(asyncContext.fCalled);
859 if (!asyncContext.fResult) {
860 return false;
861 }
862 SkRectMemcpy(pm.writable_addr(), pm.rowBytes(), asyncContext.fResult->data(0),
863 asyncContext.fResult->rowBytes(0), pm.info().minRowBytes(),
864 pm.height());
865 return true;
866 }
867
deregisterRecorder(const Recorder * recorder)868 void ContextPriv::deregisterRecorder(const Recorder* recorder) {
869 SKGPU_ASSERT_SINGLE_OWNER(fContext->singleOwner())
870 for (auto it = fContext->fTrackedRecorders.begin();
871 it != fContext->fTrackedRecorders.end();
872 it++) {
873 if (*it == recorder) {
874 fContext->fTrackedRecorders.erase(it);
875 return;
876 }
877 }
878 }
879
supportsPathRendererStrategy(PathRendererStrategy strategy)880 bool ContextPriv::supportsPathRendererStrategy(PathRendererStrategy strategy) {
881 AtlasProvider::PathAtlasFlagsBitMask pathAtlasFlags =
882 AtlasProvider::QueryPathAtlasSupport(this->caps());
883 switch (strategy) {
884 case PathRendererStrategy::kDefault:
885 return true;
886 case PathRendererStrategy::kComputeAnalyticAA:
887 case PathRendererStrategy::kComputeMSAA16:
888 case PathRendererStrategy::kComputeMSAA8:
889 return SkToBool(pathAtlasFlags & AtlasProvider::PathAtlasFlags::kCompute);
890 case PathRendererStrategy::kRasterAA:
891 return SkToBool(pathAtlasFlags & AtlasProvider::PathAtlasFlags::kRaster);
892 case PathRendererStrategy::kTessellation:
893 return true;
894 }
895
896 return false;
897 }
898
899 #endif
900
901 ///////////////////////////////////////////////////////////////////////////////////
902
MakeContext(sk_sp<SharedContext> sharedContext,std::unique_ptr<QueueManager> queueManager,const ContextOptions & options)903 std::unique_ptr<Context> ContextCtorAccessor::MakeContext(
904 sk_sp<SharedContext> sharedContext,
905 std::unique_ptr<QueueManager> queueManager,
906 const ContextOptions& options) {
907 auto context = std::unique_ptr<Context>(new Context(std::move(sharedContext),
908 std::move(queueManager),
909 options));
910 if (context && context->finishInitialization()) {
911 return context;
912 } else {
913 return nullptr;
914 }
915 }
916
917 } // namespace skgpu::graphite
918