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