Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
DawnResourceProvider.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2022 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
9
23
24namespace skgpu::graphite {
25
26namespace {
27
28constexpr int kBufferBindingSizeAlignment = 16;
29constexpr int kMaxNumberOfCachedBufferBindGroups = 32;
30constexpr int kMaxNumberOfCachedTextureBindGroups = 4096;
31
32wgpu::ShaderModule create_shader_module(const wgpu::Device& device, const char* source) {
33 wgpu::ShaderModuleWGSLDescriptor wgslDesc;
34 wgslDesc.code = source;
35 wgpu::ShaderModuleDescriptor descriptor;
36 descriptor.nextInChain = &wgslDesc;
37 return device.CreateShaderModule(&descriptor);
38}
39
40wgpu::RenderPipeline create_blit_render_pipeline(const DawnSharedContext* sharedContext,
41 const char* label,
42 wgpu::ShaderModule vsModule,
43 wgpu::ShaderModule fsModule,
44 wgpu::TextureFormat renderPassColorFormat,
45 wgpu::TextureFormat renderPassDepthStencilFormat,
46 int numSamples) {
47 wgpu::RenderPipelineDescriptor descriptor;
48#if defined(SK_DEBUG)
49 descriptor.label = label;
50#endif
51 descriptor.layout = nullptr;
52
53 wgpu::ColorTargetState colorTarget;
54 colorTarget.format = renderPassColorFormat;
55 colorTarget.blend = nullptr;
56 colorTarget.writeMask = wgpu::ColorWriteMask::All;
57
58 wgpu::DepthStencilState depthStencil;
59 if (renderPassDepthStencilFormat != wgpu::TextureFormat::Undefined) {
60 depthStencil.format = renderPassDepthStencilFormat;
61 depthStencil.depthWriteEnabled = false;
62 depthStencil.depthCompare = wgpu::CompareFunction::Always;
63
64 descriptor.depthStencil = &depthStencil;
65 }
66
67 wgpu::FragmentState fragment;
68 fragment.module = std::move(fsModule);
69 fragment.entryPoint = "main";
70 fragment.targetCount = 1;
71 fragment.targets = &colorTarget;
72 descriptor.fragment = &fragment;
73
74 descriptor.vertex.module = std::move(vsModule);
75 descriptor.vertex.entryPoint = "main";
76 descriptor.vertex.constantCount = 0;
77 descriptor.vertex.constants = nullptr;
78 descriptor.vertex.bufferCount = 0;
79 descriptor.vertex.buffers = nullptr;
80
81 descriptor.primitive.frontFace = wgpu::FrontFace::CCW;
82 descriptor.primitive.cullMode = wgpu::CullMode::None;
83 descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleStrip;
84 descriptor.primitive.stripIndexFormat = wgpu::IndexFormat::Undefined;
85
86 descriptor.multisample.count = numSamples;
87 descriptor.multisample.mask = 0xFFFFFFFF;
88 descriptor.multisample.alphaToCoverageEnabled = false;
89
90 std::optional<DawnErrorChecker> errorChecker;
91 if (sharedContext->dawnCaps()->allowScopedErrorChecks()) {
92 errorChecker.emplace(sharedContext);
93 }
94 auto pipeline = sharedContext->device().CreateRenderPipeline(&descriptor);
95 if (errorChecker.has_value() && errorChecker->popErrorScopes() != DawnErrorType::kNoError) {
96 return nullptr;
97 }
98
99 return pipeline;
100}
101
102UniqueKey make_ubo_bind_group_key(
103 const std::array<std::pair<const DawnBuffer*, uint32_t>, 3>& boundBuffersAndSizes) {
104 static const UniqueKey::Domain kBufferBindGroupDomain = UniqueKey::GenerateDomain();
105
106 UniqueKey uniqueKey;
107 {
108 // Each entry in the bind group needs 2 uint32_t in the key:
109 // - buffer's unique ID: 32 bits.
110 // - buffer's binding size: 32 bits.
111 // We need total of 3 entries in the uniform buffer bind group.
112 // Unused entries will be assigned zero values.
113 UniqueKey::Builder builder(
114 &uniqueKey, kBufferBindGroupDomain, 6, "GraphicsPipelineBufferBindGroup");
115
116 for (uint32_t i = 0; i < boundBuffersAndSizes.size(); ++i) {
117 const DawnBuffer* boundBuffer = boundBuffersAndSizes[i].first;
118 const uint32_t bindingSize = boundBuffersAndSizes[i].second;
119 if (boundBuffer) {
120 builder[2 * i] = boundBuffer->uniqueID().asUInt();
121 builder[2 * i + 1] = bindingSize;
122 } else {
123 builder[2 * i] = 0;
124 builder[2 * i + 1] = 0;
125 }
126 }
127
128 builder.finish();
129 }
130
131 return uniqueKey;
132}
133
134UniqueKey make_texture_bind_group_key(const DawnSampler* sampler, const DawnTexture* texture) {
135 static const UniqueKey::Domain kTextureBindGroupDomain = UniqueKey::GenerateDomain();
136
137 UniqueKey uniqueKey;
138 {
139 UniqueKey::Builder builder(&uniqueKey,
140 kTextureBindGroupDomain,
141 2,
142 "GraphicsPipelineSingleTextureSamplerBindGroup");
143
144 builder[0] = sampler->uniqueID().asUInt();
145 builder[1] = texture->uniqueID().asUInt();
146
147 builder.finish();
148 }
149
150 return uniqueKey;
151}
152} // namespace
153
155 SingleOwner* singleOwner,
156 uint32_t recorderID,
157 size_t resourceBudget)
158 : ResourceProvider(sharedContext, singleOwner, recorderID, resourceBudget)
159 , fUniformBufferBindGroupCache(kMaxNumberOfCachedBufferBindGroups)
160 , fSingleTextureSamplerBindGroups(kMaxNumberOfCachedTextureBindGroups) {}
161
163
165 const RenderPassDesc& renderPassDesc) {
166 uint64_t renderPassKey =
167 this->dawnSharedContext()->dawnCaps()->getRenderPassDescKey(renderPassDesc);
168 wgpu::RenderPipeline pipeline = fBlitWithDrawPipelines[renderPassKey];
169 if (!pipeline) {
170 static constexpr char kVertexShaderText[] = R"(
171 var<private> fullscreenTriPositions : array<vec2<f32>, 3> = array<vec2<f32>, 3>(
172 vec2(-1.0, -1.0), vec2(-1.0, 3.0), vec2(3.0, -1.0));
173
174 @vertex
175 fn main(@builtin(vertex_index) vertexIndex : u32) -> @builtin(position) vec4<f32> {
176 return vec4(fullscreenTriPositions[vertexIndex], 1.0, 1.0);
177 }
178 )";
179
180 static constexpr char kFragmentShaderText[] = R"(
181 @group(0) @binding(0) var colorMap: texture_2d<f32>;
182
183 @fragment
184 fn main(@builtin(position) fragPosition : vec4<f32>) -> @location(0) vec4<f32> {
185 var coords : vec2<i32> = vec2<i32>(i32(fragPosition.x), i32(fragPosition.y));
186 return textureLoad(colorMap, coords, 0);
187 }
188 )";
189
190 auto vsModule = create_shader_module(dawnSharedContext()->device(), kVertexShaderText);
191 auto fsModule = create_shader_module(dawnSharedContext()->device(), kFragmentShaderText);
192
193 pipeline = create_blit_render_pipeline(
194 dawnSharedContext(),
195 /*label=*/"BlitWithDraw",
196 std::move(vsModule),
197 std::move(fsModule),
198 /*renderPassColorFormat=*/
199 renderPassDesc.fColorAttachment.fTextureInfo.dawnTextureSpec().getViewFormat(),
200 /*renderPassDepthStencilFormat=*/
202 ? renderPassDesc.fDepthStencilAttachment.fTextureInfo.dawnTextureSpec()
203 .getViewFormat()
204 : wgpu::TextureFormat::Undefined,
205 /*numSamples=*/renderPassDesc.fColorAttachment.fTextureInfo.numSamples());
206
207 if (pipeline) {
208 fBlitWithDrawPipelines.set(renderPassKey, pipeline);
209 }
210 }
211
212 return pipeline;
213}
214
216 // Convert to smart pointers. wgpu::Texture* constructor will increment the ref count.
217 wgpu::Texture dawnTexture = texture.getDawnTexturePtr();
218 wgpu::TextureView dawnTextureView = texture.getDawnTextureViewPtr();
219 SkASSERT(!dawnTexture || !dawnTextureView);
220
221 if (!dawnTexture && !dawnTextureView) {
222 return {};
223 }
224
225 if (dawnTexture) {
226 return DawnTexture::MakeWrapped(this->dawnSharedContext(),
227 texture.dimensions(),
228 texture.info(),
229 std::move(dawnTexture));
230 } else {
231 return DawnTexture::MakeWrapped(this->dawnSharedContext(),
232 texture.dimensions(),
233 texture.info(),
234 std::move(dawnTextureView));
235 }
236}
237
239 SkISize dimensions, const TextureInfo& msaaInfo) {
240 SkASSERT(msaaInfo.isValid());
241
242 // Derive the load texture's info from MSAA texture's info.
243 DawnTextureInfo dawnMsaaLoadTextureInfo;
244 msaaInfo.getDawnTextureInfo(&dawnMsaaLoadTextureInfo);
245 dawnMsaaLoadTextureInfo.fSampleCount = 1;
246 dawnMsaaLoadTextureInfo.fUsage |= wgpu::TextureUsage::TextureBinding;
247
248#if !defined(__EMSCRIPTEN__)
249 // MSAA texture can be transient attachment (memoryless) but the load texture cannot be.
250 // This is because the load texture will need to have its content retained between two passes
251 // loading:
252 // - first pass: the resolve texture is blitted to the load texture.
253 // - 2nd pass: the actual render pass is started and the load texture is blitted to the MSAA
254 // texture.
255 dawnMsaaLoadTextureInfo.fUsage &= (~wgpu::TextureUsage::TransientAttachment);
256#endif
257
258 auto texture = this->findOrCreateDiscardableMSAAAttachment(dimensions, dawnMsaaLoadTextureInfo);
259
260 return sk_sp<DawnTexture>(static_cast<DawnTexture*>(texture.release()));
261}
262
264 const RuntimeEffectDictionary* runtimeDict,
265 const GraphicsPipelineDesc& pipelineDesc,
266 const RenderPassDesc& renderPassDesc) {
267 return DawnGraphicsPipeline::Make(this->dawnSharedContext(),
268 this,
269 runtimeDict,
270 pipelineDesc,
271 renderPassDesc);
272}
273
278
280 const TextureInfo& info,
281 skgpu::Budgeted budgeted) {
282 return DawnTexture::Make(this->dawnSharedContext(), dimensions, info, budgeted);
283}
284
287 AccessPattern accessPattern,
288 std::string_view label) {
289 return DawnBuffer::Make(this->dawnSharedContext(),
290 size,
291 type,
292 accessPattern,
293 std::move(label));
294}
295
297 return DawnSampler::Make(this->dawnSharedContext(),
298 samplerDesc.samplingOptions(),
299 samplerDesc.tileModeX(),
300 samplerDesc.tileModeY());
301}
302
304 const TextureInfo& info) {
305 wgpu::Texture texture = DawnTexture::MakeDawnTexture(this->dawnSharedContext(),
306 dimensions,
307 info);
308 if (!texture) {
309 return {};
310 }
311
312 return BackendTexture(texture.MoveToCHandle());
313}
314
316 SkASSERT(texture.isValid());
317 SkASSERT(texture.backend() == BackendApi::kDawn);
318
319 // Automatically release the pointers in wgpu::TextureView & wgpu::Texture's dtor.
320 // Acquire() won't increment the ref count.
321 wgpu::TextureView::Acquire(texture.getDawnTextureViewPtr());
322 // We need to explicitly call Destroy() here since since that is the recommended way to delete
323 // a Dawn texture predictably versus just dropping a ref and relying on garbage collection.
324 //
325 // Additionally this helps to work around an issue where Skia may have cached a BindGroup that
326 // references the underlying texture. Skia currently doesn't destroy BindGroups when its use of
327 // the texture goes away, thus a ref to the texture remains on the BindGroup and memory is never
328 // cleared up unless we call Destroy() here.
329 wgpu::Texture::Acquire(texture.getDawnTexturePtr()).Destroy();
330}
331
332DawnSharedContext* DawnResourceProvider::dawnSharedContext() const {
333 return static_cast<DawnSharedContext*>(fSharedContext);
334}
335
338 AccessPattern accessPattern,
339 std::string_view label) {
340 sk_sp<Buffer> buffer = this->findOrCreateBuffer(size, type, accessPattern, std::move(label));
341 DawnBuffer* ptr = static_cast<DawnBuffer*>(buffer.release());
342 return sk_sp<DawnBuffer>(ptr);
343}
344
346 if (fUniformBuffersBindGroupLayout) {
347 return fUniformBuffersBindGroupLayout;
348 }
349
350 std::array<wgpu::BindGroupLayoutEntry, 3> entries;
352 entries[0].visibility = wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment;
353 entries[0].buffer.type = wgpu::BufferBindingType::Uniform;
354 entries[0].buffer.hasDynamicOffset = true;
355 entries[0].buffer.minBindingSize = 0;
356
358 entries[1].visibility = wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment;
359 entries[1].buffer.type = fSharedContext->caps()->storageBufferPreferred()
360 ? wgpu::BufferBindingType::ReadOnlyStorage
361 : wgpu::BufferBindingType::Uniform;
362 entries[1].buffer.hasDynamicOffset = true;
363 entries[1].buffer.minBindingSize = 0;
364
366 entries[2].visibility = wgpu::ShaderStage::Fragment;
367 entries[2].buffer.type = fSharedContext->caps()->storageBufferPreferred()
368 ? wgpu::BufferBindingType::ReadOnlyStorage
369 : wgpu::BufferBindingType::Uniform;
370 entries[2].buffer.hasDynamicOffset = true;
371 entries[2].buffer.minBindingSize = 0;
372
373 wgpu::BindGroupLayoutDescriptor groupLayoutDesc;
374#if defined(SK_DEBUG)
375 groupLayoutDesc.label = "Uniform buffers bind group layout";
376#endif
377
378 groupLayoutDesc.entryCount = entries.size();
379 groupLayoutDesc.entries = entries.data();
380 fUniformBuffersBindGroupLayout =
381 this->dawnSharedContext()->device().CreateBindGroupLayout(&groupLayoutDesc);
382
383 return fUniformBuffersBindGroupLayout;
384}
385
386const wgpu::BindGroupLayout&
388 if (fSingleTextureSamplerBindGroupLayout) {
389 return fSingleTextureSamplerBindGroupLayout;
390 }
391
392 std::array<wgpu::BindGroupLayoutEntry, 2> entries;
393
394 entries[0].binding = 0;
395 entries[0].visibility = wgpu::ShaderStage::Fragment;
396 entries[0].sampler.type = wgpu::SamplerBindingType::Filtering;
397
398 entries[1].binding = 1;
399 entries[1].visibility = wgpu::ShaderStage::Fragment;
400 entries[1].texture.sampleType = wgpu::TextureSampleType::Float;
401 entries[1].texture.viewDimension = wgpu::TextureViewDimension::e2D;
402 entries[1].texture.multisampled = false;
403
404 wgpu::BindGroupLayoutDescriptor groupLayoutDesc;
405#if defined(SK_DEBUG)
406 groupLayoutDesc.label = "Single texture + sampler bind group layout";
407#endif
408
409 groupLayoutDesc.entryCount = entries.size();
410 groupLayoutDesc.entries = entries.data();
411 fSingleTextureSamplerBindGroupLayout =
412 this->dawnSharedContext()->device().CreateBindGroupLayout(&groupLayoutDesc);
413
414 return fSingleTextureSamplerBindGroupLayout;
415}
416
417const wgpu::Buffer& DawnResourceProvider::getOrCreateNullBuffer() {
418 if (!fNullBuffer) {
419 wgpu::BufferDescriptor desc;
420#if defined(SK_DEBUG)
421 desc.label = "UnusedBufferSlot";
422#endif
423 desc.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Uniform |
424 wgpu::BufferUsage::Storage;
425 desc.size = kBufferBindingSizeAlignment;
426 desc.mappedAtCreation = false;
427
428 fNullBuffer = this->dawnSharedContext()->device().CreateBuffer(&desc);
429 SkASSERT(fNullBuffer);
430 }
431
432 return fNullBuffer;
433}
434
436 const std::array<std::pair<const DawnBuffer*, uint32_t>, 3>& boundBuffersAndSizes) {
437 auto key = make_ubo_bind_group_key(boundBuffersAndSizes);
438 auto* existingBindGroup = fUniformBufferBindGroupCache.find(key);
439 if (existingBindGroup) {
440 // cache hit.
441 return *existingBindGroup;
442 }
443
444 // Translate to wgpu::BindGroupDescriptor
445 std::array<wgpu::BindGroupEntry, 3> entries;
446
447 constexpr uint32_t kBindingIndices[] = {
451 };
452
453 for (uint32_t i = 0; i < boundBuffersAndSizes.size(); ++i) {
454 const DawnBuffer* boundBuffer = boundBuffersAndSizes[i].first;
455 const uint32_t bindingSize = boundBuffersAndSizes[i].second;
456
457 entries[i].binding = kBindingIndices[i];
458 entries[i].offset = 0;
459 if (boundBuffer) {
460 entries[i].buffer = boundBuffer->dawnBuffer();
461 entries[i].size = SkAlignTo(bindingSize, kBufferBindingSizeAlignment);
462 } else {
463 entries[i].buffer = this->getOrCreateNullBuffer();
464 entries[i].size = wgpu::kWholeSize;
465 }
466 }
467
468 wgpu::BindGroupDescriptor desc;
469 desc.layout = this->getOrCreateUniformBuffersBindGroupLayout();
470 desc.entryCount = entries.size();
471 desc.entries = entries.data();
472
473 const auto& device = this->dawnSharedContext()->device();
474 auto bindGroup = device.CreateBindGroup(&desc);
475
476 return *fUniformBufferBindGroupCache.insert(key, bindGroup);
477}
478
480 const DawnSampler* sampler, const DawnTexture* texture) {
481 auto key = make_texture_bind_group_key(sampler, texture);
482 auto* existingBindGroup = fSingleTextureSamplerBindGroups.find(key);
483 if (existingBindGroup) {
484 // cache hit.
485 return *existingBindGroup;
486 }
487
488 std::array<wgpu::BindGroupEntry, 2> entries;
489
490 entries[0].binding = 0;
491 entries[0].sampler = sampler->dawnSampler();
492 entries[1].binding = 1;
493 entries[1].textureView = texture->sampleTextureView();
494
495 wgpu::BindGroupDescriptor desc;
497 desc.entryCount = entries.size();
498 desc.entries = entries.data();
499
500 const auto& device = this->dawnSharedContext()->device();
501 auto bindGroup = device.CreateBindGroup(&desc);
502
503 return *fSingleTextureSamplerBindGroups.insert(key, bindGroup);
504}
505
506} // namespace skgpu::graphite
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition DM.cpp:213
static constexpr size_t SkAlignTo(size_t x, size_t alignment)
Definition SkAlign.h:33
#define SkASSERT(cond)
Definition SkAssert.h:116
V * find(const K &key)
Definition SkLRUCache.h:49
V * insert(const K &key, V value)
Definition SkLRUCache.h:62
static Domain GenerateDomain()
bool storageBufferPreferred() const
Definition Caps.h:232
static sk_sp< DawnBuffer > Make(const DawnSharedContext *, size_t size, BufferType type, AccessPattern, std::string_view label)
const wgpu::Buffer & dawnBuffer() const
Definition DawnBuffer.h:33
uint64_t getRenderPassDescKey(const RenderPassDesc &renderPassDesc) const
Definition DawnCaps.cpp:836
static sk_sp< DawnComputePipeline > Make(const DawnSharedContext *, const ComputePipelineDesc &)
static constexpr unsigned int kRenderStepUniformBufferIndex
static constexpr unsigned int kPaintUniformBufferIndex
static sk_sp< DawnGraphicsPipeline > Make(const DawnSharedContext *sharedContext, DawnResourceProvider *resourceProvider, const RuntimeEffectDictionary *runtimeDict, const GraphicsPipelineDesc &pipelineDesc, const RenderPassDesc &renderPassDesc)
static constexpr unsigned int kIntrinsicUniformBufferIndex
sk_sp< Sampler > createSampler(const SamplerDesc &) override
sk_sp< GraphicsPipeline > createGraphicsPipeline(const RuntimeEffectDictionary *, const GraphicsPipelineDesc &, const RenderPassDesc &) override
const wgpu::BindGroup & findOrCreateSingleTextureSamplerBindGroup(const DawnSampler *sampler, const DawnTexture *texture)
DawnResourceProvider(SharedContext *sharedContext, SingleOwner *, uint32_t recorderID, size_t resourceBudget)
BackendTexture onCreateBackendTexture(SkISize dimensions, const TextureInfo &) override
const wgpu::BindGroupLayout & getOrCreateSingleTextureSamplerBindGroupLayout()
sk_sp< DawnTexture > findOrCreateDiscardableMSAALoadTexture(SkISize dimensions, const TextureInfo &msaaInfo)
sk_sp< Buffer > createBuffer(size_t size, BufferType type, AccessPattern, std::string_view label) override
sk_sp< Texture > createTexture(SkISize, const TextureInfo &, skgpu::Budgeted) override
sk_sp< Texture > createWrappedTexture(const BackendTexture &) override
const wgpu::BindGroupLayout & getOrCreateUniformBuffersBindGroupLayout()
const wgpu::BindGroup & findOrCreateUniformBuffersBindGroup(const std::array< std::pair< const DawnBuffer *, uint32_t >, 3 > &boundBuffersAndSizes)
void onDeleteBackendTexture(const BackendTexture &) override
wgpu::RenderPipeline findOrCreateBlitWithDrawPipeline(const RenderPassDesc &renderPassDesc)
sk_sp< DawnBuffer > findOrCreateDawnBuffer(size_t size, BufferType type, AccessPattern, std::string_view label)
sk_sp< ComputePipeline > createComputePipeline(const ComputePipelineDesc &) override
const wgpu::Sampler & dawnSampler() const
Definition DawnSampler.h:30
static sk_sp< DawnSampler > Make(const DawnSharedContext *, const SkSamplingOptions &samplingOptions, SkTileMode xTileMode, SkTileMode yTileMode)
const DawnCaps * dawnCaps() const
const wgpu::Device & device() const
static sk_sp< Texture > MakeWrapped(const DawnSharedContext *, SkISize dimensions, const TextureInfo &, wgpu::Texture)
static wgpu::Texture MakeDawnTexture(const DawnSharedContext *, SkISize dimensions, const TextureInfo &)
static sk_sp< Texture > Make(const DawnSharedContext *, SkISize dimensions, const TextureInfo &, skgpu::Budgeted)
sk_sp< Texture > findOrCreateDiscardableMSAAAttachment(SkISize dimensions, const TextureInfo &)
sk_sp< Buffer > findOrCreateBuffer(size_t size, BufferType type, AccessPattern, std::string_view label)
const Caps * caps() const
uint32_t numSamples() const
Definition TextureInfo.h:78
V * set(K key, V val)
Definition SkTHash.h:472
VkDevice device
Definition main.cc:53
SkBitmap source
Definition examples.cpp:28
static const uint8_t buffer[]
FlTexture * texture
Budgeted
Definition GpuTypes.h:35
wgpu::TextureUsage fUsage
Definition DawnTypes.h:24
SkTileMode tileModeX() const
SkSamplingOptions samplingOptions() const
SkTileMode tileModeY() const