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