• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/graphite/ResourceProvider.h"
9 
10 #include "include/core/SkSamplingOptions.h"
11 #include "include/core/SkTileMode.h"
12 #include "include/gpu/graphite/BackendTexture.h"
13 #include "src/core/SkTraceEvent.h"
14 #include "src/gpu/graphite/Buffer.h"
15 #include "src/gpu/graphite/Caps.h"
16 #include "src/gpu/graphite/CommandBuffer.h"
17 #include "src/gpu/graphite/ComputePipeline.h"
18 #include "src/gpu/graphite/ContextPriv.h"
19 #include "src/gpu/graphite/ContextUtils.h"
20 #include "src/gpu/graphite/GlobalCache.h"
21 #include "src/gpu/graphite/GraphicsPipeline.h"
22 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
23 #include "src/gpu/graphite/Log.h"
24 #include "src/gpu/graphite/RenderPassDesc.h"
25 #include "src/gpu/graphite/RendererProvider.h"
26 #include "src/gpu/graphite/ResourceCache.h"
27 #include "src/gpu/graphite/Sampler.h"
28 #include "src/gpu/graphite/SharedContext.h"
29 #include "src/gpu/graphite/Texture.h"
30 #include "src/sksl/SkSLCompiler.h"
31 
32 namespace skgpu::graphite {
33 
34 // This is only used when tracing is enabled at compile time.
to_str(const SharedContext * ctx,const GraphicsPipelineDesc & gpDesc,const RenderPassDesc & rpDesc)35 [[maybe_unused]] static std::string to_str(const SharedContext* ctx,
36                                            const GraphicsPipelineDesc& gpDesc,
37                                            const RenderPassDesc& rpDesc) {
38     const ShaderCodeDictionary* dict = ctx->shaderCodeDictionary();
39     const RenderStep* step = ctx->rendererProvider()->lookup(gpDesc.renderStepID());
40     return GetPipelineLabel(dict, rpDesc, step, gpDesc.paintParamsID());
41 }
42 
ResourceProvider(SharedContext * sharedContext,SingleOwner * singleOwner,uint32_t recorderID,size_t resourceBudget)43 ResourceProvider::ResourceProvider(SharedContext* sharedContext,\
44                                    SingleOwner* singleOwner,
45                                    uint32_t recorderID,
46                                    size_t resourceBudget)
47         : fSharedContext(sharedContext)
48         , fResourceCache(ResourceCache::Make(singleOwner, recorderID, resourceBudget)) {}
49 
~ResourceProvider()50 ResourceProvider::~ResourceProvider() {
51     fResourceCache->shutdown();
52 }
53 
findOrCreateGraphicsPipeline(const RuntimeEffectDictionary * runtimeDict,const GraphicsPipelineDesc & pipelineDesc,const RenderPassDesc & renderPassDesc,SkEnumBitMask<PipelineCreationFlags> pipelineCreationFlags)54 sk_sp<GraphicsPipeline> ResourceProvider::findOrCreateGraphicsPipeline(
55         const RuntimeEffectDictionary* runtimeDict,
56         const GraphicsPipelineDesc& pipelineDesc,
57         const RenderPassDesc& renderPassDesc,
58         SkEnumBitMask<PipelineCreationFlags> pipelineCreationFlags) {
59 
60     auto globalCache = fSharedContext->globalCache();
61     UniqueKey pipelineKey = fSharedContext->caps()->makeGraphicsPipelineKey(pipelineDesc,
62                                                                             renderPassDesc);
63 
64     uint32_t compilationID = 0;
65     sk_sp<GraphicsPipeline> pipeline = globalCache->findGraphicsPipeline(pipelineKey,
66                                                                          pipelineCreationFlags,
67                                                                          &compilationID);
68     if (!pipeline) {
69         // Haven't encountered this pipeline, so create a new one. Since pipelines are shared
70         // across Recorders, we could theoretically create equivalent pipelines on different
71         // threads. If this happens, GlobalCache returns the first-through-gate pipeline and we
72         // discard the redundant pipeline. While this is wasted effort in the rare event of a race,
73         // it allows pipeline creation to be performed without locking the global cache.
74         // NOTE: The parameters to TRACE_EVENT are only evaluated inside an if-block when the
75         // category is enabled.
76         TRACE_EVENT1_ALWAYS(
77                 "skia.shaders", "createGraphicsPipeline", "desc",
78                 TRACE_STR_COPY(to_str(fSharedContext, pipelineDesc, renderPassDesc).c_str()));
79 
80 #if defined(SK_PIPELINE_LIFETIME_LOGGING)
81         bool forPrecompile =
82                 SkToBool(pipelineCreationFlags & PipelineCreationFlags::kForPrecompilation);
83 
84         static const char* kNames[2] = { "BeginBuildN", "BeginBuildP" };
85         TRACE_EVENT_INSTANT2("skia.gpu",
86                              TRACE_STR_STATIC(kNames[forPrecompile]),
87                              TRACE_EVENT_SCOPE_THREAD,
88                              "key", pipelineKey.hash(),
89                              "compilationID", compilationID);
90 #endif
91 
92         pipeline = this->createGraphicsPipeline(runtimeDict, pipelineKey,
93                                                 pipelineDesc, renderPassDesc,
94                                                 pipelineCreationFlags,
95                                                 compilationID);
96         if (pipeline) {
97             globalCache->invokePipelineCallback(fSharedContext, pipelineDesc, renderPassDesc);
98             // TODO: Should we store a null pipeline if we failed to create one so that subsequent
99             // usage immediately sees that the pipeline cannot be created, vs. retrying every time?
100             pipeline = globalCache->addGraphicsPipeline(pipelineKey, std::move(pipeline));
101         }
102     }
103     return pipeline;
104 }
105 
findOrCreateComputePipeline(const ComputePipelineDesc & pipelineDesc)106 sk_sp<ComputePipeline> ResourceProvider::findOrCreateComputePipeline(
107         const ComputePipelineDesc& pipelineDesc) {
108     auto globalCache = fSharedContext->globalCache();
109     UniqueKey pipelineKey = fSharedContext->caps()->makeComputePipelineKey(pipelineDesc);
110     sk_sp<ComputePipeline> pipeline = globalCache->findComputePipeline(pipelineKey);
111     if (!pipeline) {
112         pipeline = this->createComputePipeline(pipelineDesc);
113         if (pipeline) {
114             pipeline = globalCache->addComputePipeline(pipelineKey, std::move(pipeline));
115         }
116     }
117     return pipeline;
118 }
119 
120 ////////////////////////////////////////////////////////////////////////////////////////////////
121 
findOrCreateNonShareableTexture(SkISize dimensions,const TextureInfo & info,std::string_view label,Budgeted budgeted)122 sk_sp<Texture> ResourceProvider::findOrCreateNonShareableTexture(SkISize dimensions,
123                                                                  const TextureInfo& info,
124                                                                  std::string_view label,
125                                                                  Budgeted budgeted) {
126     SkASSERT(info.isValid());
127     return this->findOrCreateTexture(dimensions,
128                                      info,
129                                      std::move(label),
130                                      budgeted,
131                                      Shareable::kNo);
132 }
133 
findOrCreateScratchTexture(SkISize dimensions,const TextureInfo & info,std::string_view label,const ResourceCache::ScratchResourceSet & unavailable)134 sk_sp<Texture> ResourceProvider::findOrCreateScratchTexture(
135         SkISize dimensions,
136         const TextureInfo& info,
137         std::string_view label,
138         const ResourceCache::ScratchResourceSet& unavailable) {
139     SkASSERT(info.isValid());
140     return this->findOrCreateTexture(dimensions,
141                                      info,
142                                      std::move(label),
143                                      Budgeted::kYes,
144                                      Shareable::kScratch,
145                                      &unavailable);
146 }
147 
findOrCreateDepthStencilAttachment(SkISize dimensions,const TextureInfo & info)148 sk_sp<Texture> ResourceProvider::findOrCreateDepthStencilAttachment(SkISize dimensions,
149                                                                     const TextureInfo& info) {
150     SkASSERT(info.isValid());
151     // We always make depth and stencil attachments shareable. Between any render pass the values
152     // are reset. Thus it is safe to be used by multiple different render passes without worry of
153     // stomping on each other's data.
154     return this->findOrCreateTexture(dimensions,
155                                      info,
156                                      "DepthStencilAttachment",
157                                      Budgeted::kYes,
158                                      Shareable::kYes);
159 }
160 
findOrCreateDiscardableMSAAAttachment(SkISize dimensions,const TextureInfo & info)161 sk_sp<Texture> ResourceProvider::findOrCreateDiscardableMSAAAttachment(SkISize dimensions,
162                                                                        const TextureInfo& info) {
163     SkASSERT(info.isValid());
164     // We always make discardable msaa attachments shareable. Between any render pass we discard
165     // the values of the MSAA texture. Thus it is safe to be used by multiple different render
166     // passes without worry of stomping on each other's data. It is the callings code's
167     // responsibility to populate the discardable MSAA texture with data at the start of the
168     // render pass.
169     return this->findOrCreateTexture(dimensions,
170                                      info,
171                                      "DiscardableMSAAAttachment",
172                                      Budgeted::kYes,
173                                      Shareable::kYes);
174 }
175 
findOrCreateTexture(SkISize dimensions,const TextureInfo & info,std::string_view label,Budgeted budgeted,Shareable shareable,const ResourceCache::ScratchResourceSet * unavailable)176 sk_sp<Texture> ResourceProvider::findOrCreateTexture(
177         SkISize dimensions,
178         const TextureInfo& info,
179         std::string_view label,
180         Budgeted budgeted,
181         Shareable shareable,
182         const ResourceCache::ScratchResourceSet* unavailable) {
183     // If the resource is shareable it should be budgeted since it shouldn't be backing any client
184     // owned object.
185     SkASSERT(shareable == Shareable::kNo || budgeted == Budgeted::kYes);
186     SkASSERT(shareable != Shareable::kScratch || SkToBool(unavailable));
187 
188     static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
189 
190     GraphiteResourceKey key;
191     fSharedContext->caps()->buildKeyForTexture(dimensions, info, kType, &key);
192 
193     if (Resource* resource =
194                 fResourceCache->findAndRefResource(key, budgeted, shareable, unavailable)) {
195         resource->setLabel(std::move(label));
196         return sk_sp<Texture>(static_cast<Texture*>(resource));
197     }
198 
199     auto tex = this->createTexture(dimensions, info);
200     if (!tex) {
201         return nullptr;
202     }
203 
204     tex->setLabel(std::move(label));
205     fResourceCache->insertResource(tex.get(), key, budgeted, shareable);
206 
207     return tex;
208 }
209 
createWrappedTexture(const BackendTexture & backendTexture,std::string_view label)210 sk_sp<Texture> ResourceProvider::createWrappedTexture(const BackendTexture& backendTexture,
211                                                       std::string_view label) {
212     sk_sp<Texture> texture = this->onCreateWrappedTexture(backendTexture);
213     if (texture) {
214         texture->setLabel(std::move(label));
215         SkASSERT(texture->ownership() == Ownership::kWrapped);
216     }
217     return texture;
218 }
219 
findOrCreateCompatibleSampler(const SamplerDesc & samplerDesc)220 sk_sp<Sampler> ResourceProvider::findOrCreateCompatibleSampler(const SamplerDesc& samplerDesc) {
221     static constexpr Budgeted kBudgeted = Budgeted::kYes;
222     static constexpr Shareable kShareable = Shareable::kYes;
223     static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
224 
225     GraphiteResourceKey key;
226     {
227         // The size of the returned span accurately captures the quantity of uint32s needed whether
228         // the sampler is immutable or not. Each backend will already have encoded any specific
229         // immutable sampler details into the SamplerDesc, so there is no need to delegate to Caps
230         // to create a specific key.
231         const SkSpan<const uint32_t>& samplerData = samplerDesc.asSpan();
232         GraphiteResourceKey::Builder builder(&key, kType, samplerData.size());
233 
234         for (size_t i = 0; i < samplerData.size(); i++) {
235             builder[i] = samplerData[i];
236         }
237     }
238 
239     if (Resource* resource = fResourceCache->findAndRefResource(key, kBudgeted, kShareable)) {
240         return sk_sp<Sampler>(static_cast<Sampler*>(resource));
241     }
242 
243     sk_sp<Sampler> sampler = this->createSampler(samplerDesc);
244     if (!sampler) {
245         return nullptr;
246     }
247 
248     fResourceCache->insertResource(sampler.get(), key, kBudgeted, kShareable);
249     return sampler;
250 }
251 
findOrCreateBuffer(size_t size,BufferType type,AccessPattern accessPattern,std::string_view label)252 sk_sp<Buffer> ResourceProvider::findOrCreateBuffer(size_t size,
253                                                    BufferType type,
254                                                    AccessPattern accessPattern,
255                                                    std::string_view label) {
256     static constexpr Budgeted kBudgeted = Budgeted::kYes;
257     static constexpr Shareable kShareable = Shareable::kNo;
258     static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
259 
260     GraphiteResourceKey key;
261     {
262         // For the key we need ((sizeof(size_t) + (sizeof(uint32_t) - 1)) / (sizeof(uint32_t))
263         // uint32_t's for the size and one uint32_t for the rest.
264         static_assert(sizeof(uint32_t) == 4);
265         static const int kSizeKeyNum32DataCnt = (sizeof(size_t) + 3) / 4;
266         static const int kKeyNum32DataCnt =  kSizeKeyNum32DataCnt + 1;
267 
268         SkASSERT(static_cast<uint32_t>(type) < (1u << 4));
269         SkASSERT(static_cast<uint32_t>(accessPattern) < (1u << 1));
270 
271         GraphiteResourceKey::Builder builder(&key, kType, kKeyNum32DataCnt);
272         builder[0] = (static_cast<uint32_t>(type) << 0) |
273                      (static_cast<uint32_t>(accessPattern) << 4);
274         size_t szKey = size;
275         for (int i = 0; i < kSizeKeyNum32DataCnt; ++i) {
276             builder[i + 1] = (uint32_t) szKey;
277 
278             // If size_t is 4 bytes, we cannot do a shift of 32 or else we get a warning/error that
279             // shift amount is >= width of the type.
280             if constexpr(kSizeKeyNum32DataCnt > 1) {
281                 szKey = szKey >> 32;
282             }
283         }
284     }
285 
286     if (Resource* resource = fResourceCache->findAndRefResource(key, kBudgeted, kShareable)) {
287         resource->setLabel(std::move(label));
288         return sk_sp<Buffer>(static_cast<Buffer*>(resource));
289     }
290     auto buffer = this->createBuffer(size, type, accessPattern);
291     if (!buffer) {
292         return nullptr;
293     }
294 
295     buffer->setLabel(std::move(label));
296     fResourceCache->insertResource(buffer.get(), key, kBudgeted, kShareable);
297     return buffer;
298 }
299 
300 namespace {
dimensions_are_valid(const int maxTextureSize,const SkISize & dimensions)301 bool dimensions_are_valid(const int maxTextureSize, const SkISize& dimensions) {
302     if (dimensions.isEmpty() ||
303         dimensions.width()  > maxTextureSize ||
304         dimensions.height() > maxTextureSize) {
305         SKGPU_LOG_W("Call to createBackendTexture has requested dimensions (%d, %d) larger than the"
306                     " supported gpu max texture size: %d. Or the dimensions are empty.",
307                     dimensions.fWidth, dimensions.fHeight, maxTextureSize);
308         return false;
309     }
310     return true;
311 }
312 }
313 
createBackendTexture(SkISize dimensions,const TextureInfo & info)314 BackendTexture ResourceProvider::createBackendTexture(SkISize dimensions, const TextureInfo& info) {
315     if (!dimensions_are_valid(fSharedContext->caps()->maxTextureSize(), dimensions)) {
316         return {};
317     }
318     return this->onCreateBackendTexture(dimensions, info);
319 }
320 
321 #ifdef SK_BUILD_FOR_ANDROID
createBackendTexture(AHardwareBuffer * hardwareBuffer,bool isRenderable,bool isProtectedContent,SkISize dimensions,bool fromAndroidWindow) const322 BackendTexture ResourceProvider::createBackendTexture(AHardwareBuffer* hardwareBuffer,
323                                                       bool isRenderable,
324                                                       bool isProtectedContent,
325                                                       SkISize dimensions,
326                                                       bool fromAndroidWindow) const {
327     if (!dimensions_are_valid(fSharedContext->caps()->maxTextureSize(), dimensions)) {
328         return {};
329     }
330     return this->onCreateBackendTexture(hardwareBuffer,
331                                         isRenderable,
332                                         isProtectedContent,
333                                         dimensions,
334                                         fromAndroidWindow);
335 }
336 
onCreateBackendTexture(AHardwareBuffer *,bool isRenderable,bool isProtectedContent,SkISize dimensions,bool fromAndroidWindow) const337 BackendTexture ResourceProvider::onCreateBackendTexture(AHardwareBuffer*,
338                                                         bool isRenderable,
339                                                         bool isProtectedContent,
340                                                         SkISize dimensions,
341                                                         bool fromAndroidWindow) const {
342     return {};
343 }
344 #endif
345 
deleteBackendTexture(const BackendTexture & texture)346 void ResourceProvider::deleteBackendTexture(const BackendTexture& texture) {
347     this->onDeleteBackendTexture(texture);
348 }
349 
freeGpuResources()350 void ResourceProvider::freeGpuResources() {
351     this->onFreeGpuResources();
352 
353     // TODO: Are there Resources that are ref'd by the ResourceProvider or its subclasses that need
354     // be released? If we ever find that we're holding things directly on the ResourceProviders we
355     // call down into the subclasses to allow them to release things.
356 
357     fResourceCache->purgeResources();
358 }
359 
purgeResourcesNotUsedSince(StdSteadyClock::time_point purgeTime)360 void ResourceProvider::purgeResourcesNotUsedSince(StdSteadyClock::time_point purgeTime) {
361     this->onPurgeResourcesNotUsedSince(purgeTime);
362     fResourceCache->purgeResourcesNotUsedSince(purgeTime);
363 }
364 
365 }  // namespace skgpu::graphite
366