1 // Copyright 2020 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/unittests/validation/ValidationTest.h"
16
17 #include "utils/ComboRenderBundleEncoderDescriptor.h"
18 #include "utils/ComboRenderPipelineDescriptor.h"
19 #include "utils/WGPUHelpers.h"
20
21 class IndexBufferValidationTest : public ValidationTest {
22 protected:
MakeTestPipeline(wgpu::IndexFormat format,wgpu::PrimitiveTopology primitiveTopology)23 wgpu::RenderPipeline MakeTestPipeline(wgpu::IndexFormat format,
24 wgpu::PrimitiveTopology primitiveTopology) {
25 wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
26 [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
27 return vec4<f32>(0.0, 0.0, 0.0, 1.0);
28 })");
29
30 wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
31 [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
32 return vec4<f32>(0.0, 1.0, 0.0, 1.0);
33 })");
34
35 utils::ComboRenderPipelineDescriptor descriptor;
36 descriptor.vertex.module = vsModule;
37 descriptor.cFragment.module = fsModule;
38 descriptor.primitive.topology = primitiveTopology;
39 descriptor.primitive.stripIndexFormat = format;
40 descriptor.cTargets[0].format = wgpu::TextureFormat::RGBA8Unorm;
41
42 return device.CreateRenderPipeline(&descriptor);
43 }
44 };
45
46 // Test that IndexFormat::Undefined is disallowed.
TEST_F(IndexBufferValidationTest,UndefinedIndexFormat)47 TEST_F(IndexBufferValidationTest, UndefinedIndexFormat) {
48 wgpu::BufferDescriptor bufferDesc;
49 bufferDesc.usage = wgpu::BufferUsage::Index;
50 bufferDesc.size = 256;
51 wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc);
52
53 DummyRenderPass renderPass(device);
54 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
55 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
56 pass.SetIndexBuffer(buffer, wgpu::IndexFormat::Undefined);
57 pass.EndPass();
58 ASSERT_DEVICE_ERROR(encoder.Finish());
59 }
60
61 // Test that an invalid index format is disallowed.
TEST_F(IndexBufferValidationTest,InvalidIndexFormat)62 TEST_F(IndexBufferValidationTest, InvalidIndexFormat) {
63 wgpu::BufferDescriptor bufferDesc;
64 bufferDesc.usage = wgpu::BufferUsage::Index;
65 bufferDesc.size = 256;
66 wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc);
67
68 DummyRenderPass renderPass(device);
69 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
70 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
71 pass.SetIndexBuffer(buffer, static_cast<wgpu::IndexFormat>(404));
72 pass.EndPass();
73 ASSERT_DEVICE_ERROR(encoder.Finish());
74 }
75
76 // Test that for OOB validation of index buffer offset and size.
TEST_F(IndexBufferValidationTest,IndexBufferOffsetOOBValidation)77 TEST_F(IndexBufferValidationTest, IndexBufferOffsetOOBValidation) {
78 wgpu::BufferDescriptor bufferDesc;
79 bufferDesc.usage = wgpu::BufferUsage::Index;
80 bufferDesc.size = 256;
81 wgpu::Buffer buffer = device.CreateBuffer(&bufferDesc);
82
83 DummyRenderPass renderPass(device);
84 // Control case, using the full buffer, with or without an explicit size is valid.
85 {
86 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
87 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
88 // Explicit size
89 pass.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32, 0, 256);
90 // Implicit size
91 pass.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32, 0, wgpu::kWholeSize);
92 pass.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32, 256 - 4, wgpu::kWholeSize);
93 pass.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32, 4, wgpu::kWholeSize);
94 // Implicit size of zero
95 pass.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32, 256, wgpu::kWholeSize);
96 pass.EndPass();
97 encoder.Finish();
98 }
99
100 // Bad case, offset + size is larger than the buffer
101 {
102 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
103 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
104 pass.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32, 4, 256);
105 pass.EndPass();
106 ASSERT_DEVICE_ERROR(encoder.Finish());
107 }
108
109 // Bad case, size is 0 but the offset is larger than the buffer
110 {
111 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
112 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
113 pass.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32, 256 + 4, 0);
114 pass.EndPass();
115 ASSERT_DEVICE_ERROR(encoder.Finish());
116 }
117
118 utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {};
119 renderBundleDesc.colorFormatsCount = 1;
120 renderBundleDesc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm;
121
122 // Control case, using the full buffer, with or without an explicit size is valid.
123 {
124 wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc);
125 // Explicit size
126 encoder.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32, 0, 256);
127 // Implicit size
128 encoder.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32, 0, wgpu::kWholeSize);
129 encoder.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32, 256 - 4, wgpu::kWholeSize);
130 encoder.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32, 4, wgpu::kWholeSize);
131 // Implicit size of zero
132 encoder.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32, 256, wgpu::kWholeSize);
133 encoder.Finish();
134 }
135
136 // Bad case, offset + size is larger than the buffer
137 {
138 wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc);
139 encoder.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32, 4, 256);
140 ASSERT_DEVICE_ERROR(encoder.Finish());
141 }
142
143 // Bad case, size is 0 but the offset is larger than the buffer
144 {
145 wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc);
146 encoder.SetIndexBuffer(buffer, wgpu::IndexFormat::Uint32, 256 + 4, 0);
147 ASSERT_DEVICE_ERROR(encoder.Finish());
148 }
149 }
150
151 // Test that formats given when setting an index buffers must match the format specified on the
152 // pipeline for strip primitive topologies.
TEST_F(IndexBufferValidationTest,IndexBufferFormatMatchesPipelineStripFormat)153 TEST_F(IndexBufferValidationTest, IndexBufferFormatMatchesPipelineStripFormat) {
154 wgpu::RenderPipeline pipeline32 = MakeTestPipeline(wgpu::IndexFormat::Uint32,
155 wgpu::PrimitiveTopology::TriangleStrip);
156 wgpu::RenderPipeline pipeline16 = MakeTestPipeline(wgpu::IndexFormat::Uint16,
157 wgpu::PrimitiveTopology::LineStrip);
158 wgpu::RenderPipeline pipelineUndef =
159 MakeTestPipeline(wgpu::IndexFormat::Undefined, wgpu::PrimitiveTopology::LineStrip);
160
161 wgpu::Buffer indexBuffer =
162 utils::CreateBufferFromData<uint32_t>(device, wgpu::BufferUsage::Index, {0, 1, 2});
163
164 utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {};
165 renderBundleDesc.colorFormatsCount = 1;
166 renderBundleDesc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm;
167
168 // Expected to fail because pipeline and index formats don't match.
169 {
170 wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc);
171 encoder.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint16);
172 encoder.SetPipeline(pipeline32);
173 encoder.DrawIndexed(3);
174 ASSERT_DEVICE_ERROR(encoder.Finish());
175 }
176
177 {
178 wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc);
179 encoder.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32);
180 encoder.SetPipeline(pipeline16);
181 encoder.DrawIndexed(3);
182 ASSERT_DEVICE_ERROR(encoder.Finish());
183 }
184
185 // Expected to succeed because pipeline and index formats match.
186 {
187 wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc);
188 encoder.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint16);
189 encoder.SetPipeline(pipeline16);
190 encoder.DrawIndexed(3);
191 encoder.Finish();
192 }
193
194 {
195 wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc);
196 encoder.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32);
197 encoder.SetPipeline(pipeline32);
198 encoder.DrawIndexed(3);
199 encoder.Finish();
200 }
201
202 // Expected to fail because pipeline doesn't specify an index format.
203 {
204 wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc);
205 encoder.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint16);
206 encoder.SetPipeline(pipelineUndef);
207 encoder.DrawIndexed(3);
208 ASSERT_DEVICE_ERROR(encoder.Finish());
209 }
210
211 {
212 wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc);
213 encoder.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32);
214 encoder.SetPipeline(pipelineUndef);
215 encoder.DrawIndexed(3);
216 ASSERT_DEVICE_ERROR(encoder.Finish());
217 }
218
219 // Expected to succeed because non-indexed draw calls don't require a pipeline index format.
220 {
221 wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc);
222 encoder.SetPipeline(pipelineUndef);
223 encoder.Draw(3);
224 encoder.Finish();
225 }
226 }
227
228 // Check that the index buffer must have the Index usage.
TEST_F(IndexBufferValidationTest,InvalidUsage)229 TEST_F(IndexBufferValidationTest, InvalidUsage) {
230 wgpu::Buffer indexBuffer =
231 utils::CreateBufferFromData<uint32_t>(device, wgpu::BufferUsage::Index, {0, 1, 2});
232 wgpu::Buffer copyBuffer =
233 utils::CreateBufferFromData<uint32_t>(device, wgpu::BufferUsage::CopySrc, {0, 1, 2});
234
235 DummyRenderPass renderPass(device);
236 // Control case: using the index buffer is valid.
237 {
238 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
239 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
240 pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32);
241 pass.EndPass();
242 encoder.Finish();
243 }
244 // Error case: using the copy buffer is an error.
245 {
246 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
247 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
248 pass.SetIndexBuffer(copyBuffer, wgpu::IndexFormat::Uint32);
249 pass.EndPass();
250 ASSERT_DEVICE_ERROR(encoder.Finish());
251 }
252
253 utils::ComboRenderBundleEncoderDescriptor renderBundleDesc = {};
254 renderBundleDesc.colorFormatsCount = 1;
255 renderBundleDesc.cColorFormats[0] = wgpu::TextureFormat::RGBA8Unorm;
256 // Control case: using the index buffer is valid.
257 {
258 wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc);
259 encoder.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32);
260 encoder.Finish();
261 }
262 // Error case: using the copy buffer is an error.
263 {
264 wgpu::RenderBundleEncoder encoder = device.CreateRenderBundleEncoder(&renderBundleDesc);
265 encoder.SetIndexBuffer(copyBuffer, wgpu::IndexFormat::Uint32);
266 ASSERT_DEVICE_ERROR(encoder.Finish());
267 }
268 }
269
270 // Check the alignment constraint on the index buffer offset.
TEST_F(IndexBufferValidationTest,OffsetAlignment)271 TEST_F(IndexBufferValidationTest, OffsetAlignment) {
272 wgpu::Buffer indexBuffer =
273 utils::CreateBufferFromData<uint32_t>(device, wgpu::BufferUsage::Index, {0, 1, 2});
274
275 DummyRenderPass renderPass(device);
276 // Control cases: index buffer offset is a multiple of the index format size
277 {
278 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
279 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
280 pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32, 0);
281 pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32, 4);
282 pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint16, 0);
283 pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint16, 2);
284 pass.EndPass();
285 encoder.Finish();
286 }
287
288 // Error case: index buffer offset isn't a multiple of 4 for IndexFormat::Uint32
289 {
290 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
291 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
292 pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32, 2);
293 pass.EndPass();
294 ASSERT_DEVICE_ERROR(encoder.Finish());
295 }
296 // Error case: index buffer offset isn't a multiple of 2 for IndexFormat::Uint16
297 {
298 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
299 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
300 pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint16, 1);
301 pass.EndPass();
302 ASSERT_DEVICE_ERROR(encoder.Finish());
303 }
304 }
305