/* * Copyright 2025 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef skgpu_graphite_PipelineManager_DEFINED #define skgpu_graphite_PipelineManager_DEFINED #include "include/core/SkRefCnt.h" #include "include/private/SkEnumBitMask.h" #include "src/core/SkSpinlock.h" #include "src/core/SkTHash.h" namespace skgpu { class UniqueKey; } class SkExecutor; namespace skgpu::graphite { class GraphicsPipeline; class GraphicsPipelineDesc; class GraphicsPipelineHandle; enum class PipelineCreationFlags : uint8_t; class PipelineCreationTask; class RuntimeEffectDictionary; class SharedContext; struct RenderPassDesc; class PipelineManager { public: PipelineManager(SkExecutor* executor); ~PipelineManager(); // If an existing Pipeline is found, it is just wrapped in a Handle and returned. // Otherwise, a compilation task is created and queued up for execution. // If no Executor is provided the compilations will occur synchronously, in-line. GraphicsPipelineHandle createHandle( SharedContext*, sk_sp runtimeDict, const GraphicsPipelineDesc&, const RenderPassDesc&, SkEnumBitMask); sk_sp resolveHandle(const GraphicsPipelineHandle&); // Wait for any in-flight tasks to complete. Additionally, disable the addition of any // more threaded tasks. void shutDown() SK_EXCLUDES(fSpinLock); #if defined(GPU_TEST_UTILS) void wait_TestOnly() SK_EXCLUDES(fSpinLock); struct Stats { // The number of times we find a pre-existing task for a Pipeline int fNumPreemptivelyFoundTasks = 0; int fNumTasksCreated = 0; }; Stats getStats() const SK_EXCLUDES(fSpinLock); #endif private: enum class Priority { kHigh = 0, kLow = 1 }; sk_sp findOrCreateTask( SharedContext*, sk_sp, const UniqueKey& pipelineKey, const GraphicsPipelineDesc&, const RenderPassDesc&, Priority) SK_EXCLUDES(fSpinLock); void addTaskToWorkList(SharedContext*, sk_sp, Priority) SK_EXCLUDES(fSpinLock); void removeTask(const sk_sp&) SK_EXCLUDES(fSpinLock); void potentiallyWaitOn(const sk_sp&) SK_EXCLUDES(fSpinLock); sk_sp getWork(bool inclInProgress) SK_EXCLUDES(fSpinLock); void wait() SK_EXCLUDES(fSpinLock); // Returns true if compilation occurred; false otherwise. // All callers must hold a ref on the PipelineCreationTask. static bool InlineCompile(const sk_sp&) SK_EXCLUDES(fSpinLock); struct Traits { static const UniqueKey& GetKey(const sk_sp&); static uint32_t Hash(const UniqueKey& pipelineKey); }; using TaskMap = skia_private::THashTable, UniqueKey, Traits>; mutable SkSpinlock fSpinLock; TaskMap fActiveTasks SK_GUARDED_BY(fSpinLock); #if defined(GPU_TEST_UTILS) Stats fStats SK_GUARDED_BY(fSpinLock); #endif // The executor is obtained from ContextOptions. It is up to the client to ensure it // exists past the Context's destruction. The Context does call PipelineManager::shutDown // from its destructor to end its use. SkExecutor* fExecutor SK_GUARDED_BY(fSpinLock) = nullptr; }; } // namespace skgpu::graphite #endif // skgpu_graphite_PipelineManager_DEFINED