| /* | |
| * 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 "include/private/SkAttributes.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/PipelineCreationTask.h" | |
| #include "src/gpu/graphite/RendererProvider.h" | |
| #include "src/gpu/graphite/ResourceCache.h" | |
| #include "src/gpu/graphite/ResourceProvider.h" | |
| #include "src/gpu/graphite/RuntimeEffectDictionary.h" | |
| #include "src/gpu/graphite/SerializationUtils.h" | |
| #include "src/gpu/graphite/ThreadSafeResourceProvider.h" | |
| namespace skgpu::graphite { | |
| static Layout get_binding_layout(const Caps* caps) { | |
| ResourceBindingRequirements reqs = caps->resourceBindingRequirements(); | |
| return caps->storageBufferSupport() ? reqs.fStorageBufferLayout : reqs.fUniformBufferLayout; | |
| } | |
| SharedContext::SharedContext(std::unique_ptr<const Caps> caps, | |
| BackendApi backend, | |
| ExecutorType* executor, | |
| SkSpan<sk_sp<SkRuntimeEffect>> userDefinedKnownRuntimeEffects) | |
| : fCaps(std::move(caps)) | |
| , fBackend(backend) | |
| , fGlobalCache() | |
| , fShaderDictionary(get_binding_layout(fCaps.get()), userDefinedKnownRuntimeEffects) | |
| , fPipelineManager(executor) {} | |
| 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(ctx->caps(), dict, rpDesc, step, gpDesc.paintParamsID()); | |
| } | |
| sk_sp<GraphicsPipeline> SharedContext::findOrCreateGraphicsPipeline( | |
| 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(SK_UNSAFE_TODO(kNames[forPrecompile])), | |
| TRACE_EVENT_SCOPE_THREAD, | |
| "key", pipelineKey.hash(), | |
| "compilationID", compilationID); | |
| #endif | |
| pipeline = this->createGraphicsPipeline(runtimeDict, pipelineKey, | |
| pipelineDesc, renderPassDesc, | |
| pipelineCreationFlags, | |
| compilationID); | |
| if (pipeline) { | |
| // 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? | |
| bool addedToCache; | |
| std::tie(pipeline, addedToCache) = globalCache->addGraphicsPipeline(pipelineKey, | |
| pipeline); | |
| if (addedToCache && globalCache->hasPipelineCallback()) { | |
| sk_sp<SkData> data = PipelineDescToData(this->caps(), | |
| this->shaderCodeDictionary(), | |
| pipelineDesc, | |
| renderPassDesc); | |
| globalCache->invokePipelineCallback( | |
| ContextOptions::PipelineCacheOp::kAddingPipeline, | |
| pipeline.get(), | |
| std::move(data)); | |
| } | |
| } | |
| } | |
| return pipeline; | |
| } | |
| #if defined(SK_DEBUG) | |
| size_t SharedContext::getResourceCacheLimit() const { | |
| return fThreadSafeResourceProvider->getResourceCacheLimit(); | |
| } | |
| size_t SharedContext::getResourceCacheCurrentBudgetedBytes() const { | |
| return fThreadSafeResourceProvider->getResourceCacheCurrentBudgetedBytes(); | |
| } | |
| size_t SharedContext::getResourceCacheCurrentPurgeableBytes() const { | |
| return fThreadSafeResourceProvider->getResourceCacheCurrentPurgeableBytes(); | |
| } | |
| #endif | |
| void SharedContext::dumpMemoryStatistics(SkTraceMemoryDump* traceMemoryDump) const { | |
| fThreadSafeResourceProvider->dumpMemoryStatistics(traceMemoryDump); | |
| } | |
| void SharedContext::freeGpuResources() { | |
| fThreadSafeResourceProvider->freeGpuResources(); | |
| } | |
| void SharedContext::purgeResourcesNotUsedSince(StdSteadyClock::time_point purgeTime) { | |
| fThreadSafeResourceProvider->purgeResourcesNotUsedSince(purgeTime); | |
| } | |
| void SharedContext::forceProcessReturnedResources() { | |
| fThreadSafeResourceProvider->forceProcessReturnedResources(); | |
| } | |
| } // namespace skgpu::graphite |