1 // Copyright 2019 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "tests/perf_tests/DawnPerfTest.h"
16
17 #include "common/Assert.h"
18 #include "common/Constants.h"
19 #include "common/Math.h"
20 #include "utils/ComboRenderPipelineDescriptor.h"
21 #include "utils/WGPUHelpers.h"
22
23 namespace {
24
25 constexpr unsigned int kNumDraws = 2000;
26
27 constexpr uint32_t kTextureSize = 64;
28 constexpr size_t kUniformSize = 3 * sizeof(float);
29
30 constexpr float kVertexData[12] = {
31 0.0f, 0.5f, 0.0f, 1.0f, -0.5f, -0.5f, 0.0f, 1.0f, 0.5f, -0.5f, 0.0f, 1.0f,
32 };
33
34 constexpr char kVertexShader[] = R"(
35 [[stage(vertex)]] fn main(
36 [[location(0)]] pos : vec4<f32>
37 ) -> [[builtin(position)]] vec4<f32> {
38 return pos;
39 })";
40
41 constexpr char kFragmentShaderA[] = R"(
42 [[block]] struct Uniforms {
43 color : vec3<f32>;
44 };
45 [[group(0), binding(0)]] var<uniform> uniforms : Uniforms;
46 [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
47 return vec4<f32>(uniforms.color * (1.0 / 5000.0), 1.0);
48 })";
49
50 constexpr char kFragmentShaderB[] = R"(
51 [[block]] struct Constants {
52 color : vec3<f32>;
53 };
54 [[block]] struct Uniforms {
55 color : vec3<f32>;
56 };
57 [[group(0), binding(0)]] var<uniform> constants : Constants;
58 [[group(1), binding(0)]] var<uniform> uniforms : Uniforms;
59
60 [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
61 return vec4<f32>((constants.color + uniforms.color) * (1.0 / 5000.0), 1.0);
62 })";
63
64 enum class Pipeline {
65 Static, // Keep the same pipeline for all draws.
66 Redundant, // Use the same pipeline, but redundantly set it.
67 Dynamic, // Change the pipeline between draws.
68 };
69
70 enum class UniformData {
71 Static, // Don't update per-draw uniform data.
72 Dynamic, // Update the per-draw uniform data once per frame.
73 };
74
75 enum class BindGroup {
76 NoChange, // Use one bind group for all draws.
77 Redundant, // Use the same bind group, but redundantly set it.
78 NoReuse, // Create a new bind group every time.
79 Multiple, // Use multiple static bind groups.
80 Dynamic, // Use bind groups with dynamic offsets.
81 };
82
83 enum class VertexBuffer {
84 NoChange, // Use one vertex buffer for all draws.
85 Multiple, // Use multiple static vertex buffers.
86 Dynamic, // Switch vertex buffers between draws.
87 };
88
89 enum class RenderBundle {
90 No, // Record commands in a render pass
91 Yes, // Record commands in a render bundle
92 };
93
94 struct DrawCallParam {
95 Pipeline pipelineType;
96 VertexBuffer vertexBufferType;
97 BindGroup bindGroupType;
98 UniformData uniformDataType;
99 RenderBundle withRenderBundle;
100 };
101
102 using DrawCallParamTuple =
103 std::tuple<Pipeline, VertexBuffer, BindGroup, UniformData, RenderBundle>;
104
105 template <typename T>
AssignParam(T & lhs,T rhs)106 unsigned int AssignParam(T& lhs, T rhs) {
107 lhs = rhs;
108 return 0u;
109 }
110
111 // This helper function allows creating a DrawCallParam from a list of arguments
112 // without specifying all of the members. Provided members can be passed once in an arbitrary
113 // order. Unspecified members default to:
114 // - Pipeline::Static
115 // - VertexBuffer::NoChange
116 // - BindGroup::NoChange
117 // - UniformData::Static
118 // - RenderBundle::No
119 template <typename... Ts>
MakeParam(Ts...args)120 DrawCallParam MakeParam(Ts... args) {
121 // Baseline param
122 DrawCallParamTuple paramTuple{Pipeline::Static, VertexBuffer::NoChange, BindGroup::NoChange,
123 UniformData::Static, RenderBundle::No};
124
125 unsigned int unused[] = {
126 0, // Avoid making a 0-sized array.
127 AssignParam(std::get<Ts>(paramTuple), args)...,
128 };
129 DAWN_UNUSED(unused);
130
131 return DrawCallParam{
132 std::get<Pipeline>(paramTuple), std::get<VertexBuffer>(paramTuple),
133 std::get<BindGroup>(paramTuple), std::get<UniformData>(paramTuple),
134 std::get<RenderBundle>(paramTuple),
135 };
136 }
137
138 struct DrawCallParamForTest : AdapterTestParam {
DrawCallParamForTest__anon069ae7a00111::DrawCallParamForTest139 DrawCallParamForTest(const AdapterTestParam& backendParam, DrawCallParam param)
140 : AdapterTestParam(backendParam), param(param) {
141 }
142 DrawCallParam param;
143 };
144
operator <<(std::ostream & ostream,const DrawCallParamForTest & testParams)145 std::ostream& operator<<(std::ostream& ostream, const DrawCallParamForTest& testParams) {
146 ostream << static_cast<const AdapterTestParam&>(testParams);
147
148 const DrawCallParam& param = testParams.param;
149
150 switch (param.pipelineType) {
151 case Pipeline::Static:
152 break;
153 case Pipeline::Redundant:
154 ostream << "_RedundantPipeline";
155 break;
156 case Pipeline::Dynamic:
157 ostream << "_DynamicPipeline";
158 break;
159 }
160
161 switch (param.vertexBufferType) {
162 case VertexBuffer::NoChange:
163 break;
164 case VertexBuffer::Multiple:
165 ostream << "_MultipleVertexBuffers";
166 break;
167 case VertexBuffer::Dynamic:
168 ostream << "_DynamicVertexBuffer";
169 }
170
171 switch (param.bindGroupType) {
172 case BindGroup::NoChange:
173 break;
174 case BindGroup::Redundant:
175 ostream << "_RedundantBindGroups";
176 break;
177 case BindGroup::NoReuse:
178 ostream << "_NoReuseBindGroups";
179 break;
180 case BindGroup::Multiple:
181 ostream << "_MultipleBindGroups";
182 break;
183 case BindGroup::Dynamic:
184 ostream << "_DynamicBindGroup";
185 break;
186 }
187
188 switch (param.uniformDataType) {
189 case UniformData::Static:
190 break;
191 case UniformData::Dynamic:
192 ostream << "_DynamicData";
193 break;
194 }
195
196 switch (param.withRenderBundle) {
197 case RenderBundle::No:
198 break;
199 case RenderBundle::Yes:
200 ostream << "_RenderBundle";
201 break;
202 }
203
204 return ostream;
205 }
206
207 } // anonymous namespace
208
209 // DrawCallPerf is an uber-benchmark with supports many parameterizations.
210 // The specific parameterizations we care about are explicitly instantiated at the bottom
211 // of this test file.
212 // DrawCallPerf tests drawing a simple triangle with many ways of encoding commands,
213 // binding, and uploading data to the GPU. The rationale for this is the following:
214 // - Static/Multiple/Dynamic vertex buffers: Tests switching buffer bindings. This has
215 // a state tracking cost as well as a GPU driver cost.
216 // - Static/Multiple/Dynamic bind groups: Same rationale as vertex buffers
217 // - Static/Dynamic pipelines: In addition to a change to GPU state, changing the pipeline
218 // layout incurs additional state tracking costs in Dawn.
219 // - With/Without render bundles: All of the above can have lower validation costs if
220 // precomputed in a render bundle.
221 // - Static/Dynamic data: Updating data for each draw is a common use case. It also tests
222 // the efficiency of resource transitions.
223 class DrawCallPerf : public DawnPerfTestWithParams<DrawCallParamForTest> {
224 public:
DrawCallPerf()225 DrawCallPerf() : DawnPerfTestWithParams(kNumDraws, 3) {
226 }
227 ~DrawCallPerf() override = default;
228
229 void SetUp() override;
230
231 protected:
GetParam() const232 DrawCallParam GetParam() const {
233 return DawnPerfTestWithParams::GetParam().param;
234 }
235
236 template <typename Encoder>
237 void RecordRenderCommands(Encoder encoder);
238
239 private:
240 void Step() override;
241
242 // One large dynamic vertex buffer, or multiple separate vertex buffers.
243 wgpu::Buffer mVertexBuffers[kNumDraws];
244 size_t mAlignedVertexDataSize;
245
246 std::vector<float> mUniformBufferData;
247 // One large dynamic uniform buffer, or multiple separate uniform buffers.
248 wgpu::Buffer mUniformBuffers[kNumDraws];
249
250 wgpu::BindGroupLayout mUniformBindGroupLayout;
251 // One dynamic bind group or multiple bind groups.
252 wgpu::BindGroup mUniformBindGroups[kNumDraws];
253 size_t mAlignedUniformSize;
254 size_t mNumUniformFloats;
255
256 wgpu::BindGroupLayout mConstantBindGroupLayout;
257 wgpu::BindGroup mConstantBindGroup;
258
259 // If the pipeline is static, only the first is used.
260 // Otherwise, the test alternates between two pipelines for each draw.
261 wgpu::RenderPipeline mPipelines[2];
262
263 wgpu::TextureView mColorAttachment;
264 wgpu::TextureView mDepthStencilAttachment;
265
266 wgpu::RenderBundle mRenderBundle;
267 };
268
SetUp()269 void DrawCallPerf::SetUp() {
270 DawnPerfTestWithParams::SetUp();
271
272 // Compute aligned uniform / vertex data sizes.
273 mAlignedUniformSize =
274 Align(kUniformSize, GetSupportedLimits().limits.minUniformBufferOffsetAlignment);
275 mAlignedVertexDataSize = Align(sizeof(kVertexData), 4);
276
277 // Initialize uniform buffer data.
278 mNumUniformFloats = mAlignedUniformSize / sizeof(float);
279 mUniformBufferData = std::vector<float>(kNumDraws * mNumUniformFloats, 0.0);
280
281 // Create the color / depth stencil attachments.
282 {
283 wgpu::TextureDescriptor descriptor = {};
284 descriptor.dimension = wgpu::TextureDimension::e2D;
285 descriptor.size.width = kTextureSize;
286 descriptor.size.height = kTextureSize;
287 descriptor.size.depthOrArrayLayers = 1;
288 descriptor.usage = wgpu::TextureUsage::RenderAttachment;
289
290 descriptor.format = wgpu::TextureFormat::RGBA8Unorm;
291 mColorAttachment = device.CreateTexture(&descriptor).CreateView();
292
293 descriptor.format = wgpu::TextureFormat::Depth24PlusStencil8;
294 mDepthStencilAttachment = device.CreateTexture(&descriptor).CreateView();
295 }
296
297 // Create vertex buffer(s)
298 switch (GetParam().vertexBufferType) {
299 case VertexBuffer::NoChange:
300 mVertexBuffers[0] = utils::CreateBufferFromData(
301 device, kVertexData, sizeof(kVertexData), wgpu::BufferUsage::Vertex);
302 break;
303
304 case VertexBuffer::Multiple: {
305 for (uint32_t i = 0; i < kNumDraws; ++i) {
306 mVertexBuffers[i] = utils::CreateBufferFromData(
307 device, kVertexData, sizeof(kVertexData), wgpu::BufferUsage::Vertex);
308 }
309 break;
310 }
311
312 case VertexBuffer::Dynamic: {
313 std::vector<char> data(mAlignedVertexDataSize * kNumDraws);
314 for (uint32_t i = 0; i < kNumDraws; ++i) {
315 memcpy(data.data() + mAlignedVertexDataSize * i, kVertexData, sizeof(kVertexData));
316 }
317
318 mVertexBuffers[0] = utils::CreateBufferFromData(device, data.data(), data.size(),
319 wgpu::BufferUsage::Vertex);
320 break;
321 }
322 }
323
324 // Create the bind group layout.
325 switch (GetParam().bindGroupType) {
326 case BindGroup::NoChange:
327 case BindGroup::Redundant:
328 case BindGroup::NoReuse:
329 case BindGroup::Multiple:
330 mUniformBindGroupLayout = utils::MakeBindGroupLayout(
331 device,
332 {
333 {0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform, false},
334 });
335 break;
336
337 case BindGroup::Dynamic:
338 mUniformBindGroupLayout = utils::MakeBindGroupLayout(
339 device,
340 {
341 {0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform, true},
342 });
343 break;
344
345 default:
346 UNREACHABLE();
347 break;
348 }
349
350 // Setup the base render pipeline descriptor.
351 utils::ComboRenderPipelineDescriptor renderPipelineDesc;
352 renderPipelineDesc.vertex.bufferCount = 1;
353 renderPipelineDesc.cBuffers[0].arrayStride = 4 * sizeof(float);
354 renderPipelineDesc.cBuffers[0].attributeCount = 1;
355 renderPipelineDesc.cAttributes[0].format = wgpu::VertexFormat::Float32x4;
356 renderPipelineDesc.EnableDepthStencil(wgpu::TextureFormat::Depth24PlusStencil8);
357 renderPipelineDesc.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm;
358
359 // Create the pipeline layout for the first pipeline.
360 wgpu::PipelineLayoutDescriptor pipelineLayoutDesc = {};
361 pipelineLayoutDesc.bindGroupLayouts = &mUniformBindGroupLayout;
362 pipelineLayoutDesc.bindGroupLayoutCount = 1;
363 wgpu::PipelineLayout pipelineLayout = device.CreatePipelineLayout(&pipelineLayoutDesc);
364
365 // Create the shaders for the first pipeline.
366 wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, kVertexShader);
367 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, kFragmentShaderA);
368
369 // Create the first pipeline.
370 renderPipelineDesc.layout = pipelineLayout;
371 renderPipelineDesc.vertex.module = vsModule;
372 renderPipelineDesc.cFragment.module = fsModule;
373 mPipelines[0] = device.CreateRenderPipeline(&renderPipelineDesc);
374
375 // If the test is using a dynamic pipeline, create the second pipeline.
376 if (GetParam().pipelineType == Pipeline::Dynamic) {
377 // Create another bind group layout. The data for this binding point will be the same for
378 // all draws.
379 mConstantBindGroupLayout = utils::MakeBindGroupLayout(
380 device, {
381 {0, wgpu::ShaderStage::Fragment, wgpu::BufferBindingType::Uniform, false},
382 });
383
384 // Create the pipeline layout.
385 wgpu::BindGroupLayout bindGroupLayouts[2] = {
386 mConstantBindGroupLayout,
387 mUniformBindGroupLayout,
388 };
389 pipelineLayoutDesc.bindGroupLayouts = bindGroupLayouts,
390 pipelineLayoutDesc.bindGroupLayoutCount = 2;
391 wgpu::PipelineLayout pipelineLayout = device.CreatePipelineLayout(&pipelineLayoutDesc);
392
393 // Create the fragment shader module. This shader matches the pipeline layout described
394 // above.
395 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, kFragmentShaderB);
396
397 // Create the pipeline.
398 renderPipelineDesc.layout = pipelineLayout;
399 renderPipelineDesc.cFragment.module = fsModule;
400 mPipelines[1] = device.CreateRenderPipeline(&renderPipelineDesc);
401
402 // Create the buffer and bind group to bind to the constant bind group layout slot.
403 constexpr float kConstantData[] = {0.01, 0.02, 0.03};
404 wgpu::Buffer constantBuffer = utils::CreateBufferFromData(
405 device, kConstantData, sizeof(kConstantData), wgpu::BufferUsage::Uniform);
406 mConstantBindGroup = utils::MakeBindGroup(device, mConstantBindGroupLayout,
407 {{0, constantBuffer, 0, sizeof(kConstantData)}});
408 }
409
410 // Create the buffers and bind groups for the per-draw uniform data.
411 switch (GetParam().bindGroupType) {
412 case BindGroup::NoChange:
413 case BindGroup::Redundant:
414 mUniformBuffers[0] = utils::CreateBufferFromData(
415 device, mUniformBufferData.data(), 3 * sizeof(float), wgpu::BufferUsage::Uniform);
416
417 mUniformBindGroups[0] = utils::MakeBindGroup(
418 device, mUniformBindGroupLayout, {{0, mUniformBuffers[0], 0, kUniformSize}});
419 break;
420
421 case BindGroup::NoReuse:
422 for (uint32_t i = 0; i < kNumDraws; ++i) {
423 mUniformBuffers[i] = utils::CreateBufferFromData(
424 device, mUniformBufferData.data() + i * mNumUniformFloats, 3 * sizeof(float),
425 wgpu::BufferUsage::Uniform);
426 }
427 // Bind groups are created on-the-fly.
428 break;
429
430 case BindGroup::Multiple:
431 for (uint32_t i = 0; i < kNumDraws; ++i) {
432 mUniformBuffers[i] = utils::CreateBufferFromData(
433 device, mUniformBufferData.data() + i * mNumUniformFloats, 3 * sizeof(float),
434 wgpu::BufferUsage::Uniform);
435
436 mUniformBindGroups[i] = utils::MakeBindGroup(
437 device, mUniformBindGroupLayout, {{0, mUniformBuffers[i], 0, kUniformSize}});
438 }
439 break;
440
441 case BindGroup::Dynamic:
442 mUniformBuffers[0] = utils::CreateBufferFromData(
443 device, mUniformBufferData.data(), mUniformBufferData.size() * sizeof(float),
444 wgpu::BufferUsage::Uniform);
445
446 mUniformBindGroups[0] = utils::MakeBindGroup(
447 device, mUniformBindGroupLayout, {{0, mUniformBuffers[0], 0, kUniformSize}});
448 break;
449 default:
450 UNREACHABLE();
451 break;
452 }
453
454 // If using render bundles, record the render commands now.
455 if (GetParam().withRenderBundle == RenderBundle::Yes) {
456 wgpu::RenderBundleEncoderDescriptor descriptor = {};
457 descriptor.colorFormatsCount = 1;
458 descriptor.colorFormats = &renderPipelineDesc.cTargets[0].format;
459 descriptor.depthStencilFormat = wgpu::TextureFormat::Depth24PlusStencil8;
460
461 wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&descriptor);
462 RecordRenderCommands(encoder);
463 mRenderBundle = encoder.Finish();
464 }
465 }
466
467 template <typename Encoder>
RecordRenderCommands(Encoder pass)468 void DrawCallPerf::RecordRenderCommands(Encoder pass) {
469 uint32_t uniformBindGroupIndex = 0;
470
471 if (GetParam().pipelineType == Pipeline::Static) {
472 // Static pipeline can be set now.
473 pass.SetPipeline(mPipelines[0]);
474 }
475
476 if (GetParam().vertexBufferType == VertexBuffer::NoChange) {
477 // Static vertex buffer can be set now.
478 pass.SetVertexBuffer(0, mVertexBuffers[0]);
479 }
480
481 if (GetParam().bindGroupType == BindGroup::NoChange) {
482 // Incompatible. Can't change pipeline without changing bind groups.
483 ASSERT(GetParam().pipelineType == Pipeline::Static);
484
485 // Static bind group can be set now.
486 pass.SetBindGroup(uniformBindGroupIndex, mUniformBindGroups[0]);
487 }
488
489 for (unsigned int i = 0; i < kNumDraws; ++i) {
490 switch (GetParam().pipelineType) {
491 case Pipeline::Static:
492 break;
493 case Pipeline::Redundant:
494 pass.SetPipeline(mPipelines[0]);
495 break;
496 case Pipeline::Dynamic: {
497 // If the pipeline is dynamic, ping pong between two pipelines.
498 pass.SetPipeline(mPipelines[i % 2]);
499
500 // The pipelines have different layouts so we change the binding index here.
501 uniformBindGroupIndex = i % 2;
502 if (uniformBindGroupIndex == 1) {
503 // Because of the pipeline layout change, we need to rebind bind group index 0.
504 pass.SetBindGroup(0, mConstantBindGroup);
505 }
506 break;
507 }
508 }
509
510 // Set the vertex buffer, if it changes.
511 switch (GetParam().vertexBufferType) {
512 case VertexBuffer::NoChange:
513 break;
514
515 case VertexBuffer::Multiple:
516 pass.SetVertexBuffer(0, mVertexBuffers[i]);
517 break;
518
519 case VertexBuffer::Dynamic:
520 pass.SetVertexBuffer(0, mVertexBuffers[0], i * mAlignedVertexDataSize);
521 break;
522 }
523
524 // Set the bind group, if it changes.
525 switch (GetParam().bindGroupType) {
526 case BindGroup::NoChange:
527 break;
528
529 case BindGroup::Redundant:
530 pass.SetBindGroup(uniformBindGroupIndex, mUniformBindGroups[0]);
531 break;
532
533 case BindGroup::NoReuse: {
534 wgpu::BindGroup bindGroup = utils::MakeBindGroup(
535 device, mUniformBindGroupLayout, {{0, mUniformBuffers[i], 0, kUniformSize}});
536 pass.SetBindGroup(uniformBindGroupIndex, bindGroup);
537 break;
538 }
539
540 case BindGroup::Multiple:
541 pass.SetBindGroup(uniformBindGroupIndex, mUniformBindGroups[i]);
542 break;
543
544 case BindGroup::Dynamic: {
545 uint32_t dynamicOffset = static_cast<uint32_t>(i * mAlignedUniformSize);
546 pass.SetBindGroup(uniformBindGroupIndex, mUniformBindGroups[0], 1, &dynamicOffset);
547 break;
548 }
549
550 default:
551 UNREACHABLE();
552 break;
553 }
554 pass.Draw(3);
555 }
556 }
557
Step()558 void DrawCallPerf::Step() {
559 if (GetParam().uniformDataType == UniformData::Dynamic) {
560 // Update uniform data if it's dynamic.
561 std::fill(mUniformBufferData.begin(), mUniformBufferData.end(),
562 mUniformBufferData[0] + 1.0);
563
564 switch (GetParam().bindGroupType) {
565 case BindGroup::NoChange:
566 case BindGroup::Redundant:
567 queue.WriteBuffer(mUniformBuffers[0], 0, mUniformBufferData.data(),
568 3 * sizeof(float));
569 break;
570 case BindGroup::NoReuse:
571 case BindGroup::Multiple:
572 for (uint32_t i = 0; i < kNumDraws; ++i) {
573 queue.WriteBuffer(mUniformBuffers[i], 0,
574 mUniformBufferData.data() + i * mNumUniformFloats,
575 3 * sizeof(float));
576 }
577 break;
578 case BindGroup::Dynamic:
579 queue.WriteBuffer(mUniformBuffers[0], 0, mUniformBufferData.data(),
580 mUniformBufferData.size() * sizeof(float));
581 break;
582 }
583 }
584
585 wgpu::CommandEncoder commands = device.CreateCommandEncoder();
586 utils::ComboRenderPassDescriptor renderPass({mColorAttachment}, mDepthStencilAttachment);
587 wgpu::RenderPassEncoder pass = commands.BeginRenderPass(&renderPass);
588
589 switch (GetParam().withRenderBundle) {
590 case RenderBundle::No:
591 RecordRenderCommands(pass);
592 break;
593 case RenderBundle::Yes:
594 pass.ExecuteBundles(1, &mRenderBundle);
595 break;
596 default:
597 UNREACHABLE();
598 break;
599 }
600
601 pass.EndPass();
602 wgpu::CommandBuffer commandBuffer = commands.Finish();
603 queue.Submit(1, &commandBuffer);
604 }
605
TEST_P(DrawCallPerf,Run)606 TEST_P(DrawCallPerf, Run) {
607 RunTest();
608 }
609
610 DAWN_INSTANTIATE_TEST_P(
611 DrawCallPerf,
612 {D3D12Backend(), MetalBackend(), OpenGLBackend(), VulkanBackend(),
613 VulkanBackend({"skip_validation"})},
614 {
615 // Baseline
616 MakeParam(),
617
618 // Change vertex buffer binding
619 MakeParam(VertexBuffer::Multiple), // Multiple vertex buffers
620 MakeParam(VertexBuffer::Dynamic), // Dynamic vertex buffer
621
622 // Change bind group binding
623 MakeParam(BindGroup::Multiple), // Multiple bind groups
624 MakeParam(BindGroup::Dynamic), // Dynamic bind groups
625 MakeParam(BindGroup::NoReuse), // New bind group per-draw
626
627 // Redundantly set pipeline / bind groups
628 MakeParam(Pipeline::Redundant, BindGroup::Redundant),
629
630 // Switch the pipeline every draw to test state tracking and updates to binding points
631 MakeParam(Pipeline::Dynamic,
632 BindGroup::Multiple), // Multiple bind groups w/ dynamic pipeline
633 MakeParam(Pipeline::Dynamic,
634 BindGroup::Dynamic), // Dynamic bind groups w/ dynamic pipeline
635
636 // ----------- Render Bundles -----------
637 // Command validation / state tracking can be futher optimized / precomputed.
638 // Use render bundles with varying vertex buffer binding
639 MakeParam(VertexBuffer::Multiple,
640 RenderBundle::Yes), // Multiple vertex buffers w/ render bundle
641 MakeParam(VertexBuffer::Dynamic,
642 RenderBundle::Yes), // Dynamic vertex buffer w/ render bundle
643
644 // Use render bundles with varying bind group binding
645 MakeParam(BindGroup::Multiple, RenderBundle::Yes), // Multiple bind groups w/ render bundle
646 MakeParam(BindGroup::Dynamic, RenderBundle::Yes), // Dynamic bind groups w/ render bundle
647
648 // Use render bundles with dynamic pipeline
649 MakeParam(Pipeline::Dynamic,
650 BindGroup::Multiple,
651 RenderBundle::Yes), // Multiple bind groups w/ dynamic pipeline w/ render bundle
652 MakeParam(Pipeline::Dynamic,
653 BindGroup::Dynamic,
654 RenderBundle::Yes), // Dynamic bind groups w/ dynamic pipeline w/ render bundle
655
656 // ----------- Render Bundles (end)-------
657
658 // Update per-draw data in the bind group(s). This will cause resource transitions between
659 // updating and drawing.
660 MakeParam(BindGroup::Multiple,
661 UniformData::Dynamic), // Update per-draw data: Multiple bind groups
662 MakeParam(BindGroup::Dynamic,
663 UniformData::Dynamic), // Update per-draw data: Dynamic bind groups
664 });
665