• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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