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