blob: cc81f0867efb60659225f53c406309068a41934d [file]
/*
* 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<const RuntimeEffectDictionary> runtimeDict,
const GraphicsPipelineDesc&,
const RenderPassDesc&,
SkEnumBitMask<PipelineCreationFlags>);
sk_sp<GraphicsPipeline> 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<PipelineCreationTask> findOrCreateTask(
SharedContext*,
sk_sp<const RuntimeEffectDictionary>,
const UniqueKey& pipelineKey,
const GraphicsPipelineDesc&,
const RenderPassDesc&,
Priority) SK_EXCLUDES(fSpinLock);
void addTaskToWorkList(SharedContext*,
sk_sp<PipelineCreationTask>,
Priority) SK_EXCLUDES(fSpinLock);
void removeTask(const sk_sp<PipelineCreationTask>&) SK_EXCLUDES(fSpinLock);
void potentiallyWaitOn(const sk_sp<PipelineCreationTask>&) SK_EXCLUDES(fSpinLock);
sk_sp<PipelineCreationTask> 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<PipelineCreationTask>&) SK_EXCLUDES(fSpinLock);
struct Traits {
static const UniqueKey& GetKey(const sk_sp<PipelineCreationTask>&);
static uint32_t Hash(const UniqueKey& pipelineKey);
};
using TaskMap = skia_private::THashTable<sk_sp<PipelineCreationTask>, 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