xref: /aosp_15_r20/external/skia/src/gpu/graphite/ResourceProvider.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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     auto globalCache = fSharedContext->globalCache();
60     UniqueKey pipelineKey = fSharedContext->caps()->makeGraphicsPipelineKey(pipelineDesc,
61                                                                             renderPassDesc);
62     sk_sp<GraphicsPipeline> pipeline = globalCache->findGraphicsPipeline(pipelineKey);
63     if (!pipeline) {
64         // Haven't encountered this pipeline, so create a new one. Since pipelines are shared
65         // across Recorders, we could theoretically create equivalent pipelines on different
66         // threads. If this happens, GlobalCache returns the first-through-gate pipeline and we
67         // discard the redundant pipeline. While this is wasted effort in the rare event of a race,
68         // it allows pipeline creation to be performed without locking the global cache.
69         // NOTE: The parameters to TRACE_EVENT are only evaluated inside an if-block when the
70         // category is enabled.
71         TRACE_EVENT1_ALWAYS(
72                 "skia.shaders", "createGraphicsPipeline", "desc",
73                 TRACE_STR_COPY(to_str(fSharedContext, pipelineDesc, renderPassDesc).c_str()));
74         pipeline = this->createGraphicsPipeline(runtimeDict, pipelineDesc, renderPassDesc,
75                                                 pipelineCreationFlags);
76         if (pipeline) {
77             // TODO: Should we store a null pipeline if we failed to create one so that subsequent
78             // usage immediately sees that the pipeline cannot be created, vs. retrying every time?
79             pipeline = globalCache->addGraphicsPipeline(pipelineKey, std::move(pipeline));
80         }
81     }
82     return pipeline;
83 }
84 
findOrCreateComputePipeline(const ComputePipelineDesc & pipelineDesc)85 sk_sp<ComputePipeline> ResourceProvider::findOrCreateComputePipeline(
86         const ComputePipelineDesc& pipelineDesc) {
87     auto globalCache = fSharedContext->globalCache();
88     UniqueKey pipelineKey = fSharedContext->caps()->makeComputePipelineKey(pipelineDesc);
89     sk_sp<ComputePipeline> pipeline = globalCache->findComputePipeline(pipelineKey);
90     if (!pipeline) {
91         pipeline = this->createComputePipeline(pipelineDesc);
92         if (pipeline) {
93             pipeline = globalCache->addComputePipeline(pipelineKey, std::move(pipeline));
94         }
95     }
96     return pipeline;
97 }
98 
99 ////////////////////////////////////////////////////////////////////////////////////////////////
100 
findOrCreateScratchTexture(SkISize dimensions,const TextureInfo & info,std::string_view label,skgpu::Budgeted budgeted)101 sk_sp<Texture> ResourceProvider::findOrCreateScratchTexture(SkISize dimensions,
102                                                             const TextureInfo& info,
103                                                             std::string_view label,
104                                                             skgpu::Budgeted budgeted) {
105     SkASSERT(info.isValid());
106 
107     static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
108 
109     GraphiteResourceKey key;
110     // Scratch textures are not shareable
111     fSharedContext->caps()->buildKeyForTexture(dimensions, info, kType, Shareable::kNo, &key);
112 
113     return this->findOrCreateTextureWithKey(dimensions,
114                                             info,
115                                             key,
116                                             std::move(label),
117                                             budgeted);
118 }
119 
findOrCreateDepthStencilAttachment(SkISize dimensions,const TextureInfo & info)120 sk_sp<Texture> ResourceProvider::findOrCreateDepthStencilAttachment(SkISize dimensions,
121                                                                     const TextureInfo& info) {
122     SkASSERT(info.isValid());
123 
124     static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
125 
126     GraphiteResourceKey key;
127     // We always make depth and stencil attachments shareable. Between any render pass the values
128     // are reset. Thus it is safe to be used by multiple different render passes without worry of
129     // stomping on each other's data.
130     fSharedContext->caps()->buildKeyForTexture(dimensions, info, kType, Shareable::kYes, &key);
131 
132     return this->findOrCreateTextureWithKey(dimensions,
133                                             info,
134                                             key,
135                                             "DepthStencilAttachment",
136                                             skgpu::Budgeted::kYes);
137 }
138 
findOrCreateDiscardableMSAAAttachment(SkISize dimensions,const TextureInfo & info)139 sk_sp<Texture> ResourceProvider::findOrCreateDiscardableMSAAAttachment(SkISize dimensions,
140                                                                        const TextureInfo& info) {
141     SkASSERT(info.isValid());
142 
143     static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
144 
145     GraphiteResourceKey key;
146     // We always make discardable msaa attachments shareable. Between any render pass we discard
147     // the values of the MSAA texture. Thus it is safe to be used by multiple different render
148     // passes without worry of stomping on each other's data. It is the callings code's
149     // responsibility to populate the discardable MSAA texture with data at the start of the
150     // render pass.
151     fSharedContext->caps()->buildKeyForTexture(dimensions, info, kType, Shareable::kYes, &key);
152 
153     return this->findOrCreateTextureWithKey(dimensions,
154                                             info,
155                                             key,
156                                             "DiscardableMSAAAttachment",
157                                             skgpu::Budgeted::kYes);
158 }
159 
findOrCreateTextureWithKey(SkISize dimensions,const TextureInfo & info,const GraphiteResourceKey & key,std::string_view label,skgpu::Budgeted budgeted)160 sk_sp<Texture> ResourceProvider::findOrCreateTextureWithKey(SkISize dimensions,
161                                                             const TextureInfo& info,
162                                                             const GraphiteResourceKey& key,
163                                                             std::string_view label,
164                                                             skgpu::Budgeted budgeted) {
165     // If the resource is shareable it should be budgeted since it shouldn't be backing any client
166     // owned object.
167     SkASSERT(key.shareable() == Shareable::kNo || budgeted == skgpu::Budgeted::kYes);
168 
169     if (Resource* resource = fResourceCache->findAndRefResource(key, budgeted)) {
170         resource->setLabel(std::move(label));
171         return sk_sp<Texture>(static_cast<Texture*>(resource));
172     }
173 
174     auto tex = this->createTexture(dimensions, info, budgeted);
175     if (!tex) {
176         return nullptr;
177     }
178 
179     tex->setKey(key);
180     tex->setLabel(std::move(label));
181     fResourceCache->insertResource(tex.get());
182 
183     return tex;
184 }
185 
createWrappedTexture(const BackendTexture & backendTexture,std::string_view label)186 sk_sp<Texture> ResourceProvider::createWrappedTexture(const BackendTexture& backendTexture,
187                                                       std::string_view label) {
188     sk_sp<Texture> texture = this->onCreateWrappedTexture(backendTexture);
189     if (texture) {
190         texture->setLabel(std::move(label));
191     }
192     return texture;
193 }
194 
findOrCreateCompatibleSampler(const SamplerDesc & samplerDesc)195 sk_sp<Sampler> ResourceProvider::findOrCreateCompatibleSampler(const SamplerDesc& samplerDesc) {
196     GraphiteResourceKey key = fSharedContext->caps()->makeSamplerKey(samplerDesc);
197 
198     if (Resource* resource = fResourceCache->findAndRefResource(key, skgpu::Budgeted::kYes)) {
199         return sk_sp<Sampler>(static_cast<Sampler*>(resource));
200     }
201 
202     sk_sp<Sampler> sampler = this->createSampler(samplerDesc);
203     if (!sampler) {
204         return nullptr;
205     }
206 
207     sampler->setKey(key);
208     fResourceCache->insertResource(sampler.get());
209     return sampler;
210 }
211 
findOrCreateBuffer(size_t size,BufferType type,AccessPattern accessPattern,std::string_view label)212 sk_sp<Buffer> ResourceProvider::findOrCreateBuffer(size_t size,
213                                                    BufferType type,
214                                                    AccessPattern accessPattern,
215                                                    std::string_view label) {
216     static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
217 
218     GraphiteResourceKey key;
219     {
220         // For the key we need ((sizeof(size_t) + (sizeof(uint32_t) - 1)) / (sizeof(uint32_t))
221         // uint32_t's for the size and one uint32_t for the rest.
222         static_assert(sizeof(uint32_t) == 4);
223         static const int kSizeKeyNum32DataCnt = (sizeof(size_t) + 3) / 4;
224         static const int kKeyNum32DataCnt =  kSizeKeyNum32DataCnt + 1;
225 
226         SkASSERT(static_cast<uint32_t>(type) < (1u << 4));
227         SkASSERT(static_cast<uint32_t>(accessPattern) < (1u << 1));
228 
229         GraphiteResourceKey::Builder builder(&key, kType, kKeyNum32DataCnt, Shareable::kNo);
230         builder[0] = (static_cast<uint32_t>(type) << 0) |
231                      (static_cast<uint32_t>(accessPattern) << 4);
232         size_t szKey = size;
233         for (int i = 0; i < kSizeKeyNum32DataCnt; ++i) {
234             builder[i + 1] = (uint32_t) szKey;
235 
236             // If size_t is 4 bytes, we cannot do a shift of 32 or else we get a warning/error that
237             // shift amount is >= width of the type.
238             if constexpr(kSizeKeyNum32DataCnt > 1) {
239                 szKey = szKey >> 32;
240             }
241         }
242     }
243 
244     skgpu::Budgeted budgeted = skgpu::Budgeted::kYes;
245     if (Resource* resource = fResourceCache->findAndRefResource(key, budgeted)) {
246         resource->setLabel(std::move(label));
247         return sk_sp<Buffer>(static_cast<Buffer*>(resource));
248     }
249     auto buffer = this->createBuffer(size, type, accessPattern);
250     if (!buffer) {
251         return nullptr;
252     }
253 
254     buffer->setKey(key);
255     buffer->setLabel(std::move(label));
256     fResourceCache->insertResource(buffer.get());
257     return buffer;
258 }
259 
260 namespace {
dimensions_are_valid(const int maxTextureSize,const SkISize & dimensions)261 bool dimensions_are_valid(const int maxTextureSize, const SkISize& dimensions) {
262     if (dimensions.isEmpty() ||
263         dimensions.width()  > maxTextureSize ||
264         dimensions.height() > maxTextureSize) {
265         SKGPU_LOG_W("Call to createBackendTexture has requested dimensions (%d, %d) larger than the"
266                     " supported gpu max texture size: %d. Or the dimensions are empty.",
267                     dimensions.fWidth, dimensions.fHeight, maxTextureSize);
268         return false;
269     }
270     return true;
271 }
272 }
273 
createBackendTexture(SkISize dimensions,const TextureInfo & info)274 BackendTexture ResourceProvider::createBackendTexture(SkISize dimensions, const TextureInfo& info) {
275     if (!dimensions_are_valid(fSharedContext->caps()->maxTextureSize(), dimensions)) {
276         return {};
277     }
278     return this->onCreateBackendTexture(dimensions, info);
279 }
280 
281 #ifdef SK_BUILD_FOR_ANDROID
createBackendTexture(AHardwareBuffer * hardwareBuffer,bool isRenderable,bool isProtectedContent,SkISize dimensions,bool fromAndroidWindow) const282 BackendTexture ResourceProvider::createBackendTexture(AHardwareBuffer* hardwareBuffer,
283                                                       bool isRenderable,
284                                                       bool isProtectedContent,
285                                                       SkISize dimensions,
286                                                       bool fromAndroidWindow) const {
287     if (!dimensions_are_valid(fSharedContext->caps()->maxTextureSize(), dimensions)) {
288         return {};
289     }
290     return this->onCreateBackendTexture(hardwareBuffer,
291                                         isRenderable,
292                                         isProtectedContent,
293                                         dimensions,
294                                         fromAndroidWindow);
295 }
296 
onCreateBackendTexture(AHardwareBuffer *,bool isRenderable,bool isProtectedContent,SkISize dimensions,bool fromAndroidWindow) const297 BackendTexture ResourceProvider::onCreateBackendTexture(AHardwareBuffer*,
298                                                         bool isRenderable,
299                                                         bool isProtectedContent,
300                                                         SkISize dimensions,
301                                                         bool fromAndroidWindow) const {
302     return {};
303 }
304 #endif
305 
deleteBackendTexture(const BackendTexture & texture)306 void ResourceProvider::deleteBackendTexture(const BackendTexture& texture) {
307     this->onDeleteBackendTexture(texture);
308 }
309 
freeGpuResources()310 void ResourceProvider::freeGpuResources() {
311     this->onFreeGpuResources();
312 
313     // TODO: Are there Resources that are ref'd by the ResourceProvider or its subclasses that need
314     // be released? If we ever find that we're holding things directly on the ResourceProviders we
315     // call down into the subclasses to allow them to release things.
316 
317     fResourceCache->purgeResources();
318 }
319 
purgeResourcesNotUsedSince(StdSteadyClock::time_point purgeTime)320 void ResourceProvider::purgeResourcesNotUsedSince(StdSteadyClock::time_point purgeTime) {
321     this->onPurgeResourcesNotUsedSince(purgeTime);
322     fResourceCache->purgeResourcesNotUsedSince(purgeTime);
323 }
324 
325 }  // namespace skgpu::graphite
326