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
8 #include "src/gpu/graphite/dawn/DawnCommandBuffer.h"
9
10 #include "include/gpu/graphite/TextureInfo.h"
11 #include "src/gpu/graphite/ContextUtils.h"
12 #include "src/gpu/graphite/Log.h"
13 #include "src/gpu/graphite/RenderPassDesc.h"
14 #include "src/gpu/graphite/TextureProxy.h"
15 #include "src/gpu/graphite/UniformManager.h"
16 #include "src/gpu/graphite/compute/DispatchGroup.h"
17 #include "src/gpu/graphite/dawn/DawnBuffer.h"
18 #include "src/gpu/graphite/dawn/DawnCaps.h"
19 #include "src/gpu/graphite/dawn/DawnComputePipeline.h"
20 #include "src/gpu/graphite/dawn/DawnGraphicsPipeline.h"
21 #include "src/gpu/graphite/dawn/DawnGraphiteTypesPriv.h"
22 #include "src/gpu/graphite/dawn/DawnGraphiteUtilsPriv.h"
23 #include "src/gpu/graphite/dawn/DawnQueueManager.h"
24 #include "src/gpu/graphite/dawn/DawnSampler.h"
25 #include "src/gpu/graphite/dawn/DawnSharedContext.h"
26 #include "src/gpu/graphite/dawn/DawnTexture.h"
27
28 #if defined(__EMSCRIPTEN__)
29 #include <emscripten/version.h>
30 #endif
31
32 namespace skgpu::graphite {
33
34 // On emsdk before 3.1.48 the API for RenderPass and ComputePass timestamps was different
35 // and does not map to current webgpu. We check this in DawnCaps but we also must avoid
36 // naming the types from the new API because they aren't defined.
37 #if defined(__EMSCRIPTEN__) \
38 && ((__EMSCRIPTEN_major__ < 3) \
39 || (__EMSCRIPTEN_major__ == 3 && __EMSCRIPTEN_minor__ < 1) \
40 || (__EMSCRIPTEN_major__ == 3 && __EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ < 48))
41 #define WGPU_TIMESTAMP_WRITES_DEFINED 0
42 #else
43 #define WGPU_TIMESTAMP_WRITES_DEFINED 1
44 #endif
45
46 // DawnCommandBuffer
47 // ----------------------------------------------------------------------------
Make(const DawnSharedContext * sharedContext,DawnResourceProvider * resourceProvider)48 std::unique_ptr<DawnCommandBuffer> DawnCommandBuffer::Make(const DawnSharedContext* sharedContext,
49 DawnResourceProvider* resourceProvider) {
50 std::unique_ptr<DawnCommandBuffer> cmdBuffer(
51 new DawnCommandBuffer(sharedContext, resourceProvider));
52 if (!cmdBuffer->setNewCommandBufferResources()) {
53 return {};
54 }
55 return cmdBuffer;
56 }
57
DawnCommandBuffer(const DawnSharedContext * sharedContext,DawnResourceProvider * resourceProvider)58 DawnCommandBuffer::DawnCommandBuffer(const DawnSharedContext* sharedContext,
59 DawnResourceProvider* resourceProvider)
60 : CommandBuffer(Protected::kNo) // Dawn doesn't support protected memory
61 , fSharedContext(sharedContext)
62 , fResourceProvider(resourceProvider) {}
63
~DawnCommandBuffer()64 DawnCommandBuffer::~DawnCommandBuffer() {}
65
startTimerQuery()66 bool DawnCommandBuffer::startTimerQuery() {
67 wgpu::QuerySet querySet = std::move(fTimestampQuerySet);
68
69 auto buffer = fResourceProvider->findOrCreateDawnBuffer(2 * sizeof(uint64_t),
70 BufferType::kQuery,
71 AccessPattern::kHostVisible,
72 "TimerQuery");
73 if (!buffer) {
74 SKGPU_LOG_W("Failed to create buffer for resolving results, timer query will "
75 "not be reported.");
76 return false;
77 }
78 sk_sp<DawnBuffer> xferBuffer;
79 if (!fSharedContext->caps()->drawBufferCanBeMapped()) {
80 xferBuffer = fResourceProvider->findOrCreateDawnBuffer(2 * sizeof(uint64_t),
81 BufferType::kXferGpuToCpu,
82 AccessPattern::kHostVisible,
83 "TimerQueryXfer");
84 if (!xferBuffer) {
85 SKGPU_LOG_W("Failed to create buffer for transferring timestamp results, timer "
86 "query will not be reported.");
87 return false;
88 }
89 }
90 if (!querySet) {
91 wgpu::QuerySetDescriptor descriptor;
92 descriptor.count = 2;
93 descriptor.label = "Command Buffer Timer Query";
94 descriptor.type = wgpu::QueryType::Timestamp;
95 descriptor.nextInChain = nullptr;
96 querySet = fSharedContext->device().CreateQuerySet(&descriptor);
97 if (!querySet) {
98 SKGPU_LOG_W("Failed to create query set, timer query will not be reported.");
99 return false;
100 }
101 }
102
103 fTimestampQuerySet = std::move(querySet);
104 fTimestampQueryBuffer = std::move(buffer);
105 fTimestampQueryXferBuffer = std::move(xferBuffer);
106
107 if (fSharedContext->dawnCaps()->supportsCommandBufferTimestamps()) {
108 // In native Dawn we use the writeTimeStamp method on CommandBuffer to bracket
109 // the whole command buffer.
110 fCommandEncoder.WriteTimestamp(fTimestampQuerySet, 0);
111 } else {
112 // On WebGPU the best we can do is add timestamps to each render/compute pass as we record
113 // them. Since we don't know a priori how many passes there will be we write a begin
114 // timestamp on the first pass and an end on every pass, overwriting the second query in the
115 // set.
116 fWroteFirstPassTimestamps = false;
117 }
118
119 return true;
120 }
121
endTimerQuery()122 void DawnCommandBuffer::endTimerQuery() {
123 SkASSERT(fTimestampQuerySet);
124 SkASSERT(fTimestampQueryBuffer);
125 if (fSharedContext->dawnCaps()->supportsCommandBufferTimestamps()) {
126 fCommandEncoder.WriteTimestamp(fTimestampQuerySet, 1);
127 } else if (!fWroteFirstPassTimestamps) {
128 // If we didn't have any passes then we can't report anything.
129 fTimestampQueryBuffer = nullptr;
130 fTimestampQueryXferBuffer = nullptr;
131 return;
132 }
133 // This resolve covers both timestamp code paths (command encoder and render/compute pass).
134 fCommandEncoder.ResolveQuerySet(
135 fTimestampQuerySet, 0, 2, fTimestampQueryBuffer->dawnBuffer(), 0);
136 if (fTimestampQueryXferBuffer) {
137 SkASSERT(fTimestampQueryBuffer->size() == fTimestampQueryBuffer->size());
138 fCommandEncoder.CopyBufferToBuffer(fTimestampQueryBuffer->dawnBuffer(),
139 /*sourceOffset=*/0,
140 fTimestampQueryXferBuffer->dawnBuffer(),
141 /*destinationOffset=*/0,
142 /*size=*/fTimestampQueryBuffer->size());
143 fTimestampQueryBuffer.reset();
144 }
145 if (fSharedContext->caps()->bufferMapsAreAsync()) {
146 sk_sp<Buffer> buffer =
147 fTimestampQueryXferBuffer ? fTimestampQueryXferBuffer : fTimestampQueryBuffer;
148 this->addBuffersToAsyncMapOnSubmit({&buffer, 1});
149 }
150 }
151
gpuStats()152 std::optional<GpuStats> DawnCommandBuffer::gpuStats() {
153 auto& buffer = fTimestampQueryXferBuffer ? fTimestampQueryXferBuffer : fTimestampQueryBuffer;
154 if (!buffer) {
155 return {};
156 }
157 if (fSharedContext->caps()->bufferMapsAreAsync()) {
158 if (!buffer->isMapped()) {
159 return {};
160 }
161 }
162 uint64_t* results = static_cast<uint64_t*>(buffer->map());
163 if (!results) {
164 SKGPU_LOG_W("Failed to get timer query results because buffer couldn't be mapped.");
165 return {};
166 }
167 if (results[1] < results[0]) {
168 return {};
169 }
170 GpuStats stats;
171 stats.elapsedTime = results[1] - results[0];
172 return stats;
173 }
174
finishEncoding()175 wgpu::CommandBuffer DawnCommandBuffer::finishEncoding() {
176 SkASSERT(fCommandEncoder);
177 wgpu::CommandBuffer cmdBuffer = fCommandEncoder.Finish();
178
179 fCommandEncoder = nullptr;
180
181 return cmdBuffer;
182 }
183
onResetCommandBuffer()184 void DawnCommandBuffer::onResetCommandBuffer() {
185 fActiveGraphicsPipeline = nullptr;
186 fActiveRenderPassEncoder = nullptr;
187 fActiveComputePassEncoder = nullptr;
188 fCommandEncoder = nullptr;
189
190 for (auto& bufferSlot : fBoundUniforms) {
191 bufferSlot = {};
192 }
193 fBoundUniformBuffersDirty = true;
194 if (fTimestampQueryBuffer && fTimestampQueryBuffer->isUnmappable()) {
195 fTimestampQueryBuffer->unmap();
196 }
197 if (fTimestampQueryXferBuffer && fTimestampQueryXferBuffer->isUnmappable()) {
198 fTimestampQueryXferBuffer->unmap();
199 }
200 fTimestampQueryBuffer = {};
201 fTimestampQueryXferBuffer = {};
202 fWroteFirstPassTimestamps = false;
203 }
204
setNewCommandBufferResources()205 bool DawnCommandBuffer::setNewCommandBufferResources() {
206 SkASSERT(!fCommandEncoder);
207 fCommandEncoder = fSharedContext->device().CreateCommandEncoder();
208 SkASSERT(fCommandEncoder);
209
210 return true;
211 }
212
onAddRenderPass(const RenderPassDesc & renderPassDesc,SkIRect renderPassBounds,const Texture * colorTexture,const Texture * resolveTexture,const Texture * depthStencilTexture,SkIRect viewport,const DrawPassList & drawPasses)213 bool DawnCommandBuffer::onAddRenderPass(const RenderPassDesc& renderPassDesc,
214 SkIRect renderPassBounds,
215 const Texture* colorTexture,
216 const Texture* resolveTexture,
217 const Texture* depthStencilTexture,
218 SkIRect viewport,
219 const DrawPassList& drawPasses) {
220 // `viewport` has already been translated by the replay translation by the base CommandBuffer.
221 // All GPU backends support viewports that are defined to extend beyond the render target
222 // (allowing for a stable linear transformation from NDC to viewport coordinates as the replay
223 // translation pushes the viewport off the final deferred target's edges).
224 // However, WebGPU validation layers currently require that the viewport is contained within
225 // the attachment so we intersect the viewport before setting the intrinsic constants or
226 // viewport state.
227 // TODO(https://github.com/gpuweb/gpuweb/issues/373): Hopefully the validation layers can be
228 // relaxed and then this extra intersection can be removed.
229 if (!viewport.intersect(SkIRect::MakeSize(fColorAttachmentSize))) SK_UNLIKELY {
230 // The entire pass is offscreen
231 return true;
232 }
233
234 // Update the intrinsic constant buffer before starting a render pass.
235 if (!this->updateIntrinsicUniforms(viewport)) SK_UNLIKELY {
236 return false;
237 }
238
239 if (!this->beginRenderPass(renderPassDesc,
240 renderPassBounds,
241 colorTexture,
242 resolveTexture,
243 depthStencilTexture)) SK_UNLIKELY {
244 return false;
245 }
246
247 this->setViewport(viewport);
248
249 for (const auto& drawPass : drawPasses) {
250 if (!this->addDrawPass(drawPass.get())) SK_UNLIKELY {
251 this->endRenderPass();
252 return false;
253 }
254 }
255
256 this->endRenderPass();
257 return true;
258 }
259
onAddComputePass(DispatchGroupSpan groups)260 bool DawnCommandBuffer::onAddComputePass(DispatchGroupSpan groups) {
261 this->beginComputePass();
262 for (const auto& group : groups) {
263 group->addResourceRefs(this);
264 for (const auto& dispatch : group->dispatches()) {
265 this->bindComputePipeline(group->getPipeline(dispatch.fPipelineIndex));
266 this->bindDispatchResources(*group, dispatch);
267 if (const WorkgroupSize* globalSize =
268 std::get_if<WorkgroupSize>(&dispatch.fGlobalSizeOrIndirect)) {
269 this->dispatchWorkgroups(*globalSize);
270 } else {
271 SkASSERT(std::holds_alternative<BindBufferInfo>(dispatch.fGlobalSizeOrIndirect));
272 const BindBufferInfo& indirect =
273 *std::get_if<BindBufferInfo>(&dispatch.fGlobalSizeOrIndirect);
274 this->dispatchWorkgroupsIndirect(indirect.fBuffer, indirect.fOffset);
275 }
276 }
277 }
278 this->endComputePass();
279 return true;
280 }
281
beginRenderPass(const RenderPassDesc & renderPassDesc,SkIRect renderPassBounds,const Texture * colorTexture,const Texture * resolveTexture,const Texture * depthStencilTexture)282 bool DawnCommandBuffer::beginRenderPass(const RenderPassDesc& renderPassDesc,
283 SkIRect renderPassBounds,
284 const Texture* colorTexture,
285 const Texture* resolveTexture,
286 const Texture* depthStencilTexture) {
287 SkASSERT(!fActiveRenderPassEncoder);
288 SkASSERT(!fActiveComputePassEncoder);
289
290 constexpr static wgpu::LoadOp wgpuLoadActionMap[]{
291 wgpu::LoadOp::Load,
292 wgpu::LoadOp::Clear,
293 wgpu::LoadOp::Clear // Don't care
294 };
295 static_assert((int)LoadOp::kLoad == 0);
296 static_assert((int)LoadOp::kClear == 1);
297 static_assert((int)LoadOp::kDiscard == 2);
298 static_assert(std::size(wgpuLoadActionMap) == kLoadOpCount);
299
300 constexpr static wgpu::StoreOp wgpuStoreActionMap[]{wgpu::StoreOp::Store,
301 wgpu::StoreOp::Discard};
302 static_assert((int)StoreOp::kStore == 0);
303 static_assert((int)StoreOp::kDiscard == 1);
304 static_assert(std::size(wgpuStoreActionMap) == kStoreOpCount);
305
306 wgpu::RenderPassDescriptor wgpuRenderPass = {};
307 wgpu::RenderPassColorAttachment wgpuColorAttachment;
308 wgpu::RenderPassDepthStencilAttachment wgpuDepthStencilAttachment;
309
310 // Set up color attachment.
311 #if !defined(__EMSCRIPTEN__)
312 wgpu::DawnRenderPassColorAttachmentRenderToSingleSampled mssaRenderToSingleSampledDesc;
313 wgpu::RenderPassDescriptorExpandResolveRect wgpuPartialRect = {};
314 #endif
315
316 #if WGPU_TIMESTAMP_WRITES_DEFINED
317 wgpu::RenderPassTimestampWrites wgpuTimestampWrites;
318 if (!fSharedContext->dawnCaps()->supportsCommandBufferTimestamps() && fTimestampQueryBuffer) {
319 SkASSERT(fTimestampQuerySet);
320 wgpuTimestampWrites.querySet = fTimestampQuerySet;
321 if (!fWroteFirstPassTimestamps) {
322 wgpuTimestampWrites.beginningOfPassWriteIndex = 0;
323 fWroteFirstPassTimestamps = true;
324 }
325 wgpuTimestampWrites.endOfPassWriteIndex = 1;
326 wgpuRenderPass.timestampWrites = &wgpuTimestampWrites;
327 }
328 #else
329 SkASSERT(!fTimestampQueryBuffer);
330 #endif
331
332 auto& colorInfo = renderPassDesc.fColorAttachment;
333 bool loadMSAAFromResolveExplicitly = false;
334 if (colorTexture) {
335 wgpuRenderPass.colorAttachments = &wgpuColorAttachment;
336 wgpuRenderPass.colorAttachmentCount = 1;
337
338 // TODO: check Texture matches RenderPassDesc
339 const auto* dawnColorTexture = static_cast<const DawnTexture*>(colorTexture);
340 SkASSERT(dawnColorTexture->renderTextureView());
341 wgpuColorAttachment.view = dawnColorTexture->renderTextureView();
342
343 const std::array<float, 4>& clearColor = renderPassDesc.fClearColor;
344 wgpuColorAttachment.clearValue = {
345 clearColor[0], clearColor[1], clearColor[2], clearColor[3]};
346 wgpuColorAttachment.loadOp = wgpuLoadActionMap[static_cast<int>(colorInfo.fLoadOp)];
347 wgpuColorAttachment.storeOp = wgpuStoreActionMap[static_cast<int>(colorInfo.fStoreOp)];
348
349 // Set up resolve attachment
350 if (resolveTexture) {
351 SkASSERT(renderPassDesc.fColorResolveAttachment.fStoreOp == StoreOp::kStore);
352 // TODO: check Texture matches RenderPassDesc
353 const auto* dawnResolveTexture = static_cast<const DawnTexture*>(resolveTexture);
354 SkASSERT(dawnResolveTexture->renderTextureView());
355 wgpuColorAttachment.resolveTarget = dawnResolveTexture->renderTextureView();
356
357 // Inclusion of a resolve texture implies the client wants to finish the
358 // renderpass with a resolve.
359 SkASSERT(wgpuColorAttachment.storeOp == wgpu::StoreOp::Discard);
360
361 // But it also means we have to load the resolve texture into the MSAA color attachment
362 if (renderPassDesc.fColorResolveAttachment.fLoadOp == LoadOp::kLoad) {
363 std::optional<wgpu::LoadOp> resolveLoadOp =
364 fSharedContext->dawnCaps()->resolveTextureLoadOp();
365 if (resolveLoadOp.has_value()) {
366 wgpuColorAttachment.loadOp = *resolveLoadOp;
367 #if !defined(__EMSCRIPTEN__)
368 if (fSharedContext->dawnCaps()->supportsPartialLoadResolve()) {
369 wgpuPartialRect.x = renderPassBounds.x();
370 wgpuPartialRect.y = renderPassBounds.y();
371 wgpuPartialRect.width = renderPassBounds.width();
372 wgpuPartialRect.height = renderPassBounds.height();
373 wgpuRenderPass.nextInChain = &wgpuPartialRect;
374 }
375 #endif
376 } else {
377 // No Dawn built-in support, we need to manually load the resolve texture.
378 loadMSAAFromResolveExplicitly = true;
379 }
380 }
381 // TODO: If the color resolve texture is read-only we can use a private (vs. memoryless)
382 // msaa attachment that's coupled to the framebuffer and the StoreAndMultisampleResolve
383 // action instead of loading as a draw.
384 } else {
385 [[maybe_unused]] bool isMSAAToSingleSampled = renderPassDesc.fSampleCount > 1 &&
386 colorTexture->numSamples() == 1;
387 #if defined(__EMSCRIPTEN__)
388 SkASSERT(!isMSAAToSingleSampled);
389 #else
390 if (isMSAAToSingleSampled) {
391 // If render pass is multi sampled but the color attachment is single sampled, we
392 // need to activate multisampled render to single sampled feature for this render
393 // pass.
394 SkASSERT(fSharedContext->device().HasFeature(
395 wgpu::FeatureName::MSAARenderToSingleSampled));
396
397 wgpuColorAttachment.nextInChain = &mssaRenderToSingleSampledDesc;
398 mssaRenderToSingleSampledDesc.implicitSampleCount = renderPassDesc.fSampleCount;
399 }
400 #endif
401 }
402 }
403
404 // Set up stencil/depth attachment
405 auto& depthStencilInfo = renderPassDesc.fDepthStencilAttachment;
406 if (depthStencilTexture) {
407 const auto* dawnDepthStencilTexture = static_cast<const DawnTexture*>(depthStencilTexture);
408 auto format = TextureInfos::GetDawnViewFormat(dawnDepthStencilTexture->textureInfo());
409 SkASSERT(DawnFormatIsDepthOrStencil(format));
410
411 // TODO: check Texture matches RenderPassDesc
412 SkASSERT(dawnDepthStencilTexture->renderTextureView());
413 wgpuDepthStencilAttachment.view = dawnDepthStencilTexture->renderTextureView();
414
415 if (DawnFormatIsDepth(format)) {
416 wgpuDepthStencilAttachment.depthClearValue = renderPassDesc.fClearDepth;
417 wgpuDepthStencilAttachment.depthLoadOp =
418 wgpuLoadActionMap[static_cast<int>(depthStencilInfo.fLoadOp)];
419 wgpuDepthStencilAttachment.depthStoreOp =
420 wgpuStoreActionMap[static_cast<int>(depthStencilInfo.fStoreOp)];
421 }
422
423 if (DawnFormatIsStencil(format)) {
424 wgpuDepthStencilAttachment.stencilClearValue = renderPassDesc.fClearStencil;
425 wgpuDepthStencilAttachment.stencilLoadOp =
426 wgpuLoadActionMap[static_cast<int>(depthStencilInfo.fLoadOp)];
427 wgpuDepthStencilAttachment.stencilStoreOp =
428 wgpuStoreActionMap[static_cast<int>(depthStencilInfo.fStoreOp)];
429 }
430
431 wgpuRenderPass.depthStencilAttachment = &wgpuDepthStencilAttachment;
432 } else {
433 SkASSERT(!depthStencilInfo.fTextureInfo.isValid());
434 }
435
436 if (loadMSAAFromResolveExplicitly) {
437 // Manually load the contents of the resolve texture into the MSAA attachment as a draw,
438 // so the actual load op for the MSAA attachment had better have been discard.
439
440 if (!this->loadMSAAFromResolveAndBeginRenderPassEncoder(
441 renderPassDesc,
442 wgpuRenderPass,
443 static_cast<const DawnTexture*>(colorTexture))) {
444 return false;
445 }
446 }
447 else {
448 fActiveRenderPassEncoder = fCommandEncoder.BeginRenderPass(&wgpuRenderPass);
449 }
450
451 return true;
452 }
453
loadMSAAFromResolveAndBeginRenderPassEncoder(const RenderPassDesc & frontendRenderPassDesc,const wgpu::RenderPassDescriptor & wgpuRenderPassDesc,const DawnTexture * msaaTexture)454 bool DawnCommandBuffer::loadMSAAFromResolveAndBeginRenderPassEncoder(
455 const RenderPassDesc& frontendRenderPassDesc,
456 const wgpu::RenderPassDescriptor& wgpuRenderPassDesc,
457 const DawnTexture* msaaTexture) {
458 SkASSERT(!fActiveRenderPassEncoder);
459
460 // Copy from resolve texture to an intermediate texture. Using blit with draw
461 // pipeline because the resolveTexture might be created from a swapchain, and it
462 // is possible that only its texture view is available. So onCopyTextureToTexture()
463 // which operates on wgpu::Texture instead of wgpu::TextureView cannot be used in that case.
464 auto msaaLoadTexture = fResourceProvider->findOrCreateDiscardableMSAALoadTexture(
465 msaaTexture->dimensions(), msaaTexture->textureInfo());
466 if (!msaaLoadTexture) {
467 SKGPU_LOG_E("DawnCommandBuffer::loadMSAAFromResolveAndBeginRenderPassEncoder: "
468 "Can't create MSAA Load Texture.");
469 return false;
470 }
471
472 this->trackCommandBufferResource(msaaLoadTexture);
473
474 // Creating intermediate render pass (copy from resolve texture -> MSAA load texture)
475 RenderPassDesc intermediateRenderPassDesc = {};
476 intermediateRenderPassDesc.fColorAttachment.fLoadOp = LoadOp::kDiscard;
477 intermediateRenderPassDesc.fColorAttachment.fStoreOp = StoreOp::kStore;
478 intermediateRenderPassDesc.fColorAttachment.fTextureInfo =
479 frontendRenderPassDesc.fColorResolveAttachment.fTextureInfo;
480
481 wgpu::RenderPassColorAttachment wgpuIntermediateColorAttachment;
482 // Dawn doesn't support actual DontCare so use LoadOp::Clear.
483 wgpuIntermediateColorAttachment.loadOp = wgpu::LoadOp::Clear;
484 wgpuIntermediateColorAttachment.clearValue = {1, 1, 1, 1};
485 wgpuIntermediateColorAttachment.storeOp = wgpu::StoreOp::Store;
486 wgpuIntermediateColorAttachment.view = msaaLoadTexture->renderTextureView();
487
488 wgpu::RenderPassDescriptor wgpuIntermediateRenderPassDesc;
489 wgpuIntermediateRenderPassDesc.colorAttachmentCount = 1;
490 wgpuIntermediateRenderPassDesc.colorAttachments = &wgpuIntermediateColorAttachment;
491
492 auto renderPassEncoder = fCommandEncoder.BeginRenderPass(&wgpuIntermediateRenderPassDesc);
493
494 bool blitSucceeded = this->doBlitWithDraw(
495 renderPassEncoder,
496 intermediateRenderPassDesc,
497 /*sourceTextureView=*/wgpuRenderPassDesc.colorAttachments[0].resolveTarget,
498 msaaTexture->dimensions().width(),
499 msaaTexture->dimensions().height());
500
501 renderPassEncoder.End();
502
503 if (!blitSucceeded) {
504 return false;
505 }
506
507 // Start actual render pass (blit from MSAA load texture -> MSAA texture)
508 renderPassEncoder = fCommandEncoder.BeginRenderPass(&wgpuRenderPassDesc);
509
510 if (!this->doBlitWithDraw(renderPassEncoder,
511 frontendRenderPassDesc,
512 /*sourceTextureView=*/msaaLoadTexture->renderTextureView(),
513 msaaTexture->dimensions().width(),
514 msaaTexture->dimensions().height())) {
515 renderPassEncoder.End();
516 return false;
517 }
518
519 fActiveRenderPassEncoder = renderPassEncoder;
520
521 return true;
522 }
523
doBlitWithDraw(const wgpu::RenderPassEncoder & renderEncoder,const RenderPassDesc & frontendRenderPassDesc,const wgpu::TextureView & sourceTextureView,int width,int height)524 bool DawnCommandBuffer::doBlitWithDraw(const wgpu::RenderPassEncoder& renderEncoder,
525 const RenderPassDesc& frontendRenderPassDesc,
526 const wgpu::TextureView& sourceTextureView,
527 int width,
528 int height) {
529 auto loadPipeline = fResourceProvider->findOrCreateBlitWithDrawPipeline(frontendRenderPassDesc);
530 if (!loadPipeline) {
531 SKGPU_LOG_E("Unable to create pipeline to blit with draw");
532 return false;
533 }
534
535 SkASSERT(renderEncoder);
536
537 renderEncoder.SetPipeline(loadPipeline);
538
539 // The load msaa pipeline takes no uniforms, no vertex/instance attributes and only uses
540 // one texture that does not require a sampler.
541
542 // TODO: b/260368758
543 // cache single texture's bind group creation.
544 wgpu::BindGroupEntry entry;
545 entry.binding = 0;
546 entry.textureView = sourceTextureView;
547
548 wgpu::BindGroupDescriptor desc;
549 desc.layout = loadPipeline.GetBindGroupLayout(0);
550 desc.entryCount = 1;
551 desc.entries = &entry;
552
553 auto bindGroup = fSharedContext->device().CreateBindGroup(&desc);
554
555 renderEncoder.SetBindGroup(0, bindGroup);
556
557 renderEncoder.SetScissorRect(0, 0, width, height);
558 renderEncoder.SetViewport(0, 0, width, height, 0, 1);
559
560 // Fullscreen triangle
561 renderEncoder.Draw(3);
562
563 return true;
564 }
565
endRenderPass()566 void DawnCommandBuffer::endRenderPass() {
567 SkASSERT(fActiveRenderPassEncoder);
568 fActiveRenderPassEncoder.End();
569 fActiveRenderPassEncoder = nullptr;
570 }
571
addDrawPass(const DrawPass * drawPass)572 bool DawnCommandBuffer::addDrawPass(const DrawPass* drawPass) {
573 drawPass->addResourceRefs(this);
574 for (auto [type, cmdPtr] : drawPass->commands()) {
575 switch (type) {
576 case DrawPassCommands::Type::kBindGraphicsPipeline: {
577 auto bgp = static_cast<DrawPassCommands::BindGraphicsPipeline*>(cmdPtr);
578 if (!this->bindGraphicsPipeline(drawPass->getPipeline(bgp->fPipelineIndex)))
579 SK_UNLIKELY { return false; }
580 break;
581 }
582 case DrawPassCommands::Type::kSetBlendConstants: {
583 auto sbc = static_cast<DrawPassCommands::SetBlendConstants*>(cmdPtr);
584 this->setBlendConstants(sbc->fBlendConstants);
585 break;
586 }
587 case DrawPassCommands::Type::kBindUniformBuffer: {
588 auto bub = static_cast<DrawPassCommands::BindUniformBuffer*>(cmdPtr);
589 this->bindUniformBuffer(bub->fInfo, bub->fSlot);
590 break;
591 }
592 case DrawPassCommands::Type::kBindDrawBuffers: {
593 auto bdb = static_cast<DrawPassCommands::BindDrawBuffers*>(cmdPtr);
594 this->bindDrawBuffers(
595 bdb->fVertices, bdb->fInstances, bdb->fIndices, bdb->fIndirect);
596 break;
597 }
598 case DrawPassCommands::Type::kBindTexturesAndSamplers: {
599 auto bts = static_cast<DrawPassCommands::BindTexturesAndSamplers*>(cmdPtr);
600 this->bindTextureAndSamplers(*drawPass, *bts);
601 break;
602 }
603 case DrawPassCommands::Type::kSetScissor: {
604 auto ss = static_cast<DrawPassCommands::SetScissor*>(cmdPtr);
605 this->setScissor(ss->fScissor);
606 break;
607 }
608 case DrawPassCommands::Type::kDraw: {
609 auto draw = static_cast<DrawPassCommands::Draw*>(cmdPtr);
610 this->draw(draw->fType, draw->fBaseVertex, draw->fVertexCount);
611 break;
612 }
613 case DrawPassCommands::Type::kDrawIndexed: {
614 auto draw = static_cast<DrawPassCommands::DrawIndexed*>(cmdPtr);
615 this->drawIndexed(
616 draw->fType, draw->fBaseIndex, draw->fIndexCount, draw->fBaseVertex);
617 break;
618 }
619 case DrawPassCommands::Type::kDrawInstanced: {
620 auto draw = static_cast<DrawPassCommands::DrawInstanced*>(cmdPtr);
621 this->drawInstanced(draw->fType,
622 draw->fBaseVertex,
623 draw->fVertexCount,
624 draw->fBaseInstance,
625 draw->fInstanceCount);
626 break;
627 }
628 case DrawPassCommands::Type::kDrawIndexedInstanced: {
629 auto draw = static_cast<DrawPassCommands::DrawIndexedInstanced*>(cmdPtr);
630 this->drawIndexedInstanced(draw->fType,
631 draw->fBaseIndex,
632 draw->fIndexCount,
633 draw->fBaseVertex,
634 draw->fBaseInstance,
635 draw->fInstanceCount);
636 break;
637 }
638 case DrawPassCommands::Type::kDrawIndirect: {
639 auto draw = static_cast<DrawPassCommands::DrawIndirect*>(cmdPtr);
640 this->drawIndirect(draw->fType);
641 break;
642 }
643 case DrawPassCommands::Type::kDrawIndexedIndirect: {
644 auto draw = static_cast<DrawPassCommands::DrawIndexedIndirect*>(cmdPtr);
645 this->drawIndexedIndirect(draw->fType);
646 break;
647 }
648 }
649 }
650
651 return true;
652 }
653
bindGraphicsPipeline(const GraphicsPipeline * graphicsPipeline)654 bool DawnCommandBuffer::bindGraphicsPipeline(const GraphicsPipeline* graphicsPipeline) {
655 SkASSERT(fActiveRenderPassEncoder);
656
657 auto* dawnGraphicsPipeline = static_cast<const DawnGraphicsPipeline*>(graphicsPipeline);
658 auto& wgpuPipeline = dawnGraphicsPipeline->dawnRenderPipeline();
659 if (!wgpuPipeline) SK_UNLIKELY {
660 return false;
661 }
662 fActiveGraphicsPipeline = dawnGraphicsPipeline;
663 fActiveRenderPassEncoder.SetPipeline(wgpuPipeline);
664 fBoundUniformBuffersDirty = true;
665
666 if (fActiveGraphicsPipeline->dstReadRequirement() == DstReadRequirement::kTextureCopy &&
667 fActiveGraphicsPipeline->numFragTexturesAndSamplers() == 2) {
668 // The pipeline has a single paired texture+sampler and uses texture copies for dst reads.
669 // This situation comes about when the program requires complex blending but otherwise
670 // is not referencing any images. Since there are no other images in play, the DrawPass
671 // will not have a BindTexturesAndSamplers command that we can tack the dstCopy on to.
672 // Instead we need to set the texture BindGroup ASAP to just the dstCopy.
673 // TODO(b/366254117): Once we standardize on a pipeline layout across all backends, the dst
674 // copy texture may not go in a group with the regular textures, in which case this binding
675 // can hopefully happen in a single place (e.g. here or at the start of the renderpass and
676 // not also every other time the textures are changed).
677 const auto* texture = static_cast<const DawnTexture*>(fDstCopy.first);
678 const auto* sampler = static_cast<const DawnSampler*>(fDstCopy.second);
679 wgpu::BindGroup bindGroup =
680 fResourceProvider->findOrCreateSingleTextureSamplerBindGroup(sampler, texture);
681 fActiveRenderPassEncoder.SetBindGroup(
682 DawnGraphicsPipeline::kTextureBindGroupIndex, bindGroup);
683 }
684
685 return true;
686 }
687
bindUniformBuffer(const BindBufferInfo & info,UniformSlot slot)688 void DawnCommandBuffer::bindUniformBuffer(const BindBufferInfo& info, UniformSlot slot) {
689 SkASSERT(fActiveRenderPassEncoder);
690
691 unsigned int bufferIndex;
692 switch (slot) {
693 case UniformSlot::kRenderStep:
694 bufferIndex = DawnGraphicsPipeline::kRenderStepUniformBufferIndex;
695 break;
696 case UniformSlot::kPaint:
697 bufferIndex = DawnGraphicsPipeline::kPaintUniformBufferIndex;
698 break;
699 case UniformSlot::kGradient:
700 bufferIndex = DawnGraphicsPipeline::kGradientBufferIndex;
701 break;
702 }
703
704 fBoundUniforms[bufferIndex] = info;
705 fBoundUniformBuffersDirty = true;
706 }
707
bindDrawBuffers(const BindBufferInfo & vertices,const BindBufferInfo & instances,const BindBufferInfo & indices,const BindBufferInfo & indirect)708 void DawnCommandBuffer::bindDrawBuffers(const BindBufferInfo& vertices,
709 const BindBufferInfo& instances,
710 const BindBufferInfo& indices,
711 const BindBufferInfo& indirect) {
712 SkASSERT(fActiveRenderPassEncoder);
713
714 if (vertices.fBuffer) {
715 auto dawnBuffer = static_cast<const DawnBuffer*>(vertices.fBuffer)->dawnBuffer();
716 fActiveRenderPassEncoder.SetVertexBuffer(
717 DawnGraphicsPipeline::kVertexBufferIndex, dawnBuffer, vertices.fOffset);
718 }
719 if (instances.fBuffer) {
720 auto dawnBuffer = static_cast<const DawnBuffer*>(instances.fBuffer)->dawnBuffer();
721 fActiveRenderPassEncoder.SetVertexBuffer(
722 DawnGraphicsPipeline::kInstanceBufferIndex, dawnBuffer, instances.fOffset);
723 }
724 if (indices.fBuffer) {
725 auto dawnBuffer = static_cast<const DawnBuffer*>(indices.fBuffer)->dawnBuffer();
726 fActiveRenderPassEncoder.SetIndexBuffer(
727 dawnBuffer, wgpu::IndexFormat::Uint16, indices.fOffset);
728 }
729 if (indirect.fBuffer) {
730 fCurrentIndirectBuffer = static_cast<const DawnBuffer*>(indirect.fBuffer)->dawnBuffer();
731 fCurrentIndirectBufferOffset = indirect.fOffset;
732 } else {
733 fCurrentIndirectBuffer = nullptr;
734 fCurrentIndirectBufferOffset = 0;
735 }
736 }
737
bindTextureAndSamplers(const DrawPass & drawPass,const DrawPassCommands::BindTexturesAndSamplers & command)738 void DawnCommandBuffer::bindTextureAndSamplers(
739 const DrawPass& drawPass, const DrawPassCommands::BindTexturesAndSamplers& command) {
740 SkASSERT(fActiveRenderPassEncoder);
741 SkASSERT(fActiveGraphicsPipeline);
742
743 // When there's an active graphics pipeline with a texture-copy dstread requirement, add one
744 // to account for the intrinsic dstCopy texture we bind here.
745 // NOTE: This is in units of pairs of textures and samplers, whereas the value reported by
746 // the current pipeline is in net bindings (textures + samplers).
747 int numTexturesAndSamplers = command.fNumTexSamplers;
748 if (fActiveGraphicsPipeline->dstReadRequirement() == DstReadRequirement::kTextureCopy) {
749 numTexturesAndSamplers++;
750 }
751 SkASSERT(fActiveGraphicsPipeline->numFragTexturesAndSamplers() == 2*numTexturesAndSamplers);
752
753 // If possible, it's ideal to optimize for the common case of using a single texture with one
754 // dynamic sampler. When using only one sampler, determine whether it is static or dynamic.
755 bool usingSingleStaticSampler = false;
756 #if !defined(__EMSCRIPTEN__)
757 if (command.fNumTexSamplers == 1) {
758 const wgpu::YCbCrVkDescriptor& ycbcrDesc =
759 TextureInfos::GetDawnTextureSpec(
760 drawPass.getTexture(command.fTextureIndices[0])->textureInfo())
761 .fYcbcrVkDescriptor;
762 usingSingleStaticSampler = ycbcrUtils::DawnDescriptorIsValid(ycbcrDesc);
763 }
764 #endif
765
766 wgpu::BindGroup bindGroup;
767 // Optimize for single texture with dynamic sampling.
768 if (numTexturesAndSamplers == 1 && !usingSingleStaticSampler) {
769 SkASSERT(fActiveGraphicsPipeline->numFragTexturesAndSamplers() == 2);
770 SkASSERT(fActiveGraphicsPipeline->dstReadRequirement() != DstReadRequirement::kTextureCopy);
771
772 const auto* texture =
773 static_cast<const DawnTexture*>(drawPass.getTexture(command.fTextureIndices[0]));
774 const auto* sampler =
775 static_cast<const DawnSampler*>(drawPass.getSampler(command.fSamplerIndices[0]));
776 bindGroup = fResourceProvider->findOrCreateSingleTextureSamplerBindGroup(sampler, texture);
777 } else {
778 std::vector<wgpu::BindGroupEntry> entries;
779
780 for (int i = 0; i < command.fNumTexSamplers; ++i) {
781 const auto* texture = static_cast<const DawnTexture*>(
782 drawPass.getTexture(command.fTextureIndices[i]));
783 const auto* sampler = static_cast<const DawnSampler*>(
784 drawPass.getSampler(command.fSamplerIndices[i]));
785 auto& wgpuTextureView = texture->sampleTextureView();
786 auto& wgpuSampler = sampler->dawnSampler();
787
788 #if !defined(__EMSCRIPTEN__)
789 // Assuming shader generator assigns binding slot to sampler then texture,
790 // then the next sampler and texture, and so on, we need to use
791 // 2 * i as base binding index of the sampler and texture.
792 // TODO: https://b.corp.google.com/issues/259457090:
793 // Better configurable way of assigning samplers and textures' bindings.
794
795 DawnTextureInfo dawnTextureInfo;
796 TextureInfos::GetDawnTextureInfo(texture->textureInfo(), &dawnTextureInfo);
797 const wgpu::YCbCrVkDescriptor& ycbcrDesc = dawnTextureInfo.fYcbcrVkDescriptor;
798
799 // Only add a sampler as a bind group entry if it's a regular dynamic sampler. A valid
800 // YCbCrVkDescriptor indicates the usage of a static sampler, which should not be
801 // included here. They should already be fully specified in the bind group layout.
802 if (!ycbcrUtils::DawnDescriptorIsValid(ycbcrDesc)) {
803 #endif
804 wgpu::BindGroupEntry samplerEntry;
805 samplerEntry.binding = 2 * i;
806 samplerEntry.sampler = wgpuSampler;
807 entries.push_back(samplerEntry);
808 #if !defined(__EMSCRIPTEN__)
809 }
810 #endif
811 wgpu::BindGroupEntry textureEntry;
812 textureEntry.binding = 2 * i + 1;
813 textureEntry.textureView = wgpuTextureView;
814 entries.push_back(textureEntry);
815 }
816
817 if (fActiveGraphicsPipeline->dstReadRequirement() == DstReadRequirement::kTextureCopy) {
818 // Append the dstCopy sampler and texture as the very last two bind group entries
819 wgpu::BindGroupEntry samplerEntry;
820 samplerEntry.binding = 2*numTexturesAndSamplers - 2;
821 samplerEntry.sampler = static_cast<const DawnSampler*>(fDstCopy.second)->dawnSampler();
822 entries.push_back(samplerEntry);
823
824 wgpu::BindGroupEntry textureEntry;
825 textureEntry.binding = 2*numTexturesAndSamplers - 1;
826 textureEntry.textureView =
827 static_cast<const DawnTexture*>(fDstCopy.first)->sampleTextureView();
828 entries.push_back(textureEntry);
829 }
830
831 wgpu::BindGroupDescriptor desc;
832 const auto& groupLayouts = fActiveGraphicsPipeline->dawnGroupLayouts();
833 desc.layout = groupLayouts[DawnGraphicsPipeline::kTextureBindGroupIndex];
834 desc.entryCount = entries.size();
835 desc.entries = entries.data();
836
837 bindGroup = fSharedContext->device().CreateBindGroup(&desc);
838 }
839
840 fActiveRenderPassEncoder.SetBindGroup(DawnGraphicsPipeline::kTextureBindGroupIndex, bindGroup);
841 }
842
syncUniformBuffers()843 void DawnCommandBuffer::syncUniformBuffers() {
844 static constexpr int kNumBuffers = DawnGraphicsPipeline::kNumUniformBuffers;
845
846 if (fBoundUniformBuffersDirty) {
847 fBoundUniformBuffersDirty = false;
848
849 std::array<uint32_t, kNumBuffers> dynamicOffsets;
850 std::array<std::pair<const DawnBuffer*, uint32_t>, kNumBuffers> boundBuffersAndSizes;
851
852 std::array<bool, kNumBuffers> enabled = {
853 true, // intrinsic uniforms are always enabled
854 fActiveGraphicsPipeline->hasStepUniforms(), // render step uniforms
855 fActiveGraphicsPipeline->hasPaintUniforms(), // paint uniforms
856 fActiveGraphicsPipeline->hasGradientBuffer(), // gradient SSBO
857 };
858
859 for (int i = 0; i < kNumBuffers; ++i) {
860 if (enabled[i] && fBoundUniforms[i]) {
861 boundBuffersAndSizes[i].first =
862 static_cast<const DawnBuffer*>(fBoundUniforms[i].fBuffer);
863 boundBuffersAndSizes[i].second = fBoundUniforms[i].fSize;
864 dynamicOffsets[i] = fBoundUniforms[i].fOffset;
865 } else {
866 // Unused or null binding
867 boundBuffersAndSizes[i].first = nullptr;
868 dynamicOffsets[i] = 0;
869 }
870 }
871
872 auto bindGroup =
873 fResourceProvider->findOrCreateUniformBuffersBindGroup(boundBuffersAndSizes);
874
875 fActiveRenderPassEncoder.SetBindGroup(DawnGraphicsPipeline::kUniformBufferBindGroupIndex,
876 bindGroup,
877 dynamicOffsets.size(),
878 dynamicOffsets.data());
879 }
880 }
881
setScissor(const Scissor & scissor)882 void DawnCommandBuffer::setScissor(const Scissor& scissor) {
883 SkASSERT(fActiveRenderPassEncoder);
884 SkIRect rect = scissor.getRect(fReplayTranslation, fReplayClip);
885 fActiveRenderPassEncoder.SetScissorRect(rect.x(), rect.y(), rect.width(), rect.height());
886 }
887
updateIntrinsicUniforms(SkIRect viewport)888 bool DawnCommandBuffer::updateIntrinsicUniforms(SkIRect viewport) {
889 UniformManager intrinsicValues{Layout::kStd140};
890 CollectIntrinsicUniforms(fSharedContext->caps(), viewport, fDstCopyBounds, &intrinsicValues);
891
892 BindBufferInfo binding = fResourceProvider->findOrCreateIntrinsicBindBufferInfo(
893 this, UniformDataBlock::Wrap(&intrinsicValues));
894 if (!binding) {
895 return false;
896 } else if (binding == fBoundUniforms[DawnGraphicsPipeline::kIntrinsicUniformBufferIndex]) {
897 return true; // no binding change needed
898 }
899
900 fBoundUniforms[DawnGraphicsPipeline::kIntrinsicUniformBufferIndex] = binding;
901 fBoundUniformBuffersDirty = true;
902 return true;
903 }
904
setViewport(SkIRect viewport)905 void DawnCommandBuffer::setViewport(SkIRect viewport) {
906 SkASSERT(fActiveRenderPassEncoder);
907 fActiveRenderPassEncoder.SetViewport(
908 viewport.x(), viewport.y(), viewport.width(), viewport.height(), 0, 1);
909 }
910
setBlendConstants(float * blendConstants)911 void DawnCommandBuffer::setBlendConstants(float* blendConstants) {
912 SkASSERT(fActiveRenderPassEncoder);
913 wgpu::Color blendConst = {
914 blendConstants[0], blendConstants[1], blendConstants[2], blendConstants[3]};
915 fActiveRenderPassEncoder.SetBlendConstant(&blendConst);
916 }
917
draw(PrimitiveType type,unsigned int baseVertex,unsigned int vertexCount)918 void DawnCommandBuffer::draw(PrimitiveType type,
919 unsigned int baseVertex,
920 unsigned int vertexCount) {
921 SkASSERT(fActiveRenderPassEncoder);
922 SkASSERT(fActiveGraphicsPipeline->primitiveType() == type);
923
924 this->syncUniformBuffers();
925
926 fActiveRenderPassEncoder.Draw(vertexCount, /*instanceCount=*/1, baseVertex);
927 }
928
drawIndexed(PrimitiveType type,unsigned int baseIndex,unsigned int indexCount,unsigned int baseVertex)929 void DawnCommandBuffer::drawIndexed(PrimitiveType type,
930 unsigned int baseIndex,
931 unsigned int indexCount,
932 unsigned int baseVertex) {
933 SkASSERT(fActiveRenderPassEncoder);
934 SkASSERT(fActiveGraphicsPipeline->primitiveType() == type);
935
936 this->syncUniformBuffers();
937
938 fActiveRenderPassEncoder.DrawIndexed(indexCount, /*instanceCount=*/1, baseIndex, baseVertex);
939 }
940
drawInstanced(PrimitiveType type,unsigned int baseVertex,unsigned int vertexCount,unsigned int baseInstance,unsigned int instanceCount)941 void DawnCommandBuffer::drawInstanced(PrimitiveType type,
942 unsigned int baseVertex,
943 unsigned int vertexCount,
944 unsigned int baseInstance,
945 unsigned int instanceCount) {
946 SkASSERT(fActiveRenderPassEncoder);
947 SkASSERT(fActiveGraphicsPipeline->primitiveType() == type);
948
949 this->syncUniformBuffers();
950
951 fActiveRenderPassEncoder.Draw(vertexCount, instanceCount, baseVertex, baseInstance);
952 }
953
drawIndexedInstanced(PrimitiveType type,unsigned int baseIndex,unsigned int indexCount,unsigned int baseVertex,unsigned int baseInstance,unsigned int instanceCount)954 void DawnCommandBuffer::drawIndexedInstanced(PrimitiveType type,
955 unsigned int baseIndex,
956 unsigned int indexCount,
957 unsigned int baseVertex,
958 unsigned int baseInstance,
959 unsigned int instanceCount) {
960 SkASSERT(fActiveRenderPassEncoder);
961 SkASSERT(fActiveGraphicsPipeline->primitiveType() == type);
962
963 this->syncUniformBuffers();
964
965 fActiveRenderPassEncoder.DrawIndexed(
966 indexCount, instanceCount, baseIndex, baseVertex, baseInstance);
967 }
968
drawIndirect(PrimitiveType type)969 void DawnCommandBuffer::drawIndirect(PrimitiveType type) {
970 SkASSERT(fActiveRenderPassEncoder);
971 SkASSERT(fActiveGraphicsPipeline->primitiveType() == type);
972 SkASSERT(fCurrentIndirectBuffer);
973
974 this->syncUniformBuffers();
975
976 fActiveRenderPassEncoder.DrawIndirect(fCurrentIndirectBuffer, fCurrentIndirectBufferOffset);
977 }
978
drawIndexedIndirect(PrimitiveType type)979 void DawnCommandBuffer::drawIndexedIndirect(PrimitiveType type) {
980 SkASSERT(fActiveRenderPassEncoder);
981 SkASSERT(fActiveGraphicsPipeline->primitiveType() == type);
982 SkASSERT(fCurrentIndirectBuffer);
983
984 this->syncUniformBuffers();
985
986 fActiveRenderPassEncoder.DrawIndexedIndirect(fCurrentIndirectBuffer,
987 fCurrentIndirectBufferOffset);
988 }
989
beginComputePass()990 void DawnCommandBuffer::beginComputePass() {
991 SkASSERT(!fActiveRenderPassEncoder);
992 SkASSERT(!fActiveComputePassEncoder);
993 wgpu::ComputePassDescriptor wgpuComputePassDescriptor = {};
994 #if WGPU_TIMESTAMP_WRITES_DEFINED
995 wgpu::ComputePassTimestampWrites wgpuTimestampWrites;
996 if (!fSharedContext->dawnCaps()->supportsCommandBufferTimestamps() && fTimestampQueryBuffer) {
997 SkASSERT(fTimestampQuerySet);
998 wgpuTimestampWrites.querySet = fTimestampQuerySet;
999 if (!fWroteFirstPassTimestamps) {
1000 wgpuTimestampWrites.beginningOfPassWriteIndex = 0;
1001 fWroteFirstPassTimestamps = true;
1002 }
1003 wgpuTimestampWrites.endOfPassWriteIndex = 1;
1004 wgpuComputePassDescriptor.timestampWrites = &wgpuTimestampWrites;
1005 }
1006 #else
1007 SkASSERT(!fTimestampQueryBuffer);
1008 #endif
1009 fActiveComputePassEncoder = fCommandEncoder.BeginComputePass(&wgpuComputePassDescriptor);
1010 }
1011
bindComputePipeline(const ComputePipeline * computePipeline)1012 void DawnCommandBuffer::bindComputePipeline(const ComputePipeline* computePipeline) {
1013 SkASSERT(fActiveComputePassEncoder);
1014
1015 fActiveComputePipeline = static_cast<const DawnComputePipeline*>(computePipeline);
1016 fActiveComputePassEncoder.SetPipeline(fActiveComputePipeline->dawnComputePipeline());
1017 }
1018
bindDispatchResources(const DispatchGroup & group,const DispatchGroup::Dispatch & dispatch)1019 void DawnCommandBuffer::bindDispatchResources(const DispatchGroup& group,
1020 const DispatchGroup::Dispatch& dispatch) {
1021 SkASSERT(fActiveComputePassEncoder);
1022 SkASSERT(fActiveComputePipeline);
1023
1024 // Bind all pipeline resources to a single new bind group at index 0.
1025 // NOTE: Caching the bind groups here might be beneficial based on the layout and the bound
1026 // resources (though it's questionable how often a bind group will end up getting reused since
1027 // the bound objects change often).
1028 skia_private::TArray<wgpu::BindGroupEntry> entries;
1029 entries.reserve(dispatch.fBindings.size());
1030
1031 for (const ResourceBinding& binding : dispatch.fBindings) {
1032 wgpu::BindGroupEntry& entry = entries.push_back();
1033 entry.binding = binding.fIndex;
1034 if (const BindBufferInfo* buffer = std::get_if<BindBufferInfo>(&binding.fResource)) {
1035 entry.buffer = static_cast<const DawnBuffer*>(buffer->fBuffer)->dawnBuffer();
1036 entry.offset = buffer->fOffset;
1037 entry.size = buffer->fSize;
1038 } else if (const TextureIndex* texIdx = std::get_if<TextureIndex>(&binding.fResource)) {
1039 const DawnTexture* texture =
1040 static_cast<const DawnTexture*>(group.getTexture(texIdx->fValue));
1041 SkASSERT(texture);
1042 entry.textureView = texture->sampleTextureView();
1043 } else if (const SamplerIndex* samplerIdx = std::get_if<SamplerIndex>(&binding.fResource)) {
1044 const DawnSampler* sampler =
1045 static_cast<const DawnSampler*>(group.getSampler(samplerIdx->fValue));
1046 entry.sampler = sampler->dawnSampler();
1047 } else {
1048 SK_ABORT("unsupported dispatch resource type");
1049 }
1050 }
1051
1052 wgpu::BindGroupDescriptor desc;
1053 desc.layout = fActiveComputePipeline->dawnGroupLayout();
1054 desc.entryCount = entries.size();
1055 desc.entries = entries.data();
1056
1057 auto bindGroup = fSharedContext->device().CreateBindGroup(&desc);
1058 fActiveComputePassEncoder.SetBindGroup(0, bindGroup);
1059 }
1060
dispatchWorkgroups(const WorkgroupSize & globalSize)1061 void DawnCommandBuffer::dispatchWorkgroups(const WorkgroupSize& globalSize) {
1062 SkASSERT(fActiveComputePassEncoder);
1063 SkASSERT(fActiveComputePipeline);
1064
1065 fActiveComputePassEncoder.DispatchWorkgroups(
1066 globalSize.fWidth, globalSize.fHeight, globalSize.fDepth);
1067 }
1068
dispatchWorkgroupsIndirect(const Buffer * indirectBuffer,size_t indirectBufferOffset)1069 void DawnCommandBuffer::dispatchWorkgroupsIndirect(const Buffer* indirectBuffer,
1070 size_t indirectBufferOffset) {
1071 SkASSERT(fActiveComputePassEncoder);
1072 SkASSERT(fActiveComputePipeline);
1073
1074 auto& wgpuIndirectBuffer = static_cast<const DawnBuffer*>(indirectBuffer)->dawnBuffer();
1075 fActiveComputePassEncoder.DispatchWorkgroupsIndirect(wgpuIndirectBuffer, indirectBufferOffset);
1076 }
1077
endComputePass()1078 void DawnCommandBuffer::endComputePass() {
1079 SkASSERT(fActiveComputePassEncoder);
1080 fActiveComputePassEncoder.End();
1081 fActiveComputePassEncoder = nullptr;
1082 }
1083
onCopyBufferToBuffer(const Buffer * srcBuffer,size_t srcOffset,const Buffer * dstBuffer,size_t dstOffset,size_t size)1084 bool DawnCommandBuffer::onCopyBufferToBuffer(const Buffer* srcBuffer,
1085 size_t srcOffset,
1086 const Buffer* dstBuffer,
1087 size_t dstOffset,
1088 size_t size) {
1089 SkASSERT(!fActiveRenderPassEncoder);
1090 SkASSERT(!fActiveComputePassEncoder);
1091
1092 auto& wgpuBufferSrc = static_cast<const DawnBuffer*>(srcBuffer)->dawnBuffer();
1093 auto& wgpuBufferDst = static_cast<const DawnBuffer*>(dstBuffer)->dawnBuffer();
1094
1095 fCommandEncoder.CopyBufferToBuffer(wgpuBufferSrc, srcOffset, wgpuBufferDst, dstOffset, size);
1096 return true;
1097 }
1098
onCopyTextureToBuffer(const Texture * texture,SkIRect srcRect,const Buffer * buffer,size_t bufferOffset,size_t bufferRowBytes)1099 bool DawnCommandBuffer::onCopyTextureToBuffer(const Texture* texture,
1100 SkIRect srcRect,
1101 const Buffer* buffer,
1102 size_t bufferOffset,
1103 size_t bufferRowBytes) {
1104 SkASSERT(!fActiveRenderPassEncoder);
1105 SkASSERT(!fActiveComputePassEncoder);
1106
1107 const auto* wgpuTexture = static_cast<const DawnTexture*>(texture);
1108 auto& wgpuBuffer = static_cast<const DawnBuffer*>(buffer)->dawnBuffer();
1109
1110 wgpu::ImageCopyTexture src;
1111 src.texture = wgpuTexture->dawnTexture();
1112 src.origin.x = srcRect.x();
1113 src.origin.y = srcRect.y();
1114 src.aspect = TextureInfos::GetDawnAspect(wgpuTexture->textureInfo());
1115
1116 wgpu::ImageCopyBuffer dst;
1117 dst.buffer = wgpuBuffer;
1118 dst.layout.offset = bufferOffset;
1119 dst.layout.bytesPerRow = bufferRowBytes;
1120
1121 wgpu::Extent3D copySize = {
1122 static_cast<uint32_t>(srcRect.width()), static_cast<uint32_t>(srcRect.height()), 1};
1123 fCommandEncoder.CopyTextureToBuffer(&src, &dst, ©Size);
1124
1125 return true;
1126 }
1127
onCopyBufferToTexture(const Buffer * buffer,const Texture * texture,const BufferTextureCopyData * copyData,int count)1128 bool DawnCommandBuffer::onCopyBufferToTexture(const Buffer* buffer,
1129 const Texture* texture,
1130 const BufferTextureCopyData* copyData,
1131 int count) {
1132 SkASSERT(!fActiveRenderPassEncoder);
1133 SkASSERT(!fActiveComputePassEncoder);
1134
1135 auto& wgpuTexture = static_cast<const DawnTexture*>(texture)->dawnTexture();
1136 auto& wgpuBuffer = static_cast<const DawnBuffer*>(buffer)->dawnBuffer();
1137
1138 wgpu::ImageCopyBuffer src;
1139 src.buffer = wgpuBuffer;
1140
1141 wgpu::ImageCopyTexture dst;
1142 dst.texture = wgpuTexture;
1143
1144 for (int i = 0; i < count; ++i) {
1145 src.layout.offset = copyData[i].fBufferOffset;
1146 src.layout.bytesPerRow = copyData[i].fBufferRowBytes;
1147
1148 dst.origin.x = copyData[i].fRect.x();
1149 dst.origin.y = copyData[i].fRect.y();
1150 dst.mipLevel = copyData[i].fMipLevel;
1151
1152 wgpu::Extent3D copySize = {static_cast<uint32_t>(copyData[i].fRect.width()),
1153 static_cast<uint32_t>(copyData[i].fRect.height()),
1154 1};
1155 fCommandEncoder.CopyBufferToTexture(&src, &dst, ©Size);
1156 }
1157
1158 return true;
1159 }
1160
onCopyTextureToTexture(const Texture * src,SkIRect srcRect,const Texture * dst,SkIPoint dstPoint,int mipLevel)1161 bool DawnCommandBuffer::onCopyTextureToTexture(const Texture* src,
1162 SkIRect srcRect,
1163 const Texture* dst,
1164 SkIPoint dstPoint,
1165 int mipLevel) {
1166 SkASSERT(!fActiveRenderPassEncoder);
1167 SkASSERT(!fActiveComputePassEncoder);
1168
1169 auto& wgpuTextureSrc = static_cast<const DawnTexture*>(src)->dawnTexture();
1170 auto& wgpuTextureDst = static_cast<const DawnTexture*>(dst)->dawnTexture();
1171
1172 wgpu::ImageCopyTexture srcArgs;
1173 srcArgs.texture = wgpuTextureSrc;
1174 srcArgs.origin.x = srcRect.fLeft;
1175 srcArgs.origin.y = srcRect.fTop;
1176
1177 wgpu::ImageCopyTexture dstArgs;
1178 dstArgs.texture = wgpuTextureDst;
1179 dstArgs.origin.x = dstPoint.fX;
1180 dstArgs.origin.y = dstPoint.fY;
1181 dstArgs.mipLevel = mipLevel;
1182
1183 wgpu::Extent3D copySize = {
1184 static_cast<uint32_t>(srcRect.width()), static_cast<uint32_t>(srcRect.height()), 1};
1185
1186 fCommandEncoder.CopyTextureToTexture(&srcArgs, &dstArgs, ©Size);
1187
1188 return true;
1189 }
1190
onSynchronizeBufferToCpu(const Buffer * buffer,bool * outDidResultInWork)1191 bool DawnCommandBuffer::onSynchronizeBufferToCpu(const Buffer* buffer, bool* outDidResultInWork) {
1192 return true;
1193 }
1194
onClearBuffer(const Buffer * buffer,size_t offset,size_t size)1195 bool DawnCommandBuffer::onClearBuffer(const Buffer* buffer, size_t offset, size_t size) {
1196 SkASSERT(!fActiveRenderPassEncoder);
1197 SkASSERT(!fActiveComputePassEncoder);
1198
1199 auto& wgpuBuffer = static_cast<const DawnBuffer*>(buffer)->dawnBuffer();
1200 fCommandEncoder.ClearBuffer(wgpuBuffer, offset, size);
1201
1202 return true;
1203 }
1204
1205 } // namespace skgpu::graphite
1206