1 // Copyright 2017 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 "common/Assert.h"
18 #include "utils/ComboRenderPipelineDescriptor.h"
19 #include "utils/WGPUHelpers.h"
20
21 // Primitive topology tests work by drawing the following vertices with all the different primitive
22 // topology states:
23 // -------------------------------------
24 // | |
25 // | 1 2 5 |
26 // | |
27 // | |
28 // | |
29 // | |
30 // | 0 3 4 |
31 // | |
32 // -------------------------------------
33 //
34 // Points: This case looks exactly like above
35 //
36 // Lines
37 // -------------------------------------
38 // | |
39 // | 1 2 5 |
40 // | | | | |
41 // | | | | |
42 // | | | | |
43 // | | | | |
44 // | 0 3 4 |
45 // | |
46 // -------------------------------------
47 //
48 // Line Strip
49 // -------------------------------------
50 // | |
51 // | 1--------2 5 |
52 // | | | | |
53 // | | | | |
54 // | | | | |
55 // | | | | |
56 // | 0 3--------4 |
57 // | |
58 // -------------------------------------
59 //
60 // Triangle
61 // -------------------------------------
62 // | |
63 // | 1--------2 5 |
64 // | |xxxxxxx x| |
65 // | |xxxxx xxx| |
66 // | |xxx xxxxx| |
67 // | |x xxxxxxx| |
68 // | 0 3--------4 |
69 // | |
70 // -------------------------------------
71 //
72 // Triangle Strip
73 // -------------------------------------
74 // | |
75 // | 1--------2 5 |
76 // | |xxxxxxxxx x| |
77 // | |xxxxxxxxxxx xxx| |
78 // | |xxx xxxxxxxxxxx| |
79 // | |x xxxxxxxxxx| |
80 // | 0 3--------4 |
81 // | |
82 // -------------------------------------
83 //
84 // Each of these different states is a superset of some of the previous states,
85 // so for every state, we check any new added test locations that are not contained in previous
86 // states We also check that the test locations of subsequent states are untouched
87
88 constexpr static unsigned int kRTSize = 32;
89
90 struct TestLocation {
91 unsigned int x, y;
92 };
93
GetMidpoint(const TestLocation & a,const TestLocation & b)94 constexpr TestLocation GetMidpoint(const TestLocation& a, const TestLocation& b) noexcept {
95 return {(a.x + b.x) / 2, (a.y + b.y) / 2};
96 }
97
GetCentroid(const TestLocation & a,const TestLocation & b,const TestLocation & c)98 constexpr TestLocation GetCentroid(const TestLocation& a,
99 const TestLocation& b,
100 const TestLocation& c) noexcept {
101 return {(a.x + b.x + c.x) / 3, (a.y + b.y + c.y) / 3};
102 }
103
104 // clang-format off
105 // Offset towards one corner to avoid x or y symmetry false positives
106 constexpr static unsigned int kOffset = kRTSize / 8;
107
108 constexpr static TestLocation kPointTestLocations[] = {
109 { kRTSize * 1 / 4 + kOffset, kRTSize * 1 / 4 + kOffset },
110 { kRTSize * 1 / 4 + kOffset, kRTSize * 3 / 4 + kOffset },
111 { kRTSize * 2 / 4 + kOffset, kRTSize * 3 / 4 + kOffset },
112 { kRTSize * 2 / 4 + kOffset, kRTSize * 1 / 4 + kOffset },
113 { kRTSize * 3 / 4 + kOffset, kRTSize * 1 / 4 + kOffset },
114 { kRTSize * 3 / 4 + kOffset, kRTSize * 3 / 4 + kOffset },
115 };
116
117 constexpr static TestLocation kLineTestLocations[] = {
118 GetMidpoint(kPointTestLocations[0], kPointTestLocations[1]),
119 GetMidpoint(kPointTestLocations[2], kPointTestLocations[3]),
120 GetMidpoint(kPointTestLocations[4], kPointTestLocations[5]),
121 };
122
123 constexpr static TestLocation kLineStripTestLocations[] = {
124 GetMidpoint(kPointTestLocations[1], kPointTestLocations[2]),
125 GetMidpoint(kPointTestLocations[3], kPointTestLocations[4]),
126 };
127
128 constexpr static TestLocation kTriangleTestLocations[] = {
129 GetCentroid(kPointTestLocations[0], kPointTestLocations[1], kPointTestLocations[2]),
130 GetCentroid(kPointTestLocations[3], kPointTestLocations[4], kPointTestLocations[5]),
131 };
132
133 constexpr static TestLocation kTriangleStripTestLocations[] = {
134 GetCentroid(kPointTestLocations[1], kPointTestLocations[2], kPointTestLocations[3]),
135 GetCentroid(kPointTestLocations[2], kPointTestLocations[3], kPointTestLocations[4]),
136 };
137
138 constexpr static float kRTSizef = static_cast<float>(kRTSize);
139 constexpr static float kVertices[] = {
140 2.f * (kPointTestLocations[0].x + 0.5f) / kRTSizef - 1.f, -2.f * (kPointTestLocations[0].y + 0.5f) / kRTSizef + 1.0f, 0.f, 1.f,
141 2.f * (kPointTestLocations[1].x + 0.5f) / kRTSizef - 1.f, -2.f * (kPointTestLocations[1].y + 0.5f) / kRTSizef + 1.0f, 0.f, 1.f,
142 2.f * (kPointTestLocations[2].x + 0.5f) / kRTSizef - 1.f, -2.f * (kPointTestLocations[2].y + 0.5f) / kRTSizef + 1.0f, 0.f, 1.f,
143 2.f * (kPointTestLocations[3].x + 0.5f) / kRTSizef - 1.f, -2.f * (kPointTestLocations[3].y + 0.5f) / kRTSizef + 1.0f, 0.f, 1.f,
144 2.f * (kPointTestLocations[4].x + 0.5f) / kRTSizef - 1.f, -2.f * (kPointTestLocations[4].y + 0.5f) / kRTSizef + 1.0f, 0.f, 1.f,
145 2.f * (kPointTestLocations[5].x + 0.5f) / kRTSizef - 1.f, -2.f * (kPointTestLocations[5].y + 0.5f) / kRTSizef + 1.0f, 0.f, 1.f,
146 };
147 // clang-format on
148
149 class PrimitiveTopologyTest : public DawnTest {
150 protected:
SetUp()151 void SetUp() override {
152 DawnTest::SetUp();
153
154 renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
155
156 vsModule = utils::CreateShaderModule(device, R"(
157 [[stage(vertex)]]
158 fn main([[location(0)]] pos : vec4<f32>) -> [[builtin(position)]] vec4<f32> {
159 return pos;
160 })");
161
162 fsModule = utils::CreateShaderModule(device, R"(
163 [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
164 return vec4<f32>(0.0, 1.0, 0.0, 1.0);
165 })");
166
167 vertexBuffer = utils::CreateBufferFromData(device, kVertices, sizeof(kVertices),
168 wgpu::BufferUsage::Vertex);
169 }
170
171 struct LocationSpec {
172 const TestLocation* locations;
173 size_t count;
174 bool include;
175 };
176
177 template <std::size_t N>
TestPoints(TestLocation const (& points)[N],bool include)178 constexpr LocationSpec TestPoints(TestLocation const (&points)[N], bool include) noexcept {
179 return {points, N, include};
180 }
181
182 // Draw the vertices with the given primitive topology and check the pixel values of the test
183 // locations
DoTest(wgpu::PrimitiveTopology primitiveTopology,const std::vector<LocationSpec> & locationSpecs)184 void DoTest(wgpu::PrimitiveTopology primitiveTopology,
185 const std::vector<LocationSpec>& locationSpecs) {
186 utils::ComboRenderPipelineDescriptor descriptor;
187 descriptor.vertex.module = vsModule;
188 descriptor.cFragment.module = fsModule;
189
190 descriptor.primitive.topology = primitiveTopology;
191 if (primitiveTopology == wgpu::PrimitiveTopology::TriangleStrip ||
192 primitiveTopology == wgpu::PrimitiveTopology::LineStrip) {
193 descriptor.primitive.stripIndexFormat = wgpu::IndexFormat::Uint32;
194 }
195
196 descriptor.vertex.bufferCount = 1;
197 descriptor.cBuffers[0].arrayStride = 4 * sizeof(float);
198 descriptor.cBuffers[0].attributeCount = 1;
199 descriptor.cAttributes[0].format = wgpu::VertexFormat::Float32x4;
200 descriptor.cTargets[0].format = renderPass.colorFormat;
201
202 wgpu::RenderPipeline pipeline = device.CreateRenderPipeline(&descriptor);
203
204 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
205 {
206 wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
207 pass.SetPipeline(pipeline);
208 pass.SetVertexBuffer(0, vertexBuffer);
209 pass.Draw(6);
210 pass.EndPass();
211 }
212
213 wgpu::CommandBuffer commands = encoder.Finish();
214 queue.Submit(1, &commands);
215
216 for (auto& locationSpec : locationSpecs) {
217 for (size_t i = 0; i < locationSpec.count; ++i) {
218 // If this pixel is included, check that it is green. Otherwise, check that it is
219 // black
220 RGBA8 color = locationSpec.include ? RGBA8::kGreen : RGBA8::kZero;
221 EXPECT_PIXEL_RGBA8_EQ(color, renderPass.color, locationSpec.locations[i].x,
222 locationSpec.locations[i].y)
223 << "Expected (" << locationSpec.locations[i].x << ", "
224 << locationSpec.locations[i].y << ") to be " << color;
225 }
226 }
227 }
228
229 utils::BasicRenderPass renderPass;
230 wgpu::ShaderModule vsModule;
231 wgpu::ShaderModule fsModule;
232 wgpu::Buffer vertexBuffer;
233 };
234
235 // Test Point primitive topology
TEST_P(PrimitiveTopologyTest,PointList)236 TEST_P(PrimitiveTopologyTest, PointList) {
237 DoTest(wgpu::PrimitiveTopology::PointList,
238 {
239 // Check that the points are drawn
240 TestPoints(kPointTestLocations, true),
241
242 // Check that line and triangle locations are untouched
243 TestPoints(kLineTestLocations, false),
244 TestPoints(kLineStripTestLocations, false),
245 TestPoints(kTriangleTestLocations, false),
246 TestPoints(kTriangleStripTestLocations, false),
247 });
248 }
249
250 // Test Line primitive topology
TEST_P(PrimitiveTopologyTest,LineList)251 TEST_P(PrimitiveTopologyTest, LineList) {
252 DoTest(wgpu::PrimitiveTopology::LineList,
253 {
254 // Check that lines are drawn
255 TestPoints(kLineTestLocations, true),
256
257 // Check that line strip and triangle locations are untouched
258 TestPoints(kLineStripTestLocations, false),
259 TestPoints(kTriangleTestLocations, false),
260 TestPoints(kTriangleStripTestLocations, false),
261 });
262 }
263
264 // Test LineStrip primitive topology
TEST_P(PrimitiveTopologyTest,LineStrip)265 TEST_P(PrimitiveTopologyTest, LineStrip) {
266 DoTest(wgpu::PrimitiveTopology::LineStrip, {
267 // Check that lines are drawn
268 TestPoints(kLineTestLocations, true),
269 TestPoints(kLineStripTestLocations, true),
270
271 // Check that triangle locations are untouched
272 TestPoints(kTriangleTestLocations, false),
273 TestPoints(kTriangleStripTestLocations, false),
274 });
275 }
276
277 // Test Triangle primitive topology
TEST_P(PrimitiveTopologyTest,TriangleList)278 TEST_P(PrimitiveTopologyTest, TriangleList) {
279 DoTest(wgpu::PrimitiveTopology::TriangleList,
280 {
281 // Check that triangles are drawn
282 TestPoints(kTriangleTestLocations, true),
283
284 // Check that triangle strip locations are untouched
285 TestPoints(kTriangleStripTestLocations, false),
286 });
287 }
288
289 // Test TriangleStrip primitive topology
TEST_P(PrimitiveTopologyTest,TriangleStrip)290 TEST_P(PrimitiveTopologyTest, TriangleStrip) {
291 DoTest(wgpu::PrimitiveTopology::TriangleStrip,
292 {
293 TestPoints(kTriangleTestLocations, true),
294 TestPoints(kTriangleStripTestLocations, true),
295 });
296 }
297
298 DAWN_INSTANTIATE_TEST(PrimitiveTopologyTest,
299 D3D12Backend(),
300 MetalBackend(),
301 OpenGLBackend(),
302 OpenGLESBackend(),
303 VulkanBackend());
304