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, ©Size);
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, ©Size);
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, ©Size);
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