blob: e986e2e086c2fdb298bbfa0b5a4e168e5f585264 [file] [log] [blame]
/*
* Copyright 2021 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/SharedContext.h"
#include "include/gpu/graphite/BackendTexture.h"
#include "include/gpu/graphite/TextureInfo.h"
#include "src/core/SkTraceEvent.h"
#include "src/gpu/graphite/Caps.h"
#include "src/gpu/graphite/CommandBuffer.h"
#include "src/gpu/graphite/ContextUtils.h"
#include "src/gpu/graphite/GpuWorkSubmission.h"
#include "src/gpu/graphite/GraphicsPipelineDesc.h"
#include "src/gpu/graphite/RendererProvider.h"
#include "src/gpu/graphite/ResourceProvider.h"
namespace skgpu::graphite {
static Layout get_binding_layout(const Caps* caps) {
ResourceBindingRequirements reqs = caps->resourceBindingRequirements();
return caps->storageBufferSupport() ? reqs.fStorageBufferLayout : reqs.fUniformBufferLayout;
}
// TODO (robertphillips): make use of executor here
SharedContext::SharedContext(std::unique_ptr<const Caps> caps,
BackendApi backend,
SkExecutor* executor,
SkSpan<sk_sp<SkRuntimeEffect>> userDefinedKnownRuntimeEffects)
: fCaps(std::move(caps))
, fBackend(backend)
, fGlobalCache()
, fPipelineManager() // TODO(robertphillips): pass in executor here
, fShaderDictionary(get_binding_layout(fCaps.get()), userDefinedKnownRuntimeEffects) {}
SharedContext::~SharedContext() {
// TODO: add disconnect?
// TODO: destroyResources instead?
}
Protected SharedContext::isProtected() const { return Protected(fCaps->protectedSupport()); }
void SharedContext::setRendererProvider(std::unique_ptr<RendererProvider> rendererProvider) {
// Should only be called once and be non-null
SkASSERT(rendererProvider && !fRendererProvider);
fRendererProvider = std::move(rendererProvider);
}
void SharedContext::setCaptureManager(sk_sp<SkCaptureManager> captureManager) {
// Should only be called once and be non-null
SkASSERT(captureManager && !fCaptureManager);
fCaptureManager = captureManager;
}
// This is only used when tracing is enabled at compile time.
[[maybe_unused]] static std::string to_str(const SharedContext* ctx,
const GraphicsPipelineDesc& gpDesc,
const RenderPassDesc& rpDesc) {
const ShaderCodeDictionary* dict = ctx->shaderCodeDictionary();
const RenderStep* step = ctx->rendererProvider()->lookup(gpDesc.renderStepID());
return GetPipelineLabel(dict, rpDesc, step, gpDesc.paintParamsID());
}
sk_sp<GraphicsPipeline> SharedContext::findOrCreateGraphicsPipeline(
ResourceProvider* resourceProvider,
const RuntimeEffectDictionary* runtimeDict,
const UniqueKey& pipelineKey,
const GraphicsPipelineDesc& pipelineDesc,
const RenderPassDesc& renderPassDesc,
SkEnumBitMask<PipelineCreationFlags> pipelineCreationFlags) {
auto globalCache = this->globalCache();
uint32_t compilationID = 0;
sk_sp<GraphicsPipeline> pipeline = globalCache->findGraphicsPipeline(pipelineKey,
pipelineCreationFlags,
&compilationID);
if (!pipeline) {
// Haven't encountered this pipeline, so create a new one. Since pipelines are shared
// across Recorders, we could theoretically create equivalent pipelines on different
// threads. If this happens, GlobalCache returns the first-through-gate pipeline and we
// discard the redundant pipeline. While this is wasted effort in the rare event of a race,
// it allows pipeline creation to be performed without locking the global cache.
// NOTE: The parameters to TRACE_EVENT are only evaluated inside an if-block when the
// category is enabled.
TRACE_EVENT1_ALWAYS(
"skia.shaders", "createGraphicsPipeline", "desc",
TRACE_STR_COPY(to_str(this, pipelineDesc, renderPassDesc).c_str()));
#if defined(SK_PIPELINE_LIFETIME_LOGGING)
bool forPrecompile =
SkToBool(pipelineCreationFlags & PipelineCreationFlags::kForPrecompilation);
static const char* kNames[2] = { "BeginBuildN", "BeginBuildP" };
TRACE_EVENT_INSTANT2("skia.gpu",
TRACE_STR_STATIC(kNames[forPrecompile]),
TRACE_EVENT_SCOPE_THREAD,
"key", pipelineKey.hash(),
"compilationID", compilationID);
#endif
pipeline = resourceProvider->createGraphicsPipeline(runtimeDict, pipelineKey,
pipelineDesc, renderPassDesc,
pipelineCreationFlags,
compilationID);
if (pipeline) {
globalCache->invokePipelineCallback(this, pipelineDesc, renderPassDesc);
// TODO: Should we store a null pipeline if we failed to create one so that subsequent
// usage immediately sees that the pipeline cannot be created, vs. retrying every time?
pipeline = globalCache->addGraphicsPipeline(pipelineKey, std::move(pipeline));
}
}
return pipeline;
}
} // namespace skgpu::graphite