• 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/Recorder.h"
9 
10 #include "include/core/SkBitmap.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColorSpace.h"
13 #include "include/core/SkTraceMemoryDump.h"
14 #include "include/effects/SkRuntimeEffect.h"
15 #include "include/gpu/graphite/BackendTexture.h"
16 #include "include/gpu/graphite/GraphiteTypes.h"
17 #include "include/gpu/graphite/ImageProvider.h"
18 #include "include/gpu/graphite/Recording.h"
19 
20 #include "src/core/SkCompressedDataUtils.h"
21 #include "src/core/SkConvertPixels.h"
22 #include "src/core/SkTraceEvent.h"
23 #include "src/gpu/AtlasTypes.h"
24 #include "src/gpu/DataUtils.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/CommandBuffer.h"
30 #include "src/gpu/graphite/ContextPriv.h"
31 #include "src/gpu/graphite/Device.h"
32 #include "src/gpu/graphite/GlobalCache.h"
33 #include "src/gpu/graphite/Log.h"
34 #include "src/gpu/graphite/PathAtlas.h"
35 #include "src/gpu/graphite/PipelineData.h"
36 #include "src/gpu/graphite/ProxyCache.h"
37 #include "src/gpu/graphite/RasterPathAtlas.h"
38 #include "src/gpu/graphite/RecorderPriv.h"
39 #include "src/gpu/graphite/RecordingPriv.h"
40 #include "src/gpu/graphite/ResourceProvider.h"
41 #include "src/gpu/graphite/RuntimeEffectDictionary.h"
42 #include "src/gpu/graphite/ScratchResourceManager.h"
43 #include "src/gpu/graphite/SharedContext.h"
44 #include "src/gpu/graphite/Texture.h"
45 #include "src/gpu/graphite/UploadBufferManager.h"
46 #include "src/gpu/graphite/task/CopyTask.h"
47 #include "src/gpu/graphite/task/TaskList.h"
48 #include "src/gpu/graphite/task/UploadTask.h"
49 #include "src/gpu/graphite/text/TextAtlasManager.h"
50 #include "src/image/SkImage_Base.h"
51 #include "src/text/gpu/StrikeCache.h"
52 #include "src/text/gpu/TextBlobRedrawCoordinator.h"
53 
54 namespace skgpu::graphite {
55 
56 #define ASSERT_SINGLE_OWNER SKGPU_ASSERT_SINGLE_OWNER(this->singleOwner())
57 #define ASSERT_SINGLE_OWNER_PRIV SKGPU_ASSERT_SINGLE_OWNER(fRecorder->singleOwner())
58 
59 /*
60  * The default image provider doesn't perform any conversion so, by default, Graphite won't
61  * draw any non-Graphite-backed images.
62  */
63 class DefaultImageProvider final : public ImageProvider {
64 public:
Make()65     static sk_sp<DefaultImageProvider> Make() { return sk_sp(new DefaultImageProvider); }
66 
findOrCreate(Recorder * recorder,const SkImage * image,SkImage::RequiredProperties)67     sk_sp<SkImage> findOrCreate(Recorder* recorder,
68                                 const SkImage* image,
69                                 SkImage::RequiredProperties) override {
70         SkASSERT(!as_IB(image)->isGraphiteBacked());
71 
72         return nullptr;
73     }
74 
75 private:
DefaultImageProvider()76     DefaultImageProvider() {}
77 };
78 
79 /**************************************************************************************************/
80 RecorderOptions::RecorderOptions() = default;
81 RecorderOptions::RecorderOptions(const RecorderOptions&) = default;
82 RecorderOptions::~RecorderOptions() = default;
83 
84 /**************************************************************************************************/
next_id()85 static uint32_t next_id() {
86     static std::atomic<uint32_t> nextID{1};
87     uint32_t id;
88     do {
89         id = nextID.fetch_add(1, std::memory_order_relaxed);
90     } while (id == SK_InvalidGenID);
91     return id;
92 }
93 
Recorder(sk_sp<SharedContext> sharedContext,const RecorderOptions & options,const Context * context)94 Recorder::Recorder(sk_sp<SharedContext> sharedContext,
95                    const RecorderOptions& options,
96                    const Context* context)
97         : fSharedContext(std::move(sharedContext))
98         , fRuntimeEffectDict(std::make_unique<RuntimeEffectDictionary>())
99         , fRootTaskList(new TaskList)
100         , fRootUploads(new UploadList)
101         , fTextureDataCache(new TextureDataCache)
102         , fProxyReadCounts(new ProxyReadCountMap)
103         , fUniqueID(next_id())
104         , fAtlasProvider(std::make_unique<AtlasProvider>(this))
105         , fTokenTracker(std::make_unique<TokenTracker>())
106         , fStrikeCache(std::make_unique<sktext::gpu::StrikeCache>())
107         , fTextBlobCache(std::make_unique<sktext::gpu::TextBlobRedrawCoordinator>(fUniqueID)) {
108     fClientImageProvider = options.fImageProvider;
109     if (!fClientImageProvider) {
110         fClientImageProvider = DefaultImageProvider::Make();
111     }
112 
113     if (context) {
114         fOwnedResourceProvider = nullptr;
115         fResourceProvider = context->priv().resourceProvider();
116     } else {
117         fOwnedResourceProvider = fSharedContext->makeResourceProvider(
118                 this->singleOwner(),
119                 fUniqueID,
120                 options.fGpuBudgetInBytes);
121         fResourceProvider = fOwnedResourceProvider.get();
122     }
123     fUploadBufferManager = std::make_unique<UploadBufferManager>(fResourceProvider,
124                                                                  fSharedContext->caps());
125     fDrawBufferManager = std::make_unique<DrawBufferManager>(fResourceProvider,
126                                                              fSharedContext->caps(),
127                                                              fUploadBufferManager.get());
128 
129     SkASSERT(fResourceProvider);
130 }
131 
~Recorder()132 Recorder::~Recorder() {
133     ASSERT_SINGLE_OWNER
134     // Any finished procs that haven't been passed to a Recording fail
135     for (int i = 0; i < fFinishedProcs.size(); ++i) {
136         fFinishedProcs[i]->setFailureResult();
137     }
138 
139     for (auto& device : fTrackedDevices) {
140         // deregisterDevice() may have left an entry as null previously.
141         if (device) {
142             device->abandonRecorder();
143         }
144     }
145 #if defined(GPU_TEST_UTILS)
146     if (fContext) {
147         fContext->priv().deregisterRecorder(this);
148     }
149 #endif
150 }
151 
backend() const152 BackendApi Recorder::backend() const { return fSharedContext->backend(); }
153 
snap()154 std::unique_ptr<Recording> Recorder::snap() {
155     TRACE_EVENT0("skia.gpu", TRACE_FUNC);
156     ASSERT_SINGLE_OWNER
157 
158     if (fTargetProxyData) {
159         // Normally devices are marked immutable when their owning Surface goes away, but the
160         // deferred canvas+device do not have a surface so mimic that operation. Do this before
161         // flushing all other tracked devices to avoid a redundant flush.
162         fTargetProxyDevice->setImmutable();
163         fTargetProxyDevice.reset();
164         fTargetProxyCanvas.reset();
165     }
166     // Collect all pending tasks on the deferred recording canvas and any other tracked device.
167     this->priv().flushTrackedDevices();
168 
169     // Now that all devices have been flushed, extract all lazy proxies from the texture
170     // data cache so that they can be instantiated easily when the Recording is inserted.
171     std::unordered_set<sk_sp<TextureProxy>, Recording::ProxyHash> nonVolatileLazyProxies;
172     std::unordered_set<sk_sp<TextureProxy>, Recording::ProxyHash> volatileLazyProxies;
173     fTextureDataCache->foreach([&](TextureDataBlock block) {
174         for (int j = 0; j < block.numTextures(); ++j) {
175             const TextureDataBlock::SampledTexture& tex = block.texture(j);
176 
177             if (tex.first->isLazy()) {
178                 if (tex.first->isVolatile()) {
179                     volatileLazyProxies.insert(tex.first);
180                 } else {
181                     nonVolatileLazyProxies.insert(tex.first);
182                 }
183             }
184         }
185     });
186 
187     // The scratch resources only need to be tracked until prepareResources() is finished, so
188     // Recorder doesn't hold a persistent manager and it can be deleted when snap() returns.
189     ScratchResourceManager scratchManager{fResourceProvider, std::move(fProxyReadCounts)};
190     std::unique_ptr<Recording> recording(new Recording(fNextRecordingID++,
191                                                        fUniqueID,
192                                                        std::move(nonVolatileLazyProxies),
193                                                        std::move(volatileLazyProxies),
194                                                        std::move(fTargetProxyData),
195                                                        std::move(fFinishedProcs)));
196     // Allow the buffer managers to add any collected tasks for data transfer or initialization
197     // before moving the root task list to the Recording.
198     bool valid = fDrawBufferManager->transferToRecording(recording.get());
199 
200     // We create the Recording's full task list even if the DrawBufferManager failed because it is
201     // a convenient way to ensure everything else is unmapped and reset for the next Recording.
202     fUploadBufferManager->transferToRecording(recording.get());
203     // Add one task for all root uploads before the rest of the rendering tasks might depend on them
204     if (fRootUploads->size() > 0) {
205         recording->priv().taskList()->add(UploadTask::Make(fRootUploads.get()));
206         SkASSERT(fRootUploads->size() == 0); // Drained by the newly added task
207     }
208     recording->priv().taskList()->add(std::move(*fRootTaskList));
209     SkASSERT(!fRootTaskList->hasTasks());
210 
211     // In both the "task failed" case and the "everything is discarded" case, there's no work that
212     // needs to be done in insertRecording(). However, we use nullptr as a failure signal, so
213     // kDiscard will return a non-null Recording that has no tasks in it.
214     valid &= recording->priv().taskList()->prepareResources(
215             fResourceProvider, &scratchManager, fRuntimeEffectDict.get()) != Task::Status::kFail;
216     if (!valid) {
217         recording = nullptr;
218         fAtlasProvider->invalidateAtlases();
219     }
220 
221     // Remaining cleanup that must always happen regardless of success or failure
222     fRuntimeEffectDict->reset();
223     fProxyReadCounts = std::make_unique<ProxyReadCountMap>();
224     fTextureDataCache = std::make_unique<TextureDataCache>();
225     if (!this->priv().caps()->requireOrderedRecordings()) {
226         fAtlasProvider->invalidateAtlases();
227     }
228 
229     return recording;
230 }
231 
makeDeferredCanvas(const SkImageInfo & imageInfo,const TextureInfo & textureInfo)232 SkCanvas* Recorder::makeDeferredCanvas(const SkImageInfo& imageInfo,
233                                        const TextureInfo& textureInfo) {
234     if (fTargetProxyCanvas) {
235         // Require snapping before requesting another canvas.
236         SKGPU_LOG_W("Requested a new deferred canvas before snapping the previous one");
237         return nullptr;
238     }
239 
240     fTargetProxyData = std::make_unique<Recording::LazyProxyData>(
241             this->priv().caps(), imageInfo.dimensions(), textureInfo);
242     // Use kLoad for the initial load op since the purpose of a deferred canvas is to draw on top
243     // of an existing, late-bound texture.
244     fTargetProxyDevice = Device::Make(this,
245                                       fTargetProxyData->refLazyProxy(),
246                                       imageInfo.dimensions(),
247                                       imageInfo.colorInfo(),
248                                       {},
249                                       LoadOp::kLoad);
250     fTargetProxyCanvas = std::make_unique<SkCanvas>(fTargetProxyDevice);
251     return fTargetProxyCanvas.get();
252 }
253 
registerDevice(sk_sp<Device> device)254 void Recorder::registerDevice(sk_sp<Device> device) {
255     ASSERT_SINGLE_OWNER
256 
257     SkASSERT(device);
258 
259     // By taking a ref on tracked devices, the Recorder prevents the Device from being deleted on
260     // another thread unless the Recorder has been destroyed or the device has abandoned its
261     // recorder (e.g. was marked immutable).
262     fTrackedDevices.emplace_back(std::move(device));
263 }
264 
deregisterDevice(const Device * device)265 void Recorder::deregisterDevice(const Device* device) {
266     ASSERT_SINGLE_OWNER
267     for (int i = 0; i < fTrackedDevices.size(); ++i) {
268         if (fTrackedDevices[i].get() == device) {
269             // Don't modify the list structure of fTrackedDevices within this loop
270             fTrackedDevices[i] = nullptr;
271             break;
272         }
273     }
274 }
275 
maxTextureSize() const276 int Recorder::maxTextureSize() const {
277     return this->priv().caps()->maxTextureSize();
278 }
279 
createBackendTexture(SkISize dimensions,const TextureInfo & info)280 BackendTexture Recorder::createBackendTexture(SkISize dimensions, const TextureInfo& info) {
281     ASSERT_SINGLE_OWNER
282 
283     if (!info.isValid() || info.backend() != this->backend()) {
284         return {};
285     }
286     return fResourceProvider->createBackendTexture(dimensions, info);
287 }
288 
289 #ifdef SK_BUILD_FOR_ANDROID
290 
createBackendTexture(AHardwareBuffer * hardwareBuffer,bool isRenderable,bool isProtectedContent,SkISize dimensions,bool fromAndroidWindow) const291 BackendTexture Recorder::createBackendTexture(AHardwareBuffer* hardwareBuffer,
292                                               bool isRenderable,
293                                               bool isProtectedContent,
294                                               SkISize dimensions,
295                                               bool fromAndroidWindow) const {
296     if (fSharedContext->backend() != BackendApi::kVulkan) {
297         SKGPU_LOG_W("Creating an AHardwareBuffer-backed BackendTexture is only supported with the"
298                     "Vulkan backend.");
299         return {};
300     }
301     return fResourceProvider->createBackendTexture(hardwareBuffer,
302                                                    isRenderable,
303                                                    isProtectedContent,
304                                                    dimensions,
305                                                    fromAndroidWindow);
306 }
307 
308 #endif // SK_BUILD_FOR_ANDROID
309 
updateBackendTexture(const BackendTexture & backendTex,const SkPixmap srcData[],int numLevels,GpuFinishedProc finishedProc,GpuFinishedContext finishedContext)310 bool Recorder::updateBackendTexture(const BackendTexture& backendTex,
311                                     const SkPixmap srcData[],
312                                     int numLevels,
313                                     GpuFinishedProc finishedProc,
314                                     GpuFinishedContext finishedContext) {
315     ASSERT_SINGLE_OWNER
316 
317     auto releaseHelper = skgpu::RefCntedCallback::Make(finishedProc, finishedContext);
318 
319     if (!backendTex.isValid() || backendTex.backend() != this->backend()) {
320         return false;
321     }
322 
323     if (!srcData || numLevels <= 0) {
324         return false;
325     }
326 
327     // If the texture has MIP levels then we require that the full set is overwritten.
328     int numExpectedLevels = 1;
329     if (backendTex.info().mipmapped() == Mipmapped::kYes) {
330         numExpectedLevels = SkMipmap::ComputeLevelCount(backendTex.dimensions().width(),
331                                                         backendTex.dimensions().height()) + 1;
332     }
333     if (numLevels != numExpectedLevels) {
334         return false;
335     }
336 
337     SkColorType ct = srcData[0].colorType();
338 
339     if (!this->priv().caps()->areColorTypeAndTextureInfoCompatible(ct, backendTex.info())) {
340         return false;
341     }
342 
343     sk_sp<Texture> texture = this->priv().resourceProvider()->createWrappedTexture(backendTex, "");
344     if (!texture) {
345         return false;
346     }
347     texture->setReleaseCallback(std::move(releaseHelper));
348 
349     sk_sp<TextureProxy> proxy = TextureProxy::Wrap(std::move(texture));
350 
351     std::vector<MipLevel> mipLevels;
352     mipLevels.resize(numLevels);
353 
354     for (int i = 0; i < numLevels; ++i) {
355         SkASSERT(srcData[i].addr());
356         SkASSERT(srcData[i].info().colorInfo() == srcData[0].info().colorInfo());
357 
358         mipLevels[i].fPixels = srcData[i].addr();
359         mipLevels[i].fRowBytes = srcData[i].rowBytes();
360     }
361 
362     // Src and dst colorInfo are the same
363     const SkColorInfo& colorInfo = srcData[0].info().colorInfo();
364     // Add UploadTask to Recorder
365     UploadInstance upload = UploadInstance::Make(this,
366                                                  std::move(proxy),
367                                                  colorInfo, colorInfo,
368                                                  mipLevels,
369                                                  SkIRect::MakeSize(backendTex.dimensions()),
370                                                  std::make_unique<ImageUploadContext>());
371     if (!upload.isValid()) {
372         SKGPU_LOG_E("Recorder::updateBackendTexture: Could not create UploadInstance");
373         return false;
374     }
375     sk_sp<Task> uploadTask = UploadTask::Make(std::move(upload));
376 
377     // Need to flush any pending work in case it depends on this texture
378     this->priv().flushTrackedDevices();
379 
380     this->priv().add(std::move(uploadTask));
381 
382     return true;
383 }
384 
updateCompressedBackendTexture(const BackendTexture & backendTex,const void * data,size_t dataSize,GpuFinishedProc finishedProc,GpuFinishedContext finishedContext)385 bool Recorder::updateCompressedBackendTexture(const BackendTexture& backendTex,
386                                               const void* data,
387                                               size_t dataSize,
388                                               GpuFinishedProc finishedProc,
389                                               GpuFinishedContext finishedContext) {
390     ASSERT_SINGLE_OWNER
391 
392     auto releaseHelper = skgpu::RefCntedCallback::Make(finishedProc, finishedContext);
393 
394     if (!backendTex.isValid() || backendTex.backend() != this->backend()) {
395         return false;
396     }
397 
398     if (!data) {
399         return false;
400     }
401 
402     sk_sp<Texture> texture = this->priv().resourceProvider()->createWrappedTexture(backendTex, "");
403     if (!texture) {
404         return false;
405     }
406     texture->setReleaseCallback(std::move(releaseHelper));
407 
408     sk_sp<TextureProxy> proxy = TextureProxy::Wrap(std::move(texture));
409 
410     // Add UploadTask to Recorder
411     UploadInstance upload = UploadInstance::MakeCompressed(this,
412                                                            std::move(proxy),
413                                                            data,
414                                                            dataSize);
415     if (!upload.isValid()) {
416         SKGPU_LOG_E("Recorder::updateBackendTexture: Could not create UploadInstance");
417         return false;
418     }
419     sk_sp<Task> uploadTask = UploadTask::Make(std::move(upload));
420 
421     // Need to flush any pending work in case it depends on this texture
422     this->priv().flushTrackedDevices();
423 
424     this->priv().add(std::move(uploadTask));
425 
426     return true;
427 }
428 
deleteBackendTexture(const BackendTexture & texture)429 void Recorder::deleteBackendTexture(const BackendTexture& texture) {
430     ASSERT_SINGLE_OWNER
431 
432     if (!texture.isValid() || texture.backend() != this->backend()) {
433         return;
434     }
435     fResourceProvider->deleteBackendTexture(texture);
436 }
437 
addFinishInfo(const InsertFinishInfo & info)438 void Recorder::addFinishInfo(const InsertFinishInfo& info) {
439     if (info.fFinishedProc) {
440         sk_sp<RefCntedCallback> callback =
441                 RefCntedCallback::Make(info.fFinishedProc, info.fFinishedContext);
442         fFinishedProcs.push_back(std::move(callback));
443     }
444 }
445 
freeGpuResources()446 void Recorder::freeGpuResources() {
447     ASSERT_SINGLE_OWNER
448 
449     // We don't want to free the Uniform/TextureDataCaches or the Draw/UploadBufferManagers since
450     // all their resources need to be held on to until a Recording is snapped. And once snapped, all
451     // their held resources are released. The StrikeCache and TextBlobCache don't hold onto any Gpu
452     // resources.
453 
454     // Notify the atlas and resource provider to free any resources it can (does not include
455     // resources that are locked due to pending work).
456     fAtlasProvider->freeGpuResources();
457 
458     fResourceProvider->freeGpuResources();
459 
460     // This is technically not GPU memory, but there's no other place for the client to tell us to
461     // clean this up, and without any cleanup it can grow unbounded.
462     fStrikeCache->freeAll();
463 }
464 
performDeferredCleanup(std::chrono::milliseconds msNotUsed)465 void Recorder::performDeferredCleanup(std::chrono::milliseconds msNotUsed) {
466     ASSERT_SINGLE_OWNER
467 
468     auto purgeTime = skgpu::StdSteadyClock::now() - msNotUsed;
469     fResourceProvider->purgeResourcesNotUsedSince(purgeTime);
470 }
471 
currentBudgetedBytes() const472 size_t Recorder::currentBudgetedBytes() const {
473     ASSERT_SINGLE_OWNER
474     return fResourceProvider->getResourceCacheCurrentBudgetedBytes();
475 }
476 
currentPurgeableBytes() const477 size_t Recorder::currentPurgeableBytes() const {
478     ASSERT_SINGLE_OWNER
479     return fResourceProvider->getResourceCacheCurrentPurgeableBytes();
480 }
481 
maxBudgetedBytes() const482 size_t Recorder::maxBudgetedBytes() const {
483     ASSERT_SINGLE_OWNER
484     return fResourceProvider->getResourceCacheLimit();
485 }
486 
setMaxBudgetedBytes(size_t bytes)487 void Recorder::setMaxBudgetedBytes(size_t bytes) {
488     ASSERT_SINGLE_OWNER
489     return fResourceProvider->setResourceCacheLimit(bytes);
490 }
491 
dumpMemoryStatistics(SkTraceMemoryDump * traceMemoryDump) const492 void Recorder::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const {
493     ASSERT_SINGLE_OWNER
494     fResourceProvider->dumpMemoryStatistics(traceMemoryDump);
495     // TODO: What is the graphite equivalent for the text blob cache and how do we print out its
496     // used bytes here (see Ganesh implementation).
497 }
498 
addPendingRead(const TextureProxy * proxy)499 void RecorderPriv::addPendingRead(const TextureProxy* proxy) {
500     ASSERT_SINGLE_OWNER_PRIV
501     fRecorder->fProxyReadCounts->increment(proxy);
502 }
503 
add(sk_sp<Task> task)504 void RecorderPriv::add(sk_sp<Task> task) {
505     ASSERT_SINGLE_OWNER_PRIV
506     fRecorder->fRootTaskList->add(std::move(task));
507 }
508 
flushTrackedDevices()509 void RecorderPriv::flushTrackedDevices() {
510     ASSERT_SINGLE_OWNER_PRIV
511 
512     // If this is the initial flushTrackedDevices() call, fFlushingTrackedDevicesIndex will be -1
513     // so we start iterating at 0. We remember the starting device index to perform clean up only
514     // when it was 0 to prevent modifying the underlying data structure while iterating over it.
515     // However, when flushing one device it may register new devices as well as recursively call
516     // flushTrackedDevices(). In that case, it picks up the next device after the current one that
517     // triggered the recursive flush since all prior devices have been flushed are in progress
518     // (and they should not be flushed while in an unfinished flush). When the control flow returns
519     // to the outer flushTrackedDevices(), it will pick up with wherever the inner flush had ended.
520     // TODO(b/330864257): Once paint data is extracted at draw time (so picture shaders are rendered
521     // to images before a flush instead of inside a flush), we can simplify this and assert that
522     // flushTrackedDevices() is not recursively called and that devices are not added or removed
523     // while flushing.
524     const int startingIndex = fRecorder->fFlushingDevicesIndex;
525     while (fRecorder->fFlushingDevicesIndex < fRecorder->fTrackedDevices.size() - 1) {
526         // Advance before calling flushPendingWorkToRecorder() so that any re-entrant clal to
527         // flushTrackedDevices() will skip the current device.
528         fRecorder->fFlushingDevicesIndex++;
529         // Entries may be set to null from a call to deregisterDevice(), which will be cleaned up
530         // along with any immutable or uniquely held Devices once everything is flushed.
531         Device* device = fRecorder->fTrackedDevices[fRecorder->fFlushingDevicesIndex].get();
532         if (device) {
533             device->flushPendingWorkToRecorder();
534         }
535     }
536 
537     // Issue next upload flush token. This is only used by the atlasing code which
538     // always uses this method. Calling in Device::flushPendingWorkToRecorder may
539     // miss parent device flushes, increment too often, and lead to atlas corruption.
540     this->tokenTracker()->issueFlushToken();
541 
542     if (startingIndex < 0) {
543         // Initial call to flushTrackedDevices() so cleanup null/immutable devices and reset the
544         // loop index.
545         int i = 0;
546         while (i < fRecorder->fTrackedDevices.size()) {
547             Device* device = fRecorder->fTrackedDevices[i].get();
548             if (!device || !device->recorder() || device->unique()) {
549                 if (device) {
550                     device->abandonRecorder(); // Keep ~Device() happy
551                 }
552                 fRecorder->fTrackedDevices.removeShuffle(i);
553                 // Keep i as-is to process what was just shuffled to the ith index.
554             } else {
555                 i++;
556             }
557         }
558 
559         fRecorder->fFlushingDevicesIndex = -1;
560     }
561 }
562 
CreateCachedProxy(Recorder * recorder,const SkBitmap & bitmap,std::string_view label)563 sk_sp<TextureProxy> RecorderPriv::CreateCachedProxy(Recorder* recorder,
564                                                     const SkBitmap& bitmap,
565                                                     std::string_view label) {
566     SkASSERT(!bitmap.isNull());
567 
568     if (!recorder) {
569         return nullptr;
570     }
571     return recorder->priv().proxyCache()->findOrCreateCachedProxy(recorder,
572                                                                   bitmap,
573                                                                   std::move(label));
574 }
575 
getResourceCacheLimit() const576 size_t RecorderPriv::getResourceCacheLimit() const {
577     return fRecorder->fResourceProvider->getResourceCacheLimit();
578 }
579 
580 #if defined(GPU_TEST_UTILS)
deviceIsRegistered(Device * device) const581 bool RecorderPriv::deviceIsRegistered(Device* device) const {
582     ASSERT_SINGLE_OWNER_PRIV
583     for (const sk_sp<Device>& currentDevice : fRecorder->fTrackedDevices) {
584         if (device == currentDevice.get()) {
585             return true;
586         }
587     }
588     return false;
589 }
590 
591 // used by the Context that created this Recorder to set a back pointer
setContext(Context * context)592 void RecorderPriv::setContext(Context* context) {
593     fRecorder->fContext = context;
594 }
595 
issueFlushToken()596 void RecorderPriv::issueFlushToken() {
597     fRecorder->fTokenTracker->issueFlushToken();
598 }
599 
600 #endif
601 
602 
603 } // namespace skgpu::graphite
604