• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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