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