• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 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/DawnTest.h"
16 
17 #include "utils/ComboRenderBundleEncoderDescriptor.h"
18 #include "utils/ComboRenderPipelineDescriptor.h"
19 #include "utils/WGPUHelpers.h"
20 
21 constexpr uint32_t kRTSize = 4;
22 
23 class DrawIndexedIndirectTest : public DawnTest {
24   protected:
SetUp()25     void SetUp() override {
26         DawnTest::SetUp();
27 
28         renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
29 
30         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
31             [[stage(vertex)]]
32             fn main([[location(0)]] pos : vec4<f32>) -> [[builtin(position)]] vec4<f32> {
33                 return pos;
34             })");
35 
36         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
37             [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
38                 return vec4<f32>(0.0, 1.0, 0.0, 1.0);
39             })");
40 
41         utils::ComboRenderPipelineDescriptor descriptor;
42         descriptor.vertex.module = vsModule;
43         descriptor.cFragment.module = fsModule;
44         descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleStrip;
45         descriptor.primitive.stripIndexFormat = wgpu::IndexFormat::Uint32;
46         descriptor.vertex.bufferCount = 1;
47         descriptor.cBuffers[0].arrayStride = 4 * sizeof(float);
48         descriptor.cBuffers[0].attributeCount = 1;
49         descriptor.cAttributes[0].format = wgpu::VertexFormat::Float32x4;
50         descriptor.cTargets[0].format = renderPass.colorFormat;
51 
52         pipeline = device.CreateRenderPipeline(&descriptor);
53 
54         vertexBuffer = utils::CreateBufferFromData<float>(
55             device, wgpu::BufferUsage::Vertex,
56             {// First quad: the first 3 vertices represent the bottom left triangle
57              -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f,
58              0.0f, 1.0f,
59 
60              // Second quad: the first 3 vertices represent the top right triangle
61              -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f,
62              0.0f, 1.0f});
63     }
64 
65     utils::BasicRenderPass renderPass;
66     wgpu::RenderPipeline pipeline;
67     wgpu::Buffer vertexBuffer;
68 
CreateIndirectBuffer(std::initializer_list<uint32_t> indirectParamList)69     wgpu::Buffer CreateIndirectBuffer(std::initializer_list<uint32_t> indirectParamList) {
70         return utils::CreateBufferFromData<uint32_t>(
71             device, wgpu::BufferUsage::Indirect | wgpu::BufferUsage::Storage, indirectParamList);
72     }
73 
CreateIndexBuffer(std::initializer_list<uint32_t> indexList)74     wgpu::Buffer CreateIndexBuffer(std::initializer_list<uint32_t> indexList) {
75         return utils::CreateBufferFromData<uint32_t>(device, wgpu::BufferUsage::Index, indexList);
76     }
77 
EncodeDrawCommands(std::initializer_list<uint32_t> bufferList,wgpu::Buffer indexBuffer,uint64_t indexOffset,uint64_t indirectOffset)78     wgpu::CommandBuffer EncodeDrawCommands(std::initializer_list<uint32_t> bufferList,
79                                            wgpu::Buffer indexBuffer,
80                                            uint64_t indexOffset,
81                                            uint64_t indirectOffset) {
82         wgpu::Buffer indirectBuffer = CreateIndirectBuffer(bufferList);
83 
84         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
85         {
86             wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
87             pass.SetPipeline(pipeline);
88             pass.SetVertexBuffer(0, vertexBuffer);
89             pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32, indexOffset);
90             pass.DrawIndexedIndirect(indirectBuffer, indirectOffset);
91             pass.EndPass();
92         }
93 
94         return encoder.Finish();
95     }
96 
TestDraw(wgpu::CommandBuffer commands,RGBA8 bottomLeftExpected,RGBA8 topRightExpected)97     void TestDraw(wgpu::CommandBuffer commands, RGBA8 bottomLeftExpected, RGBA8 topRightExpected) {
98         queue.Submit(1, &commands);
99 
100         EXPECT_PIXEL_RGBA8_EQ(bottomLeftExpected, renderPass.color, 1, 3);
101         EXPECT_PIXEL_RGBA8_EQ(topRightExpected, renderPass.color, 3, 1);
102     }
103 
Test(std::initializer_list<uint32_t> bufferList,uint64_t indexOffset,uint64_t indirectOffset,RGBA8 bottomLeftExpected,RGBA8 topRightExpected)104     void Test(std::initializer_list<uint32_t> bufferList,
105               uint64_t indexOffset,
106               uint64_t indirectOffset,
107               RGBA8 bottomLeftExpected,
108               RGBA8 topRightExpected) {
109         wgpu::Buffer indexBuffer =
110             CreateIndexBuffer({0, 1, 2, 0, 3, 1,
111                                // The indices below are added to test negatve baseVertex
112                                0 + 4, 1 + 4, 2 + 4, 0 + 4, 3 + 4, 1 + 4});
113         TestDraw(EncodeDrawCommands(bufferList, indexBuffer, indexOffset, indirectOffset),
114                  bottomLeftExpected, topRightExpected);
115     }
116 };
117 
118 // The most basic DrawIndexed triangle draw.
TEST_P(DrawIndexedIndirectTest,Uint32)119 TEST_P(DrawIndexedIndirectTest, Uint32) {
120     // TODO(crbug.com/dawn/789): Test is failing after a roll on SwANGLE on Windows only.
121     DAWN_SUPPRESS_TEST_IF(IsANGLE() && IsWindows());
122 
123     RGBA8 filled(0, 255, 0, 255);
124     RGBA8 notFilled(0, 0, 0, 0);
125 
126     // Test a draw with no indices.
127     Test({0, 0, 0, 0, 0}, 0, 0, notFilled, notFilled);
128 
129     // Test a draw with only the first 3 indices of the first quad (bottom left triangle)
130     Test({3, 1, 0, 0, 0}, 0, 0, filled, notFilled);
131 
132     // Test a draw with only the last 3 indices of the first quad (top right triangle)
133     Test({3, 1, 3, 0, 0}, 0, 0, notFilled, filled);
134 
135     // Test a draw with all 6 indices (both triangles).
136     Test({6, 1, 0, 0, 0}, 0, 0, filled, filled);
137 }
138 
139 // Test the parameter 'baseVertex' of DrawIndexed() works.
TEST_P(DrawIndexedIndirectTest,BaseVertex)140 TEST_P(DrawIndexedIndirectTest, BaseVertex) {
141     // TODO(crbug.com/dawn/161): add workaround for OpenGL index buffer offset (could be compute
142     // shader that adds it to the draw calls)
143     DAWN_TEST_UNSUPPORTED_IF(IsOpenGL());
144     DAWN_TEST_UNSUPPORTED_IF(IsOpenGLES());
145 
146     // TODO(crbug.com/dawn/966): Fails on Metal Intel, likely because [[builtin(vertex_index)]]
147     // doesn't take into account BaseVertex, which breaks programmable vertex pulling.
148     DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel());
149 
150     RGBA8 filled(0, 255, 0, 255);
151     RGBA8 notFilled(0, 0, 0, 0);
152 
153     // Test a draw with only the first 3 indices of the second quad (top right triangle)
154     Test({3, 1, 0, 4, 0}, 0, 0, notFilled, filled);
155 
156     // Test a draw with only the last 3 indices of the second quad (bottom left triangle)
157     Test({3, 1, 3, 4, 0}, 0, 0, filled, notFilled);
158 
159     const int negFour = -4;
160     uint32_t unsignedNegFour;
161     std::memcpy(&unsignedNegFour, &negFour, sizeof(int));
162 
163     // Test negative baseVertex
164     // Test a draw with only the first 3 indices of the first quad (bottom left triangle)
165     Test({3, 1, 0, unsignedNegFour, 0}, 6 * sizeof(uint32_t), 0, filled, notFilled);
166 
167     // Test a draw with only the last 3 indices of the first quad (top right triangle)
168     Test({3, 1, 3, unsignedNegFour, 0}, 6 * sizeof(uint32_t), 0, notFilled, filled);
169 }
170 
TEST_P(DrawIndexedIndirectTest,IndirectOffset)171 TEST_P(DrawIndexedIndirectTest, IndirectOffset) {
172     // TODO(crbug.com/dawn/789): Test is failing after a roll on SwANGLE on Windows only.
173     DAWN_SUPPRESS_TEST_IF(IsANGLE() && IsWindows());
174 
175     // TODO(crbug.com/dawn/966): Fails on Metal Intel, likely because [[builtin(vertex_index)]]
176     // doesn't take into account BaseVertex, which breaks programmable vertex pulling.
177     DAWN_SUPPRESS_TEST_IF(IsMetal() && IsIntel());
178 
179     RGBA8 filled(0, 255, 0, 255);
180     RGBA8 notFilled(0, 0, 0, 0);
181 
182     // Test an offset draw call, with indirect buffer containing 2 calls:
183     // 1) first 3 indices of the second quad (top right triangle)
184     // 2) last 3 indices of the second quad
185 
186     // Test #1 (no offset)
187     Test({3, 1, 0, 4, 0, 3, 1, 3, 4, 0}, 0, 0, notFilled, filled);
188 
189     // Offset to draw #2
190     Test({3, 1, 0, 4, 0, 3, 1, 3, 4, 0}, 0, 5 * sizeof(uint32_t), filled, notFilled);
191 }
192 
TEST_P(DrawIndexedIndirectTest,BasicValidation)193 TEST_P(DrawIndexedIndirectTest, BasicValidation) {
194     // TODO(crbug.com/dawn/789): Test is failing under SwANGLE on Windows only.
195     DAWN_SUPPRESS_TEST_IF(IsANGLE() && IsWindows());
196 
197     // It doesn't make sense to test invalid inputs when validation is disabled.
198     DAWN_SUPPRESS_TEST_IF(HasToggleEnabled("skip_validation"));
199 
200     RGBA8 filled(0, 255, 0, 255);
201     RGBA8 notFilled(0, 0, 0, 0);
202 
203     wgpu::Buffer indexBuffer = CreateIndexBuffer({0, 1, 2, 0, 3, 1});
204 
205     // Test a draw with an excessive indexCount. Should draw nothing.
206     TestDraw(EncodeDrawCommands({7, 1, 0, 0, 0}, indexBuffer, 0, 0), notFilled, notFilled);
207 
208     // Test a draw with an excessive firstIndex. Should draw nothing.
209     TestDraw(EncodeDrawCommands({3, 1, 7, 0, 0}, indexBuffer, 0, 0), notFilled, notFilled);
210 
211     // Test a valid draw. Should draw only the second triangle.
212     TestDraw(EncodeDrawCommands({3, 1, 3, 0, 0}, indexBuffer, 0, 0), notFilled, filled);
213 }
214 
TEST_P(DrawIndexedIndirectTest,ValidateWithOffsets)215 TEST_P(DrawIndexedIndirectTest, ValidateWithOffsets) {
216     // TODO(crbug.com/dawn/161): The GL/GLES backend doesn't support indirect index buffer offsets
217     // yet.
218     DAWN_SUPPRESS_TEST_IF(IsOpenGL() || IsOpenGLES());
219 
220     // It doesn't make sense to test invalid inputs when validation is disabled.
221     DAWN_SUPPRESS_TEST_IF(HasToggleEnabled("skip_validation"));
222 
223     RGBA8 filled(0, 255, 0, 255);
224     RGBA8 notFilled(0, 0, 0, 0);
225 
226     wgpu::Buffer indexBuffer = CreateIndexBuffer({0, 1, 2, 0, 3, 1, 0, 1, 2});
227 
228     // Test that validation properly accounts for index buffer offset.
229     TestDraw(EncodeDrawCommands({3, 1, 0, 0, 0}, indexBuffer, 6 * sizeof(uint32_t), 0), filled,
230              notFilled);
231     TestDraw(EncodeDrawCommands({4, 1, 0, 0, 0}, indexBuffer, 6 * sizeof(uint32_t), 0), notFilled,
232              notFilled);
233     TestDraw(EncodeDrawCommands({3, 1, 4, 0, 0}, indexBuffer, 3 * sizeof(uint32_t), 0), notFilled,
234              notFilled);
235 
236     // Test that validation properly accounts for indirect buffer offset.
237     TestDraw(
238         EncodeDrawCommands({3, 1, 0, 0, 0, 1000, 1, 0, 0, 0}, indexBuffer, 0, 4 * sizeof(uint32_t)),
239         notFilled, notFilled);
240     TestDraw(EncodeDrawCommands({3, 1, 0, 0, 0, 1000, 1, 0, 0, 0}, indexBuffer, 0, 0), filled,
241              notFilled);
242 }
243 
TEST_P(DrawIndexedIndirectTest,ValidateMultiplePasses)244 TEST_P(DrawIndexedIndirectTest, ValidateMultiplePasses) {
245     // TODO(crbug.com/dawn/789): Test is failing under SwANGLE on Windows only.
246     DAWN_SUPPRESS_TEST_IF(IsANGLE() && IsWindows());
247 
248     // It doesn't make sense to test invalid inputs when validation is disabled.
249     DAWN_SUPPRESS_TEST_IF(HasToggleEnabled("skip_validation"));
250 
251     RGBA8 filled(0, 255, 0, 255);
252     RGBA8 notFilled(0, 0, 0, 0);
253 
254     wgpu::Buffer indexBuffer = CreateIndexBuffer({0, 1, 2, 0, 3, 1, 0, 1, 2});
255 
256     // Test validation with multiple passes in a row. Namely this is exercising that scratch buffer
257     // data for use with a previous pass's validation commands is not overwritten before it can be
258     // used.
259     TestDraw(EncodeDrawCommands({10, 1, 0, 0, 0}, indexBuffer, 0, 0), notFilled, notFilled);
260     TestDraw(EncodeDrawCommands({6, 1, 0, 0, 0}, indexBuffer, 0, 0), filled, filled);
261     TestDraw(EncodeDrawCommands({4, 1, 6, 0, 0}, indexBuffer, 0, 0), notFilled, notFilled);
262     TestDraw(EncodeDrawCommands({3, 1, 6, 0, 0}, indexBuffer, 0, 0), filled, notFilled);
263     TestDraw(EncodeDrawCommands({3, 1, 3, 0, 0}, indexBuffer, 0, 0), notFilled, filled);
264     TestDraw(EncodeDrawCommands({6, 1, 3, 0, 0}, indexBuffer, 0, 0), filled, filled);
265     TestDraw(EncodeDrawCommands({6, 1, 6, 0, 0}, indexBuffer, 0, 0), notFilled, notFilled);
266 }
267 
TEST_P(DrawIndexedIndirectTest,ValidateMultipleDraws)268 TEST_P(DrawIndexedIndirectTest, ValidateMultipleDraws) {
269     // TODO(crbug.com/dawn/789): Test is failing under SwANGLE on Windows only.
270     DAWN_SUPPRESS_TEST_IF(IsANGLE() && IsWindows());
271 
272     // It doesn't make sense to test invalid inputs when validation is disabled.
273     DAWN_SUPPRESS_TEST_IF(HasToggleEnabled("skip_validation"));
274 
275     RGBA8 filled(0, 255, 0, 255);
276     RGBA8 notFilled(0, 0, 0, 0);
277 
278     // Validate multiple draw calls using the same index and indirect buffers as input, but with
279     // different indirect offsets.
280     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
281     {
282         wgpu::Buffer indirectBuffer =
283             CreateIndirectBuffer({3, 1, 3, 0, 0, 10, 1, 0, 0, 0, 3, 1, 6, 0, 0});
284         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
285         pass.SetPipeline(pipeline);
286         pass.SetVertexBuffer(0, vertexBuffer);
287         pass.SetIndexBuffer(CreateIndexBuffer({0, 1, 2, 0, 3, 1}), wgpu::IndexFormat::Uint32, 0);
288         pass.DrawIndexedIndirect(indirectBuffer, 0);
289         pass.DrawIndexedIndirect(indirectBuffer, 20);
290         pass.DrawIndexedIndirect(indirectBuffer, 40);
291         pass.EndPass();
292     }
293 
294     wgpu::CommandBuffer commands = encoder.Finish();
295 
296     queue.Submit(1, &commands);
297     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, 1, 3);
298     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 3, 1);
299 
300     // Validate multiple draw calls using the same indirect buffer but different index buffers as
301     // input.
302     encoder = device.CreateCommandEncoder();
303     {
304         wgpu::Buffer indirectBuffer =
305             CreateIndirectBuffer({3, 1, 3, 0, 0, 10, 1, 0, 0, 0, 3, 1, 6, 0, 0});
306         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
307         pass.SetPipeline(pipeline);
308         pass.SetVertexBuffer(0, vertexBuffer);
309         pass.SetIndexBuffer(CreateIndexBuffer({0, 1, 2, 0, 3, 1}), wgpu::IndexFormat::Uint32, 0);
310         pass.DrawIndexedIndirect(indirectBuffer, 0);
311         pass.SetIndexBuffer(CreateIndexBuffer({0, 3, 1, 0, 2, 1}), wgpu::IndexFormat::Uint32, 0);
312         pass.DrawIndexedIndirect(indirectBuffer, 20);
313         pass.SetIndexBuffer(CreateIndexBuffer({0, 1, 2, 0, 3, 1, 0, 2, 1}),
314                             wgpu::IndexFormat::Uint32, 0);
315         pass.DrawIndexedIndirect(indirectBuffer, 40);
316         pass.EndPass();
317     }
318     commands = encoder.Finish();
319 
320     queue.Submit(1, &commands);
321     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3);
322     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 3, 1);
323 
324     // Validate multiple draw calls using the same index buffer but different indirect buffers as
325     // input.
326     encoder = device.CreateCommandEncoder();
327     {
328         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
329         pass.SetPipeline(pipeline);
330         pass.SetVertexBuffer(0, vertexBuffer);
331         pass.SetIndexBuffer(CreateIndexBuffer({0, 1, 2, 0, 3, 1}), wgpu::IndexFormat::Uint32, 0);
332         pass.DrawIndexedIndirect(CreateIndirectBuffer({3, 1, 3, 0, 0}), 0);
333         pass.DrawIndexedIndirect(CreateIndirectBuffer({10, 1, 0, 0, 0}), 0);
334         pass.DrawIndexedIndirect(CreateIndirectBuffer({3, 1, 6, 0, 0}), 0);
335         pass.EndPass();
336     }
337     commands = encoder.Finish();
338 
339     queue.Submit(1, &commands);
340     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, 1, 3);
341     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 3, 1);
342 
343     // Validate multiple draw calls across different index and indirect buffers.
344     encoder = device.CreateCommandEncoder();
345     {
346         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
347         pass.SetPipeline(pipeline);
348         pass.SetVertexBuffer(0, vertexBuffer);
349         pass.SetIndexBuffer(CreateIndexBuffer({0, 1, 2, 0, 3, 1}), wgpu::IndexFormat::Uint32, 0);
350         pass.DrawIndexedIndirect(CreateIndirectBuffer({3, 1, 3, 0, 0}), 0);
351         pass.SetIndexBuffer(CreateIndexBuffer({0, 1, 2, 0, 3, 1}), wgpu::IndexFormat::Uint32, 0);
352         pass.DrawIndexedIndirect(CreateIndirectBuffer({10, 1, 0, 0, 0}), 0);
353         pass.SetIndexBuffer(CreateIndexBuffer({0, 3, 1}), wgpu::IndexFormat::Uint32, 0);
354         pass.DrawIndexedIndirect(CreateIndirectBuffer({3, 1, 3, 0, 0}), 0);
355         pass.EndPass();
356     }
357     commands = encoder.Finish();
358 
359     queue.Submit(1, &commands);
360     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, 1, 3);
361     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 3, 1);
362 }
363 
TEST_P(DrawIndexedIndirectTest,ValidateEncodeMultipleThenSubmitInOrder)364 TEST_P(DrawIndexedIndirectTest, ValidateEncodeMultipleThenSubmitInOrder) {
365     // TODO(crbug.com/dawn/789): Test is failing under SwANGLE on Windows only.
366     DAWN_SUPPRESS_TEST_IF(IsANGLE() && IsWindows());
367 
368     // It doesn't make sense to test invalid inputs when validation is disabled.
369     DAWN_SUPPRESS_TEST_IF(HasToggleEnabled("skip_validation"));
370 
371     RGBA8 filled(0, 255, 0, 255);
372     RGBA8 notFilled(0, 0, 0, 0);
373 
374     wgpu::Buffer indexBuffer = CreateIndexBuffer({0, 1, 2, 0, 3, 1, 0, 1, 2});
375 
376     wgpu::CommandBuffer commands[7];
377     commands[0] = EncodeDrawCommands({10, 1, 0, 0, 0}, indexBuffer, 0, 0);
378     commands[1] = EncodeDrawCommands({6, 1, 0, 0, 0}, indexBuffer, 0, 0);
379     commands[2] = EncodeDrawCommands({4, 1, 6, 0, 0}, indexBuffer, 0, 0);
380     commands[3] = EncodeDrawCommands({3, 1, 6, 0, 0}, indexBuffer, 0, 0);
381     commands[4] = EncodeDrawCommands({3, 1, 3, 0, 0}, indexBuffer, 0, 0);
382     commands[5] = EncodeDrawCommands({6, 1, 3, 0, 0}, indexBuffer, 0, 0);
383     commands[6] = EncodeDrawCommands({6, 1, 6, 0, 0}, indexBuffer, 0, 0);
384 
385     TestDraw(commands[0], notFilled, notFilled);
386     TestDraw(commands[1], filled, filled);
387     TestDraw(commands[2], notFilled, notFilled);
388     TestDraw(commands[3], filled, notFilled);
389     TestDraw(commands[4], notFilled, filled);
390     TestDraw(commands[5], filled, filled);
391     TestDraw(commands[6], notFilled, notFilled);
392 }
393 
TEST_P(DrawIndexedIndirectTest,ValidateEncodeMultipleThenSubmitAtOnce)394 TEST_P(DrawIndexedIndirectTest, ValidateEncodeMultipleThenSubmitAtOnce) {
395     // TODO(crbug.com/dawn/789): Test is failing under SwANGLE on Windows.
396     DAWN_SUPPRESS_TEST_IF(IsANGLE() && IsWindows());
397 
398     // TODO(crbug.com/dawn/1124): Fails on Intel+Vulkan+Windows for drivers
399     // older than 27.20.100.8587, which bots are actively using.
400     DAWN_SUPPRESS_TEST_IF(IsIntel() && IsVulkan() && IsWindows());
401 
402     // It doesn't make sense to test invalid inputs when validation is disabled.
403     DAWN_SUPPRESS_TEST_IF(HasToggleEnabled("skip_validation"));
404 
405     RGBA8 filled(0, 255, 0, 255);
406     RGBA8 notFilled(0, 0, 0, 0);
407 
408     wgpu::Buffer indexBuffer = CreateIndexBuffer({0, 1, 2, 0, 3, 1, 0, 1, 2});
409 
410     wgpu::CommandBuffer commands[5];
411     commands[0] = EncodeDrawCommands({10, 1, 0, 0, 0}, indexBuffer, 0, 0);
412     commands[1] = EncodeDrawCommands({6, 1, 0, 0, 0}, indexBuffer, 0, 0);
413     commands[2] = EncodeDrawCommands({4, 1, 6, 0, 0}, indexBuffer, 0, 0);
414     commands[3] = EncodeDrawCommands({3, 1, 6, 0, 0}, indexBuffer, 0, 0);
415     commands[4] = EncodeDrawCommands({3, 1, 3, 0, 0}, indexBuffer, 0, 0);
416 
417     queue.Submit(5, commands);
418     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, 1, 3);
419     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 3, 1);
420 }
421 
TEST_P(DrawIndexedIndirectTest,ValidateEncodeMultipleThenSubmitOutOfOrder)422 TEST_P(DrawIndexedIndirectTest, ValidateEncodeMultipleThenSubmitOutOfOrder) {
423     // TODO(crbug.com/dawn/789): Test is failing under SwANGLE on Windows only.
424     DAWN_SUPPRESS_TEST_IF(IsANGLE() && IsWindows());
425 
426     // It doesn't make sense to test invalid inputs when validation is disabled.
427     DAWN_SUPPRESS_TEST_IF(HasToggleEnabled("skip_validation"));
428 
429     RGBA8 filled(0, 255, 0, 255);
430     RGBA8 notFilled(0, 0, 0, 0);
431 
432     wgpu::Buffer indexBuffer = CreateIndexBuffer({0, 1, 2, 0, 3, 1, 0, 1, 2});
433 
434     wgpu::CommandBuffer commands[7];
435     commands[0] = EncodeDrawCommands({10, 1, 0, 0, 0}, indexBuffer, 0, 0);
436     commands[1] = EncodeDrawCommands({6, 1, 0, 0, 0}, indexBuffer, 0, 0);
437     commands[2] = EncodeDrawCommands({4, 1, 6, 0, 0}, indexBuffer, 0, 0);
438     commands[3] = EncodeDrawCommands({3, 1, 6, 0, 0}, indexBuffer, 0, 0);
439     commands[4] = EncodeDrawCommands({3, 1, 3, 0, 0}, indexBuffer, 0, 0);
440     commands[5] = EncodeDrawCommands({6, 1, 3, 0, 0}, indexBuffer, 0, 0);
441     commands[6] = EncodeDrawCommands({6, 1, 6, 0, 0}, indexBuffer, 0, 0);
442 
443     TestDraw(commands[6], notFilled, notFilled);
444     TestDraw(commands[5], filled, filled);
445     TestDraw(commands[4], notFilled, filled);
446     TestDraw(commands[3], filled, notFilled);
447     TestDraw(commands[2], notFilled, notFilled);
448     TestDraw(commands[1], filled, filled);
449     TestDraw(commands[0], notFilled, notFilled);
450 }
451 
TEST_P(DrawIndexedIndirectTest,ValidateWithBundlesInSamePass)452 TEST_P(DrawIndexedIndirectTest, ValidateWithBundlesInSamePass) {
453     // TODO(crbug.com/dawn/789): Test is failing under SwANGLE on Windows only.
454     DAWN_SUPPRESS_TEST_IF(IsANGLE() && IsWindows());
455 
456     // It doesn't make sense to test invalid inputs when validation is disabled.
457     DAWN_SUPPRESS_TEST_IF(HasToggleEnabled("skip_validation"));
458 
459     RGBA8 filled(0, 255, 0, 255);
460     RGBA8 notFilled(0, 0, 0, 0);
461 
462     wgpu::Buffer indirectBuffer =
463         CreateIndirectBuffer({3, 1, 3, 0, 0, 10, 1, 0, 0, 0, 3, 1, 6, 0, 0});
464     wgpu::Buffer indexBuffer = CreateIndexBuffer({0, 1, 2, 0, 3, 1, 0, 1, 2});
465 
466     std::vector<wgpu::RenderBundle> bundles;
467     {
468         utils::ComboRenderBundleEncoderDescriptor desc = {};
469         desc.colorFormatsCount = 1;
470         desc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm;
471         wgpu::RenderBundleEncoder bundleEncoder = device.CreateRenderBundleEncoder(&desc);
472         bundleEncoder.SetPipeline(pipeline);
473         bundleEncoder.SetVertexBuffer(0, vertexBuffer);
474         bundleEncoder.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32, 0);
475         bundleEncoder.DrawIndexedIndirect(indirectBuffer, 20);
476         bundles.push_back(bundleEncoder.Finish());
477     }
478     {
479         utils::ComboRenderBundleEncoderDescriptor desc = {};
480         desc.colorFormatsCount = 1;
481         desc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm;
482         wgpu::RenderBundleEncoder bundleEncoder = device.CreateRenderBundleEncoder(&desc);
483         bundleEncoder.SetPipeline(pipeline);
484         bundleEncoder.SetVertexBuffer(0, vertexBuffer);
485         bundleEncoder.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32, 0);
486         bundleEncoder.DrawIndexedIndirect(indirectBuffer, 40);
487         bundles.push_back(bundleEncoder.Finish());
488     }
489 
490     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
491     {
492         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
493         pass.ExecuteBundles(bundles.size(), bundles.data());
494         pass.EndPass();
495     }
496     wgpu::CommandBuffer commands = encoder.Finish();
497 
498     queue.Submit(1, &commands);
499     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3);
500     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, 3, 1);
501 }
502 
TEST_P(DrawIndexedIndirectTest,ValidateWithBundlesInDifferentPasses)503 TEST_P(DrawIndexedIndirectTest, ValidateWithBundlesInDifferentPasses) {
504     // TODO(crbug.com/dawn/789): Test is failing under SwANGLE on Windows only.
505     DAWN_SUPPRESS_TEST_IF(IsANGLE() && IsWindows());
506 
507     // It doesn't make sense to test invalid inputs when validation is disabled.
508     DAWN_SUPPRESS_TEST_IF(HasToggleEnabled("skip_validation"));
509 
510     RGBA8 filled(0, 255, 0, 255);
511     RGBA8 notFilled(0, 0, 0, 0);
512 
513     wgpu::Buffer indirectBuffer =
514         CreateIndirectBuffer({3, 1, 3, 0, 0, 10, 1, 0, 0, 0, 3, 1, 6, 0, 0});
515     wgpu::Buffer indexBuffer = CreateIndexBuffer({0, 1, 2, 0, 3, 1, 0, 1, 2});
516 
517     wgpu::CommandBuffer commands[2];
518     {
519         wgpu::RenderBundle bundle;
520         utils::ComboRenderBundleEncoderDescriptor desc = {};
521         desc.colorFormatsCount = 1;
522         desc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm;
523         wgpu::RenderBundleEncoder bundleEncoder = device.CreateRenderBundleEncoder(&desc);
524         bundleEncoder.SetPipeline(pipeline);
525         bundleEncoder.SetVertexBuffer(0, vertexBuffer);
526         bundleEncoder.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32, 0);
527         bundleEncoder.DrawIndexedIndirect(indirectBuffer, 20);
528         bundle = bundleEncoder.Finish();
529 
530         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
531         renderPass.renderPassInfo.cColorAttachments[0].loadOp = wgpu::LoadOp::Load;
532         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
533         pass.ExecuteBundles(1, &bundle);
534         pass.EndPass();
535 
536         commands[0] = encoder.Finish();
537     }
538 
539     {
540         wgpu::RenderBundle bundle;
541         utils::ComboRenderBundleEncoderDescriptor desc = {};
542         desc.colorFormatsCount = 1;
543         desc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm;
544         wgpu::RenderBundleEncoder bundleEncoder = device.CreateRenderBundleEncoder(&desc);
545         bundleEncoder.SetPipeline(pipeline);
546         bundleEncoder.SetVertexBuffer(0, vertexBuffer);
547         bundleEncoder.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32, 0);
548         bundleEncoder.DrawIndexedIndirect(indirectBuffer, 40);
549         bundle = bundleEncoder.Finish();
550 
551         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
552         renderPass.renderPassInfo.cColorAttachments[0].loadOp = wgpu::LoadOp::Clear;
553         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
554         pass.ExecuteBundles(1, &bundle);
555         pass.EndPass();
556 
557         commands[1] = encoder.Finish();
558     }
559 
560     queue.Submit(1, &commands[1]);
561     queue.Submit(1, &commands[0]);
562 
563     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3);
564     EXPECT_PIXEL_RGBA8_EQ(notFilled, renderPass.color, 3, 1);
565 }
566 
TEST_P(DrawIndexedIndirectTest,ValidateReusedBundleWithChangingParams)567 TEST_P(DrawIndexedIndirectTest, ValidateReusedBundleWithChangingParams) {
568     // TODO(crbug.com/dawn/789): Test is failing under SwANGLE on Windows.
569     DAWN_SUPPRESS_TEST_IF(IsANGLE() && IsWindows());
570 
571     // TODO(crbug.com/dawn/1124): Fails on Intel+Vulkan+Windows for drivers
572     // older than 27.20.100.8587, which bots are actively using.
573     DAWN_SUPPRESS_TEST_IF(IsIntel() && IsVulkan() && IsWindows());
574 
575     // It doesn't make sense to test invalid inputs when validation is disabled.
576     DAWN_SUPPRESS_TEST_IF(HasToggleEnabled("skip_validation"));
577 
578     RGBA8 filled(0, 255, 0, 255);
579     // RGBA8 notFilled(0, 0, 0, 0);
580 
581     wgpu::Buffer indirectBuffer = CreateIndirectBuffer({0, 0, 0, 0, 0});
582     wgpu::Buffer indexBuffer = CreateIndexBuffer({0, 1, 2, 0, 3, 1});
583 
584     // Encode a single bundle that always uses indirectBuffer offset 0 for its params.
585     wgpu::RenderBundle bundle;
586     utils::ComboRenderBundleEncoderDescriptor desc = {};
587     desc.colorFormatsCount = 1;
588     desc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm;
589     wgpu::RenderBundleEncoder bundleEncoder = device.CreateRenderBundleEncoder(&desc);
590     bundleEncoder.SetPipeline(pipeline);
591     bundleEncoder.SetVertexBuffer(0, vertexBuffer);
592     bundleEncoder.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32, 0);
593     bundleEncoder.DrawIndexedIndirect(indirectBuffer, 0);
594     bundle = bundleEncoder.Finish();
595 
596     wgpu::ShaderModule paramWriterModule = utils::CreateShaderModule(device,
597                                                                      R"(
598             [[block]] struct Input { firstIndex: u32; };
599             [[block]] struct Params {
600                 indexCount: u32;
601                 instanceCount: u32;
602                 firstIndex: u32;
603             };
604             [[group(0), binding(0)]] var<uniform> input: Input;
605             [[group(0), binding(1)]] var<storage, write> params: Params;
606             [[stage(compute), workgroup_size(1)]] fn main() {
607                 params.indexCount = 3u;
608                 params.instanceCount = 1u;
609                 params.firstIndex = input.firstIndex;
610             }
611         )");
612 
613     wgpu::ComputePipelineDescriptor computeDesc;
614     computeDesc.compute.module = paramWriterModule;
615     computeDesc.compute.entryPoint = "main";
616     wgpu::ComputePipeline computePipeline = device.CreateComputePipeline(&computeDesc);
617 
618     wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
619 
620     auto encodeComputePassToUpdateFirstIndex = [&](uint32_t newFirstIndex) {
621         wgpu::Buffer input = utils::CreateBufferFromData<uint32_t>(
622             device, wgpu::BufferUsage::Uniform, {newFirstIndex});
623         wgpu::BindGroup bindGroup = utils::MakeBindGroup(
624             device, computePipeline.GetBindGroupLayout(0),
625             {{0, input, 0, sizeof(uint32_t)}, {1, indirectBuffer, 0, 5 * sizeof(uint32_t)}});
626         wgpu::ComputePassEncoder pass = encoder.BeginComputePass();
627         pass.SetPipeline(computePipeline);
628         pass.SetBindGroup(0, bindGroup);
629         pass.Dispatch(1);
630         pass.EndPass();
631     };
632 
633     auto encodeRenderPassToExecuteBundle = [&](wgpu::LoadOp colorLoadOp) {
634         renderPass.renderPassInfo.cColorAttachments[0].loadOp = colorLoadOp;
635         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
636         pass.ExecuteBundles(1, &bundle);
637         pass.EndPass();
638     };
639 
640     encodeComputePassToUpdateFirstIndex(0);
641     encodeRenderPassToExecuteBundle(wgpu::LoadOp::Clear);
642     encodeComputePassToUpdateFirstIndex(3);
643     encodeRenderPassToExecuteBundle(wgpu::LoadOp::Load);
644     encodeComputePassToUpdateFirstIndex(6);
645     encodeRenderPassToExecuteBundle(wgpu::LoadOp::Load);
646 
647     wgpu::CommandBuffer commands = encoder.Finish();
648     queue.Submit(1, &commands);
649 
650     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 1, 3);
651     EXPECT_PIXEL_RGBA8_EQ(filled, renderPass.color, 3, 1);
652 }
653 
654 DAWN_INSTANTIATE_TEST(DrawIndexedIndirectTest,
655                       D3D12Backend(),
656                       MetalBackend(),
657                       OpenGLBackend(),
658                       OpenGLESBackend(),
659                       VulkanBackend());
660