Flutter Engine
The Flutter Engine
Context.cpp
Go to the documentation of this file.
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
9
24#include "src/core/SkYUVMath.h"
55
57
58#if defined(GRAPHITE_TEST_UTILS)
60#if defined(SK_DAWN)
62#include "webgpu/webgpu_cpp.h" // NO_G3_REWRITE
63#endif
64#endif
65
66namespace skgpu::graphite {
67
68#define ASSERT_SINGLE_OWNER SKGPU_ASSERT_SINGLE_OWNER(this->singleOwner())
69
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//--------------------------------------------------------------------------------------------------
81 std::unique_ptr<QueueManager> queueManager,
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,
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
100#if defined(GRAPHITE_TEST_UTILS)
102 for (auto& recorder : fTrackedRecorders) {
103 recorder->priv().setContext(nullptr);
104 }
105#endif
106}
107
108bool 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());
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 }
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
130BackendApi Context::backend() const { return fSharedContext->backend(); }
131
132std::unique_ptr<Recorder> Context::makeRecorder(const RecorderOptions& options) {
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
145std::unique_ptr<Recorder> Context::makeInternalRecorder() const {
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
158
159 return fQueueManager->addRecording(info, this);
160}
161
162bool Context::submit(SyncToCpu syncToCpu) {
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
175bool Context::hasUnfinishedGpuWork() const { return fQueueManager->hasUnfinishedGpuWork(); }
176
177template <typename SrcPixels>
178struct Context::AsyncParams {
179 const SrcPixels* fSrcImage;
180 SkIRect fSrcRect;
181 SkImageInfo fDstImageInfo;
182
184 SkImage::ReadPixelsContext fCallbackContext;
185
186 template <typename S>
187 AsyncParams<S> withNewSource(const S* newPixels, const SkIRect& newSrcRect) const {
188 return AsyncParams<S>{newPixels, newSrcRect,
189 fDstImageInfo, fCallback, fCallbackContext};
190 }
191
192 void fail() const {
193 (*fCallback)(fCallbackContext, nullptr);
194 }
195
196 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
213template <typename ReadFn, typename... ExtraArgs>
214void 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
246 const SkImageInfo& dstImageInfo,
247 const SkIRect& srcRect,
248 SkImage::RescaleGamma rescaleGamma,
249 SkImage::RescaleMode rescaleMode,
251 SkImage::ReadPixelsContext callbackContext) {
252 this->asyncRescaleAndReadImpl(&Context::asyncReadPixels,
253 rescaleGamma, rescaleMode,
254 {image, srcRect, dstImageInfo, callback, callbackContext});
255}
256
258 const SkImageInfo& dstImageInfo,
259 const SkIRect& srcRect,
260 SkImage::RescaleGamma rescaleGamma,
261 SkImage::RescaleMode rescaleMode,
263 SkImage::ReadPixelsContext callbackContext) {
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
286void 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(),
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
325void 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
352 SkYUVColorSpace yuvColorSpace,
353 sk_sp<SkColorSpace> dstColorSpace,
354 const SkIRect& srcRect,
355 const SkISize& dstSize,
356 SkImage::RescaleGamma rescaleGamma,
357 SkImage::RescaleMode rescaleMode,
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,
364 std::move(dstColorSpace));
365 this->asyncRescaleAndReadImpl(&Context::asyncReadPixelsYUV420,
366 rescaleGamma, rescaleMode,
367 {image, srcRect, dstImageInfo, callback, callbackContext},
368 yuvColorSpace);
369}
370
372 SkYUVColorSpace yuvColorSpace,
373 sk_sp<SkColorSpace> dstColorSpace,
374 const SkIRect& srcRect,
375 const SkISize& dstSize,
376 SkImage::RescaleGamma rescaleGamma,
377 SkImage::RescaleMode rescaleMode,
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?
385 this->asyncRescaleAndReadPixelsYUV420(surfaceImage.get(),
386 yuvColorSpace,
387 dstColorSpace,
388 srcRect,
389 dstSize,
390 rescaleGamma,
391 rescaleMode,
392 callback,
393 callbackContext);
394}
395
397 SkYUVColorSpace yuvColorSpace,
398 sk_sp<SkColorSpace> dstColorSpace,
399 const SkIRect& srcRect,
400 const SkISize& dstSize,
401 SkImage::RescaleGamma rescaleGamma,
402 SkImage::RescaleMode rescaleMode,
404 SkImage::ReadPixelsContext callbackContext) {
405 SkImageInfo dstImageInfo = SkImageInfo::Make(dstSize,
408 std::move(dstColorSpace));
409 this->asyncRescaleAndReadImpl(&Context::asyncReadPixelsYUV420,
410 rescaleGamma, rescaleMode,
411 {image, srcRect, dstImageInfo, callback, callbackContext},
412 yuvColorSpace);
413}
414
416 SkYUVColorSpace yuvColorSpace,
417 sk_sp<SkColorSpace> dstColorSpace,
418 const SkIRect& srcRect,
419 const SkISize& dstSize,
420 SkImage::RescaleGamma rescaleGamma,
421 SkImage::RescaleMode rescaleMode,
423 SkImage::ReadPixelsContext callbackContext) {
425 this->asyncRescaleAndReadPixelsYUVA420(surfaceImage.get(),
426 yuvColorSpace,
427 dstColorSpace,
428 srcRect,
429 dstSize,
430 rescaleGamma,
431 rescaleMode,
432 callback,
433 callbackContext);
434}
435
436void 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),
461 if (!dstSurface) {
462 return false;
463 }
464
465 // Render the plane defined by rgb2yuv from srcImage into dstSurface
468 sk_sp<SkShader> imgShader = params.fSrcImage->makeShader(
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
552void Context::finalizeAsyncReadPixels(std::unique_ptr<Recorder> recorder,
553 SkSpan<PixelTransferResult> transferResults,
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));
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));
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
632Context::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(
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.
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 }
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) {
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
739void Context::checkForFinishedWork(SyncToCpu syncToCpu) {
741
742 fQueueManager->checkForFinishedWork(syncToCpu);
743 fMappedBufferManager->process();
744}
745
747 this->checkForFinishedWork(SyncToCpu::kNo);
748}
749
752
753 if (!texture.isValid() || texture.backend() != this->backend()) {
754 return;
755 }
756 fResourceProvider->deleteBackendTexture(texture);
757}
758
761
763
764 fResourceProvider->freeGpuResources();
765}
766
767void Context::performDeferredCleanup(std::chrono::milliseconds msNotUsed) {
769
771
772 auto purgeTime = skgpu::StdSteadyClock::now() - msNotUsed;
773 fResourceProvider->purgeResourcesNotUsedSince(purgeTime);
774}
775
778 return fResourceProvider->getResourceCacheCurrentBudgetedBytes();
779}
780
783 return fResourceProvider->getResourceCacheLimit();
784}
785
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
794 return fSharedContext->isDeviceLost();
795}
796
798 return fSharedContext->caps()->maxTextureSize();
799}
800
802 return fSharedContext->isProtected() == Protected::kYes;
803}
804
805///////////////////////////////////////////////////////////////////////////////////
806
807#if defined(GRAPHITE_TEST_UTILS)
808bool 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
868void 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
880bool ContextPriv::supportsPathRendererStrategy(PathRendererStrategy strategy) {
883 switch (strategy) {
885 return true;
889 return SkToBool(pathAtlasFlags & AtlasProvider::PathAtlasFlags::kCompute);
891 return SkToBool(pathAtlasFlags & AtlasProvider::PathAtlasFlags::kRaster);
893 return true;
894 }
895
896 return false;
897}
898
899#endif
900
901///////////////////////////////////////////////////////////////////////////////////
902
903std::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
const char * options
#define ASSERT_SINGLE_OWNER
Definition: Context.cpp:68
static void fail(const SkString &err)
Definition: DM.cpp:234
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
for(const auto glyph :glyphs)
Definition: FontMgrTest.cpp:52
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
#define SKGPU_LOG_E(fmt,...)
Definition: Log.h:38
#define SKGPU_LOG_W(fmt,...)
Definition: Log.h:40
#define SKGPU_ASSERT_SINGLE_OWNER(obj)
Definition: SingleOwner.h:69
static constexpr size_t SkAlignTo(size_t x, size_t alignment)
Definition: SkAlign.h:33
@ kOpaque_SkAlphaType
pixel is opaque
Definition: SkAlphaType.h:28
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition: SkAlphaType.h:29
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkColorType
Definition: SkColorType.h:19
@ kAlpha_8_SkColorType
pixel with alpha in 8-bit byte
Definition: SkColorType.h:21
@ kRGBA_8888_SkColorType
pixel with 8 bits for red, green, blue, alpha; in 32-bit word
Definition: SkColorType.h:24
@ kUnknown_SkColorType
uninitialized
Definition: SkColorType.h:20
bool SkConvertPixels(const SkImageInfo &dstInfo, void *dstPixels, size_t dstRB, const SkImageInfo &srcInfo, const void *srcPixels, size_t srcRB)
static uint32_t SkColorTypeChannelFlags(SkColorType ct)
static bool SkColorInfoIsValid(const SkColorInfo &info)
static bool SkImageInfoIsValid(const SkImageInfo &info)
SK_API int SkColorTypeBytesPerPixel(SkColorType ct)
Definition: SkImageInfo.cpp:16
SkYUVColorSpace
Definition: SkImageInfo.h:68
static void SkRectMemcpy(void *dst, size_t dstRB, const void *src, size_t srcRB, size_t trimRowBytes, int rowCount)
Definition: SkRectMemcpy.h:16
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
LoopControlFlowInfo fResult
static const SkSurface_Base * asConstSB(const SkSurface *surface)
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
#define TRACE_FUNC
Definition: SkTraceEvent.h:30
static constexpr uint32_t SK_InvalidUniqueID
Definition: SkTypes.h:196
static constexpr uint32_t SK_InvalidGenID
Definition: SkTypes.h:192
void SkColorMatrix_RGB2YUV(SkYUVColorSpace cs, float m[20])
Definition: SkYUVMath.cpp:389
void alloc(const SkImageInfo &)
void drawPaint(const SkPaint &paint)
Definition: SkCanvas.cpp:1668
static sk_sp< SkColorFilter > Matrix(const SkColorMatrix &)
SkColorInfo makeColorType(SkColorType newColorType) const
Definition: SkImageInfo.cpp:80
SkColorType colorType() const
Definition: SkImageInfo.h:140
void * ReadPixelsContext
Definition: SkImage.h:578
RescaleMode
Definition: SkImage.h:587
RescaleGamma
Definition: SkImage.h:585
void(ReadPixelsContext, std::unique_ptr< const AsyncReadResult >) ReadPixelsCallback
Definition: SkImage.h:583
static SkMatrix Translate(SkScalar dx, SkScalar dy)
Definition: SkMatrix.h:91
SkMatrix & preScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py)
Definition: SkMatrix.cpp:315
size_t rowBytes() const
Definition: SkPixmap.h:145
int width() const
Definition: SkPixmap.h:160
const SkImageInfo & info() const
Definition: SkPixmap.h:135
void * writable_addr() const
Definition: SkPixmap.h:483
const void * addr() const
Definition: SkPixmap.h:153
int height() const
Definition: SkPixmap.h:166
constexpr size_t size() const
Definition: SkSpan_impl.h:95
T * get() const
Definition: SkRefCnt.h:303
static PathAtlasFlagsBitMask QueryPathAtlasSupport(const Caps *)
SkEnumBitMask< PathAtlasFlags > PathAtlasFlagsBitMask
Definition: AtlasProvider.h:43
virtual bool supportsReadPixels(const TextureInfo &textureInfo) const =0
static std::unique_ptr< Context > MakeContext(sk_sp< SharedContext >, std::unique_ptr< QueueManager >, const ContextOptions &)
Definition: Context.cpp:903
const Caps * caps() const
Definition: ContextPriv.h:32
static Context::ContextID Next()
Definition: Context.cpp:70
size_t maxBudgetedBytes() const
Definition: Context.cpp:781
ContextID contextID() const
Definition: Context.h:203
BackendApi backend() const
Definition: Context.cpp:130
int maxTextureSize() const
Definition: Context.cpp:797
void asyncRescaleAndReadPixelsYUV420(const SkImage *, SkYUVColorSpace yuvColorSpace, sk_sp< SkColorSpace > dstColorSpace, const SkIRect &srcRect, const SkISize &dstSize, SkImage::RescaleGamma rescaleGamma, SkImage::RescaleMode rescaleMode, SkImage::ReadPixelsCallback callback, SkImage::ReadPixelsContext context)
Definition: Context.cpp:351
std::unique_ptr< Recorder > makeRecorder(const RecorderOptions &={})
Definition: Context.cpp:132
void asyncRescaleAndReadPixelsYUVA420(const SkImage *, SkYUVColorSpace yuvColorSpace, sk_sp< SkColorSpace > dstColorSpace, const SkIRect &srcRect, const SkISize &dstSize, SkImage::RescaleGamma rescaleGamma, SkImage::RescaleMode rescaleMode, SkImage::ReadPixelsCallback callback, SkImage::ReadPixelsContext context)
Definition: Context.cpp:396
void performDeferredCleanup(std::chrono::milliseconds msNotUsed)
Definition: Context.cpp:767
bool submit(SyncToCpu=SyncToCpu::kNo)
Definition: Context.cpp:162
void deleteBackendTexture(const BackendTexture &)
Definition: Context.cpp:750
bool insertRecording(const InsertRecordingInfo &)
Definition: Context.cpp:156
bool supportsProtectedContent() const
Definition: Context.cpp:801
size_t currentBudgetedBytes() const
Definition: Context.cpp:776
Context(const Context &)=delete
void dumpMemoryStatistics(SkTraceMemoryDump *traceMemoryDump) const
Definition: Context.cpp:786
void asyncRescaleAndReadPixels(const SkImage *image, const SkImageInfo &dstImageInfo, const SkIRect &srcRect, SkImage::RescaleGamma rescaleGamma, SkImage::RescaleMode rescaleMode, SkImage::ReadPixelsCallback callback, SkImage::ReadPixelsContext context)
Definition: Context.cpp:245
bool hasUnfinishedGpuWork() const
Definition: Context.cpp:175
bool isDeviceLost() const
Definition: Context.cpp:793
static sk_sp< CopyTextureToBufferTask > Make(sk_sp< TextureProxy >, SkIRect srcRect, sk_sp< Buffer >, size_t bufferOffset, size_t bufferRowBytes)
Definition: CopyTask.cpp:63
void add(sk_sp< Task >)
Definition: Recorder.cpp:485
static sk_sp< Surface > MakeScratch(Recorder *recorder, const SkImageInfo &info, std::string_view label, Budgeted budgeted=Budgeted::kYes, Mipmapped mipmapped=Mipmapped::kNo, SkBackingFit backingFit=SkBackingFit::kApprox)
static sk_sp< SynchronizeToCpuTask > Make(sk_sp< Buffer >)
const TextureInfo & textureInfo() const
Definition: TextureProxy.h:38
const Paint & paint
Definition: color_source.cc:38
static const char * begin(const StringSlice &s)
Definition: editor.cpp:252
const EmbeddedViewParams * params
VkSurfaceKHR surface
Definition: main.cc:49
struct MyStruct s
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
GAsyncResult * result
FlTexture * texture
double y
double x
sk_sp< const SkImage > image
Definition: SkRecords.h:269
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
SkSamplingOptions sampling
Definition: SkRecords.h:337
SK_API sk_sp< SkImage > AsImage(sk_sp< const SkSurface >)
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font manager
Definition: switches.h:218
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
CanvasImage Image
Definition: dart_ui.cc:55
dst
Definition: cp.py:12
sk_sp< SkImage > RescaleImage(Recorder *recorder, const SkImage *srcImage, SkIRect srcIRect, const SkImageInfo &dstInfo, SkImage::RescaleGamma rescaleGamma, SkImage::RescaleMode rescaleMode)
void Flush(sk_sp< SkSurface > surface)
void * GpuFinishedContext
Definition: GraphiteTypes.h:29
TextureProxyView AsView(const SkImage *image)
sk_sp< Image > CopyAsDraw(Recorder *recorder, const SkImage *image, const SkIRect &subset, const SkColorInfo &dstColorInfo, Budgeted budgeted, Mipmapped mipmapped, SkBackingFit backingFit, std::string_view label)
BackendApi
Definition: GpuTypes.h:22
CallbackResult
Definition: GpuTypes.h:45
Definition: ref_ptr.h:256
Definition: SkRect.h:32
constexpr SkISize size() const
Definition: SkRect.h:172
constexpr int32_t height() const
Definition: SkRect.h:165
static constexpr SkIRect MakeSize(const SkISize &size)
Definition: SkRect.h:66
constexpr int32_t width() const
Definition: SkRect.h:158
static constexpr SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h)
Definition: SkRect.h:104
bool contains(int32_t x, int32_t y) const
Definition: SkRect.h:463
Definition: SkSize.h:16
SkImageInfo makeWH(int newWidth, int newHeight) const
Definition: SkImageInfo.h:444
const SkColorInfo & colorInfo() const
Definition: SkImageInfo.h:404
SkIRect bounds() const
Definition: SkImageInfo.h:427
size_t minRowBytes() const
Definition: SkImageInfo.h:517
SkISize dimensions() const
Definition: SkImageInfo.h:421
int width() const
Definition: SkImageInfo.h:365
static SkImageInfo Make(int width, int height, SkColorType ct, SkAlphaType at)
int height() const
Definition: SkImageInfo.h:371
ContextOptionsPriv * fOptionsPriv
const uintptr_t id
#define TRACE_EVENT2(category_group, name, arg1_name, arg1_val, arg2_name, arg2_val)
Definition: trace_event.h:145
skgpu::graphite::Recorder Recorder