• 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 <initializer_list>
16 #include <limits>
17 #include "tests/unittests/validation/ValidationTest.h"
18 #include "utils/ComboRenderPipelineDescriptor.h"
19 #include "utils/WGPUHelpers.h"
20 
21 class DrawIndirectValidationTest : public ValidationTest {
22   protected:
SetUp()23     void SetUp() override {
24         ValidationTest::SetUp();
25 
26         wgpu::ShaderModule vsModule = utils::CreateShaderModule(device, R"(
27             [[stage(vertex)]] fn main() -> [[builtin(position)]] vec4<f32> {
28                 return vec4<f32>(0.0, 0.0, 0.0, 0.0);
29             })");
30 
31         wgpu::ShaderModule fsModule = utils::CreateShaderModule(device, R"(
32             [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32>{
33                 return vec4<f32>(0.0, 0.0, 0.0, 0.0);
34             })");
35 
36         // Set up render pipeline
37         wgpu::PipelineLayout pipelineLayout = utils::MakeBasicPipelineLayout(device, nullptr);
38 
39         utils::ComboRenderPipelineDescriptor descriptor;
40         descriptor.layout = pipelineLayout;
41         descriptor.vertex.module = vsModule;
42         descriptor.cFragment.module = fsModule;
43 
44         pipeline = device.CreateRenderPipeline(&descriptor);
45     }
46 
ValidateExpectation(wgpu::CommandEncoder encoder,utils::Expectation expectation)47     void ValidateExpectation(wgpu::CommandEncoder encoder, utils::Expectation expectation) {
48         if (expectation == utils::Expectation::Success) {
49             encoder.Finish();
50         } else {
51             ASSERT_DEVICE_ERROR(encoder.Finish());
52         }
53     }
54 
TestIndirectOffsetDrawIndexed(utils::Expectation expectation,std::initializer_list<uint32_t> bufferList,uint64_t indirectOffset)55     void TestIndirectOffsetDrawIndexed(utils::Expectation expectation,
56                                        std::initializer_list<uint32_t> bufferList,
57                                        uint64_t indirectOffset) {
58         TestIndirectOffset(expectation, bufferList, indirectOffset, true);
59     }
60 
TestIndirectOffsetDraw(utils::Expectation expectation,std::initializer_list<uint32_t> bufferList,uint64_t indirectOffset)61     void TestIndirectOffsetDraw(utils::Expectation expectation,
62                                 std::initializer_list<uint32_t> bufferList,
63                                 uint64_t indirectOffset) {
64         TestIndirectOffset(expectation, bufferList, indirectOffset, false);
65     }
66 
TestIndirectOffset(utils::Expectation expectation,std::initializer_list<uint32_t> bufferList,uint64_t indirectOffset,bool indexed,wgpu::BufferUsage usage=wgpu::BufferUsage::Indirect)67     void TestIndirectOffset(utils::Expectation expectation,
68                             std::initializer_list<uint32_t> bufferList,
69                             uint64_t indirectOffset,
70                             bool indexed,
71                             wgpu::BufferUsage usage = wgpu::BufferUsage::Indirect) {
72         wgpu::Buffer indirectBuffer =
73             utils::CreateBufferFromData<uint32_t>(device, usage, bufferList);
74 
75         DummyRenderPass renderPass(device);
76         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
77         wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
78         pass.SetPipeline(pipeline);
79         if (indexed) {
80             uint32_t zeros[100] = {};
81             wgpu::Buffer indexBuffer =
82                 utils::CreateBufferFromData(device, zeros, sizeof(zeros), wgpu::BufferUsage::Index);
83             pass.SetIndexBuffer(indexBuffer, wgpu::IndexFormat::Uint32);
84             pass.DrawIndexedIndirect(indirectBuffer, indirectOffset);
85         } else {
86             pass.DrawIndirect(indirectBuffer, indirectOffset);
87         }
88         pass.EndPass();
89 
90         ValidateExpectation(encoder, expectation);
91     }
92 
93     wgpu::RenderPipeline pipeline;
94 };
95 
96 // Verify out of bounds indirect draw calls are caught early
TEST_F(DrawIndirectValidationTest,DrawIndirectOffsetBounds)97 TEST_F(DrawIndirectValidationTest, DrawIndirectOffsetBounds) {
98     // In bounds
99     TestIndirectOffsetDraw(utils::Expectation::Success, {1, 2, 3, 4}, 0);
100     // In bounds, bigger buffer
101     TestIndirectOffsetDraw(utils::Expectation::Success, {1, 2, 3, 4, 5, 6, 7}, 0);
102     // In bounds, bigger buffer, positive offset
103     TestIndirectOffsetDraw(utils::Expectation::Success, {1, 2, 3, 4, 5, 6, 7, 8},
104                            4 * sizeof(uint32_t));
105 
106     // In bounds, non-multiple of 4 offsets
107     TestIndirectOffsetDraw(utils::Expectation::Failure, {1, 2, 3, 4, 5}, 1);
108     TestIndirectOffsetDraw(utils::Expectation::Failure, {1, 2, 3, 4, 5}, 2);
109 
110     // Out of bounds, buffer too small
111     TestIndirectOffsetDraw(utils::Expectation::Failure, {1, 2, 3}, 0);
112     // Out of bounds, index too big
113     TestIndirectOffsetDraw(utils::Expectation::Failure, {1, 2, 3, 4}, 1 * sizeof(uint32_t));
114     // Out of bounds, index past buffer
115     TestIndirectOffsetDraw(utils::Expectation::Failure, {1, 2, 3, 4}, 5 * sizeof(uint32_t));
116     // Out of bounds, index + size of command overflows
117     uint64_t offset = std::numeric_limits<uint64_t>::max();
118     TestIndirectOffsetDraw(utils::Expectation::Failure, {1, 2, 3, 4, 5, 6, 7}, offset);
119 }
120 
121 // Verify out of bounds indirect draw indexed calls are caught early
TEST_F(DrawIndirectValidationTest,DrawIndexedIndirectOffsetBounds)122 TEST_F(DrawIndirectValidationTest, DrawIndexedIndirectOffsetBounds) {
123     // In bounds
124     TestIndirectOffsetDrawIndexed(utils::Expectation::Success, {1, 2, 3, 4, 5}, 0);
125     // In bounds, bigger buffer
126     TestIndirectOffsetDrawIndexed(utils::Expectation::Success, {1, 2, 3, 4, 5, 6, 7, 8, 9}, 0);
127     // In bounds, bigger buffer, positive offset
128     TestIndirectOffsetDrawIndexed(utils::Expectation::Success, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
129                                   5 * sizeof(uint32_t));
130 
131     // In bounds, non-multiple of 4 offsets
132     TestIndirectOffsetDrawIndexed(utils::Expectation::Failure, {1, 2, 3, 4, 5, 6}, 1);
133     TestIndirectOffsetDrawIndexed(utils::Expectation::Failure, {1, 2, 3, 4, 5, 6}, 2);
134 
135     // Out of bounds, buffer too small
136     TestIndirectOffsetDrawIndexed(utils::Expectation::Failure, {1, 2, 3, 4}, 0);
137     // Out of bounds, index too big
138     TestIndirectOffsetDrawIndexed(utils::Expectation::Failure, {1, 2, 3, 4, 5},
139                                   1 * sizeof(uint32_t));
140     // Out of bounds, index past buffer
141     TestIndirectOffsetDrawIndexed(utils::Expectation::Failure, {1, 2, 3, 4, 5},
142                                   5 * sizeof(uint32_t));
143     // Out of bounds, index + size of command overflows
144     uint64_t offset = std::numeric_limits<uint64_t>::max();
145     TestIndirectOffsetDrawIndexed(utils::Expectation::Failure, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
146                                   offset);
147 }
148 
149 // Check that the buffer must have the indirect usage
TEST_F(DrawIndirectValidationTest,IndirectUsage)150 TEST_F(DrawIndirectValidationTest, IndirectUsage) {
151     // Control cases: using a buffer with the indirect usage is valid.
152     TestIndirectOffset(utils::Expectation::Success, {1, 2, 3, 4}, 0, false,
153                        wgpu::BufferUsage::Indirect);
154     TestIndirectOffset(utils::Expectation::Success, {1, 2, 3, 4, 5}, 0, true,
155                        wgpu::BufferUsage::Indirect);
156 
157     // Error cases: using a buffer with the vertex usage is an error.
158     TestIndirectOffset(utils::Expectation::Failure, {1, 2, 3, 4}, 0, false,
159                        wgpu::BufferUsage::Vertex);
160     TestIndirectOffset(utils::Expectation::Failure, {1, 2, 3, 4, 5}, 0, true,
161                        wgpu::BufferUsage::Vertex);
162 }
163