blob: 61df2e767d3714bddc12159a4d251dffaae3a623 [file] [log] [blame]
/*
* Copyright 2025 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/graphite/PipelineManager.h"
#include "src/core/SkTaskGroup.h"
#include "src/gpu/graphite/GraphicsPipelineDesc.h"
#include "src/gpu/graphite/GraphicsPipelineHandle.h"
#include "src/gpu/graphite/Log.h"
#include "src/gpu/graphite/PipelineCreationTask.h"
#include "src/gpu/graphite/RenderPassDesc.h"
#include "src/gpu/graphite/RuntimeEffectDictionary.h"
#include "src/gpu/graphite/SharedContext.h"
namespace skgpu::graphite {
GraphicsPipelineHandle::GraphicsPipelineHandle(sk_sp<PipelineCreationTask> task)
: fTaskOrPipeline(std::move(task)) {}
GraphicsPipelineHandle::GraphicsPipelineHandle(sk_sp<GraphicsPipeline> pipeline)
: fTaskOrPipeline(std::move(pipeline)) {}
PipelineManager::PipelineManager() {}
PipelineManager::~PipelineManager() {}
const UniqueKey& PipelineManager::Traits::GetKey(const sk_sp<PipelineCreationTask>& task) {
return task->fPipelineKey;
}
uint32_t PipelineManager::Traits::Hash(const UniqueKey& pipelineKey) {
return pipelineKey.hash();
}
GraphicsPipelineHandle PipelineManager::createHandle(
SharedContext* sharedContext,
const GraphicsPipelineDesc& pipelineDesc,
const RenderPassDesc& renderPassDesc,
SkEnumBitMask<PipelineCreationFlags> pipelineCreationFlags) {
GlobalCache* globalCache = sharedContext->globalCache();
const Caps* caps = sharedContext->caps();
UniqueKey pipelineKey = caps->makeGraphicsPipelineKey(pipelineDesc, renderPassDesc);
if (sk_sp<PipelineCreationTask> task = this->findTask(pipelineKey)) {
// There is a task in progress to compile this Pipeline so it can't be ready yet (i.e.,
// it isn't in the Pipeline Cache).
return GraphicsPipelineHandle(std::move(task));
}
sk_sp<GraphicsPipeline> pipeline = globalCache->findGraphicsPipeline(
pipelineKey,
pipelineCreationFlags);
if (pipeline) {
return GraphicsPipelineHandle(std::move(pipeline));
}
sk_sp<PipelineCreationTask> task = this->findOrCreateTask(pipelineKey,
pipelineDesc,
renderPassDesc,
pipelineCreationFlags);
return GraphicsPipelineHandle(std::move(task));
}
void PipelineManager::startPipelineCreationTask(SharedContext* sharedContext,
sk_sp<const RuntimeEffectDictionary> runtimeDict,
const GraphicsPipelineHandle& handle) {
if (std::holds_alternative<sk_sp<GraphicsPipeline>>(handle.fTaskOrPipeline)) {
return;
}
sk_sp<PipelineCreationTask> task =
std::get<sk_sp<PipelineCreationTask>>(handle.fTaskOrPipeline);
sk_sp<GraphicsPipeline> pipeline = sharedContext->findOrCreateGraphicsPipeline(
runtimeDict.get(),
task->fPipelineKey,
task->fGraphicsPipelineDesc,
task->fRenderPassDesc,
task->fPipelineCreationFlags);
if (!pipeline) {
SKGPU_LOG_W("Failed to create GraphicsPipeline!");
}
if (!task->fCompleted.exchange(true)) {
task->fPipeline = pipeline;
this->removeTask(task.get());
}
}
sk_sp<GraphicsPipeline> PipelineManager::resolveHandle(const GraphicsPipelineHandle& handle) {
if (std::holds_alternative<sk_sp<GraphicsPipeline>>(handle.fTaskOrPipeline)) {
return std::get<sk_sp<GraphicsPipeline>>(handle.fTaskOrPipeline);
}
// Since 'fTaskOrPipeline' doesn't hold a pipeline the pipeline must not have existed when
// the handle was created so a compilation task must've been created to compile it
sk_sp<PipelineCreationTask> task =
std::get<sk_sp<PipelineCreationTask>>(handle.fTaskOrPipeline);
// For the non-threaded version of the PipelineManager, whenever a thread gets here it
// will already have blindly executed the task (in DrawPass::prepareResources).
SkASSERT(task->fCompleted);
return task->fPipeline;
}
#if defined(GPU_TEST_UTILS)
PipelineManager::Stats PipelineManager::getStats() const {
SkAutoSpinlock lock{fSpinLock};
return fStats;
}
#endif
sk_sp<PipelineCreationTask> PipelineManager::findTask(const UniqueKey& pipelineKey) {
SkAutoSpinlock lock{fSpinLock};
sk_sp<PipelineCreationTask> task = fActiveTasks.findOrNull(pipelineKey);
#if defined(GPU_TEST_UTILS)
if (task) {
fStats.fNumPreemptivelyFoundTasks++;
}
#endif
return task;
}
sk_sp<PipelineCreationTask> PipelineManager::findOrCreateTask(
const UniqueKey& pipelineKey,
const GraphicsPipelineDesc& pipelineDesc,
const RenderPassDesc& renderPassDesc,
SkEnumBitMask<PipelineCreationFlags> pipelineCreationFlags) {
SkAutoSpinlock lock{fSpinLock};
sk_sp<PipelineCreationTask>* task = fActiveTasks.find(pipelineKey);
if (task) {
// There is a race in createHandle from when we first check for a task; then, failing that,
// check for an existing pipeline; then, failing that, try to create a new task. Thus,
// we can sometimes find our task here.
#if defined(GPU_TEST_UTILS)
fStats.fNumTaskCreationRaces++;
#endif
return *task;
}
#if defined(GPU_TEST_UTILS)
fStats.fNumTasksCreated++;
#endif
sk_sp<PipelineCreationTask> newTask = sk_sp<PipelineCreationTask>(
new PipelineCreationTask(pipelineKey,
pipelineDesc,
renderPassDesc,
pipelineCreationFlags));
fActiveTasks.set(newTask);
return newTask;
}
void PipelineManager::removeTask(PipelineCreationTask* task) {
SkAutoSpinlock lock{fSpinLock};
// TODO(robertphillips): this guard is only necessary in the non-threaded version of
// the PipelineManager
if (fActiveTasks.findOrNull(task->fPipelineKey)) {
fActiveTasks.remove(task->fPipelineKey);
}
}
} // namespace skgpu::graphite