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