xref: /aosp_15_r20/external/skia/src/gpu/graphite/dawn/DawnCommandBuffer.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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, &copySize);
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, &copySize);
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, &copySize);
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