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/gpu/graphite/Buffer.h"
14 #include "src/gpu/graphite/Caps.h"
15 #include "src/gpu/graphite/CommandBuffer.h"
16 #include "src/gpu/graphite/ComputePipeline.h"
17 #include "src/gpu/graphite/ContextPriv.h"
18 #include "src/gpu/graphite/GlobalCache.h"
19 #include "src/gpu/graphite/GraphicsPipeline.h"
20 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
21 #include "src/gpu/graphite/Log.h"
22 #include "src/gpu/graphite/ResourceCache.h"
23 #include "src/gpu/graphite/Sampler.h"
24 #include "src/gpu/graphite/SharedContext.h"
25 #include "src/gpu/graphite/Texture.h"
26 #include "src/sksl/SkSLCompiler.h"
27
28 namespace skgpu::graphite {
29
ResourceProvider(SharedContext * sharedContext,SingleOwner * singleOwner)30 ResourceProvider::ResourceProvider(SharedContext* sharedContext,
31 SingleOwner* singleOwner)
32 : fSharedContext(sharedContext)
33 , fResourceCache(ResourceCache::Make(singleOwner))
34 , fCompiler(std::make_unique<SkSL::Compiler>(fSharedContext->caps()->shaderCaps())) {}
35
~ResourceProvider()36 ResourceProvider::~ResourceProvider() {
37 fResourceCache->shutdown();
38 }
39
findOrCreateGraphicsPipeline(const RuntimeEffectDictionary * runtimeDict,const GraphicsPipelineDesc & pipelineDesc,const RenderPassDesc & renderPassDesc)40 sk_sp<GraphicsPipeline> ResourceProvider::findOrCreateGraphicsPipeline(
41 const RuntimeEffectDictionary* runtimeDict,
42 const GraphicsPipelineDesc& pipelineDesc,
43 const RenderPassDesc& renderPassDesc) {
44 auto globalCache = fSharedContext->globalCache();
45 UniqueKey pipelineKey = fSharedContext->caps()->makeGraphicsPipelineKey(pipelineDesc,
46 renderPassDesc);
47 sk_sp<GraphicsPipeline> pipeline = globalCache->findGraphicsPipeline(pipelineKey);
48 if (!pipeline) {
49 // Haven't encountered this pipeline, so create a new one. Since pipelines are shared
50 // across Recorders, we could theoretically create equivalent pipelines on different
51 // threads. If this happens, GlobalCache returns the first-through-gate pipeline and we
52 // discard the redundant pipeline. While this is wasted effort in the rare event of a race,
53 // it allows pipeline creation to be performed without locking the global cache.
54 pipeline = this->createGraphicsPipeline(runtimeDict, pipelineDesc, renderPassDesc);
55 if (pipeline) {
56 // TODO: Should we store a null pipeline if we failed to create one so that subsequent
57 // usage immediately sees that the pipeline cannot be created, vs. retrying every time?
58 pipeline = globalCache->addGraphicsPipeline(pipelineKey, std::move(pipeline));
59 }
60 }
61 return pipeline;
62 }
63
findOrCreateComputePipeline(const ComputePipelineDesc & pipelineDesc)64 sk_sp<ComputePipeline> ResourceProvider::findOrCreateComputePipeline(
65 const ComputePipelineDesc& pipelineDesc) {
66 auto globalCache = fSharedContext->globalCache();
67 UniqueKey pipelineKey = fSharedContext->caps()->makeComputePipelineKey(pipelineDesc);
68 sk_sp<ComputePipeline> pipeline = globalCache->findComputePipeline(pipelineKey);
69 if (!pipeline) {
70 pipeline = this->createComputePipeline(pipelineDesc);
71 if (pipeline) {
72 pipeline = globalCache->addComputePipeline(pipelineKey, std::move(pipeline));
73 }
74 }
75 return pipeline;
76 }
77
78 ////////////////////////////////////////////////////////////////////////////////////////////////
79
findOrCreateScratchTexture(SkISize dimensions,const TextureInfo & info,skgpu::Budgeted budgeted)80 sk_sp<Texture> ResourceProvider::findOrCreateScratchTexture(SkISize dimensions,
81 const TextureInfo& info,
82 skgpu::Budgeted budgeted) {
83 SkASSERT(info.isValid());
84
85 static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
86
87 GraphiteResourceKey key;
88 // Scratch textures are not shareable
89 fSharedContext->caps()->buildKeyForTexture(dimensions, info, kType, Shareable::kNo, &key);
90
91 return this->findOrCreateTextureWithKey(dimensions, info, key, budgeted);
92 }
93
findOrCreateDepthStencilAttachment(SkISize dimensions,const TextureInfo & info)94 sk_sp<Texture> ResourceProvider::findOrCreateDepthStencilAttachment(SkISize dimensions,
95 const TextureInfo& info) {
96 SkASSERT(info.isValid());
97
98 static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
99
100 GraphiteResourceKey key;
101 // We always make depth and stencil attachments shareable. Between any render pass the values
102 // are reset. Thus it is safe to be used by multiple different render passes without worry of
103 // stomping on each other's data.
104 fSharedContext->caps()->buildKeyForTexture(dimensions, info, kType, Shareable::kYes, &key);
105
106 return this->findOrCreateTextureWithKey(dimensions, info, key, skgpu::Budgeted::kYes);
107 }
108
findOrCreateDiscardableMSAAAttachment(SkISize dimensions,const TextureInfo & info)109 sk_sp<Texture> ResourceProvider::findOrCreateDiscardableMSAAAttachment(SkISize dimensions,
110 const TextureInfo& info) {
111 SkASSERT(info.isValid());
112
113 static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
114
115 GraphiteResourceKey key;
116 // We always make discardable msaa attachments shareable. Between any render pass we discard
117 // the values of the MSAA texture. Thus it is safe to be used by multiple different render
118 // passes without worry of stomping on each other's data. It is the callings code responsiblity
119 // to populate the discardable MSAA texture with data at the start of the render pass.
120 fSharedContext->caps()->buildKeyForTexture(dimensions, info, kType, Shareable::kYes, &key);
121
122 return this->findOrCreateTextureWithKey(dimensions, info, key, skgpu::Budgeted::kYes);
123 }
124
findOrCreateTextureWithKey(SkISize dimensions,const TextureInfo & info,const GraphiteResourceKey & key,skgpu::Budgeted budgeted)125 sk_sp<Texture> ResourceProvider::findOrCreateTextureWithKey(SkISize dimensions,
126 const TextureInfo& info,
127 const GraphiteResourceKey& key,
128 skgpu::Budgeted budgeted) {
129 // If the resource is shareable it should be budgeted since it shouldn't be backing any client
130 // owned object.
131 SkASSERT(key.shareable() == Shareable::kNo || budgeted == skgpu::Budgeted::kYes);
132
133 if (Resource* resource = fResourceCache->findAndRefResource(key, budgeted)) {
134 return sk_sp<Texture>(static_cast<Texture*>(resource));
135 }
136
137 auto tex = this->createTexture(dimensions, info, budgeted);
138 if (!tex) {
139 return nullptr;
140 }
141
142 tex->setKey(key);
143 fResourceCache->insertResource(tex.get());
144
145 return tex;
146 }
147
findOrCreateCompatibleSampler(const SkSamplingOptions & smplOptions,SkTileMode xTileMode,SkTileMode yTileMode)148 sk_sp<Sampler> ResourceProvider::findOrCreateCompatibleSampler(const SkSamplingOptions& smplOptions,
149 SkTileMode xTileMode,
150 SkTileMode yTileMode) {
151 static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
152
153 GraphiteResourceKey key;
154 {
155 constexpr int kNumTileModeBits = SkNextLog2_portable(int(SkTileMode::kLastTileMode)+1);
156 constexpr int kNumFilterModeBits = SkNextLog2_portable(int(SkFilterMode::kLast)+1);
157 constexpr int kNumMipmapModeBits = SkNextLog2_portable(int(SkMipmapMode::kLast)+1);
158
159 constexpr int kTileModeXShift = 0;
160 constexpr int kTileModeYShift = kTileModeXShift + kNumTileModeBits;
161 constexpr int kFilterModeShift = kTileModeYShift + kNumTileModeBits;
162 constexpr int kMipmapModeShift = kFilterModeShift + kNumFilterModeBits;
163
164 static_assert(kMipmapModeShift + kNumMipmapModeBits <= 32);
165
166 // For the key we need only one uint32_t.
167 // TODO: add aniso value when used
168 static_assert(sizeof(uint32_t) == 4);
169
170 GraphiteResourceKey::Builder builder(&key, kType, 1, Shareable::kYes);
171 uint32_t myKey =
172 (static_cast<uint32_t>(xTileMode) << kTileModeXShift) |
173 (static_cast<uint32_t>(yTileMode) << kTileModeYShift) |
174 (static_cast<uint32_t>(smplOptions.filter) << kFilterModeShift) |
175 (static_cast<uint32_t>(smplOptions.mipmap) << kMipmapModeShift);
176 builder[0] = myKey;
177 }
178
179 skgpu::Budgeted budgeted = skgpu::Budgeted::kYes;
180 if (Resource* resource = fResourceCache->findAndRefResource(key, budgeted)) {
181 return sk_sp<Sampler>(static_cast<Sampler*>(resource));
182 }
183 sk_sp<Sampler> sampler = this->createSampler(smplOptions, xTileMode, yTileMode);
184 if (!sampler) {
185 return nullptr;
186 }
187
188 sampler->setKey(key);
189 fResourceCache->insertResource(sampler.get());
190 return sampler;
191 }
192
findOrCreateBuffer(size_t size,BufferType type,PrioritizeGpuReads prioritizeGpuReads)193 sk_sp<Buffer> ResourceProvider::findOrCreateBuffer(size_t size,
194 BufferType type,
195 PrioritizeGpuReads prioritizeGpuReads) {
196 static const ResourceType kType = GraphiteResourceKey::GenerateResourceType();
197
198 #ifdef SK_DEBUG
199 // The size should already be aligned.
200 size_t minAlignment = 1;
201 if (type == BufferType::kStorage || type == BufferType::kIndirect ||
202 type == BufferType::kVertexStorage || type == BufferType::kIndexStorage) {
203 minAlignment = std::max(fSharedContext->caps()->requiredStorageBufferAlignment(),
204 minAlignment);
205 } else if (type == BufferType::kUniform) {
206 minAlignment = std::max(fSharedContext->caps()->requiredUniformBufferAlignment(),
207 minAlignment);
208 } else if (type == BufferType::kXferCpuToGpu || type == BufferType::kXferGpuToCpu) {
209 minAlignment = std::max(fSharedContext->caps()->requiredTransferBufferAlignment(),
210 minAlignment);
211 }
212 SkASSERT(size % minAlignment == 0);
213 #endif
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>(prioritizeGpuReads) < (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>(prioritizeGpuReads) << 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 return sk_sp<Buffer>(static_cast<Buffer*>(resource));
244 }
245 auto buffer = this->createBuffer(size, type, prioritizeGpuReads);
246 if (!buffer) {
247 return nullptr;
248 }
249
250 buffer->setKey(key);
251 fResourceCache->insertResource(buffer.get());
252 return buffer;
253 }
254
createBackendTexture(SkISize dimensions,const TextureInfo & info)255 BackendTexture ResourceProvider::createBackendTexture(SkISize dimensions, const TextureInfo& info) {
256 const auto maxTextureSize = fSharedContext->caps()->maxTextureSize();
257 if (dimensions.isEmpty() ||
258 dimensions.width() > maxTextureSize ||
259 dimensions.height() > maxTextureSize) {
260 SKGPU_LOG_W("call to createBackendTexture has requested dimensions (%d, %d) larger than the"
261 " supported gpu max texture size: %d. Or the dimensions are empty.",
262 dimensions.fWidth, dimensions.fHeight, maxTextureSize);
263 return {};
264 }
265
266 return this->onCreateBackendTexture(dimensions, info);
267 }
268
deleteBackendTexture(BackendTexture & texture)269 void ResourceProvider::deleteBackendTexture(BackendTexture& texture) {
270 this->onDeleteBackendTexture(texture);
271 // Invalidate the texture;
272 texture = BackendTexture();
273 }
274
275 } // namespace skgpu::graphite
276