1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 #include "test_utils/ANGLETest.h"
8
9 using namespace angle;
10
11 class InstancingTest : public ANGLETest
12 {
13 protected:
InstancingTest()14 InstancingTest() : mProgram(0), mVertexBuffer(0)
15 {
16 setWindowWidth(256);
17 setWindowHeight(256);
18 setConfigRedBits(8);
19 setConfigGreenBits(8);
20 setConfigBlueBits(8);
21 setConfigAlphaBits(8);
22 }
23
~InstancingTest()24 ~InstancingTest() override
25 {
26 glDeleteBuffers(1, &mVertexBuffer);
27 glDeleteProgram(mProgram);
28 }
29
SetUp()30 void SetUp() override
31 {
32 ANGLETest::SetUp();
33
34 mVertexAttribDivisorANGLE = NULL;
35 mDrawArraysInstancedANGLE = NULL;
36 mDrawElementsInstancedANGLE = NULL;
37
38 char *extensionString = (char*)glGetString(GL_EXTENSIONS);
39 if (strstr(extensionString, "GL_ANGLE_instanced_arrays"))
40 {
41 mVertexAttribDivisorANGLE = (PFNGLVERTEXATTRIBDIVISORANGLEPROC)eglGetProcAddress("glVertexAttribDivisorANGLE");
42 mDrawArraysInstancedANGLE = (PFNGLDRAWARRAYSINSTANCEDANGLEPROC)eglGetProcAddress("glDrawArraysInstancedANGLE");
43 mDrawElementsInstancedANGLE = (PFNGLDRAWELEMENTSINSTANCEDANGLEPROC)eglGetProcAddress("glDrawElementsInstancedANGLE");
44 }
45
46 ASSERT_TRUE(mVertexAttribDivisorANGLE != NULL);
47 ASSERT_TRUE(mDrawArraysInstancedANGLE != NULL);
48 ASSERT_TRUE(mDrawElementsInstancedANGLE != NULL);
49
50 // Initialize the vertex and index vectors
51 GLfloat qvertex1[3] = {-quadRadius, quadRadius, 0.0f};
52 GLfloat qvertex2[3] = {-quadRadius, -quadRadius, 0.0f};
53 GLfloat qvertex3[3] = { quadRadius, -quadRadius, 0.0f};
54 GLfloat qvertex4[3] = { quadRadius, quadRadius, 0.0f};
55 mQuadVertices.insert(mQuadVertices.end(), qvertex1, qvertex1 + 3);
56 mQuadVertices.insert(mQuadVertices.end(), qvertex2, qvertex2 + 3);
57 mQuadVertices.insert(mQuadVertices.end(), qvertex3, qvertex3 + 3);
58 mQuadVertices.insert(mQuadVertices.end(), qvertex4, qvertex4 + 3);
59
60 GLfloat coord1[2] = {0.0f, 0.0f};
61 GLfloat coord2[2] = {0.0f, 1.0f};
62 GLfloat coord3[2] = {1.0f, 1.0f};
63 GLfloat coord4[2] = {1.0f, 0.0f};
64 mTexcoords.insert(mTexcoords.end(), coord1, coord1 + 2);
65 mTexcoords.insert(mTexcoords.end(), coord2, coord2 + 2);
66 mTexcoords.insert(mTexcoords.end(), coord3, coord3 + 2);
67 mTexcoords.insert(mTexcoords.end(), coord4, coord4 + 2);
68
69 mIndices.push_back(0);
70 mIndices.push_back(1);
71 mIndices.push_back(2);
72 mIndices.push_back(0);
73 mIndices.push_back(2);
74 mIndices.push_back(3);
75
76 for (size_t vertexIndex = 0; vertexIndex < 6; ++vertexIndex)
77 {
78 mNonIndexedVertices.insert(mNonIndexedVertices.end(),
79 mQuadVertices.begin() + mIndices[vertexIndex] * 3,
80 mQuadVertices.begin() + mIndices[vertexIndex] * 3 + 3);
81 }
82
83 for (size_t vertexIndex = 0; vertexIndex < 6; ++vertexIndex)
84 {
85 mNonIndexedVertices.insert(mNonIndexedVertices.end(),
86 mQuadVertices.begin() + mIndices[vertexIndex] * 3,
87 mQuadVertices.begin() + mIndices[vertexIndex] * 3 + 3);
88 }
89
90 // Tile a 2x2 grid of the tiles
91 for (float y = -1.0f + quadRadius; y < 1.0f - quadRadius; y += quadRadius * 3)
92 {
93 for (float x = -1.0f + quadRadius; x < 1.0f - quadRadius; x += quadRadius * 3)
94 {
95 GLfloat instance[3] = {x + quadRadius, y + quadRadius, 0.0f};
96 mInstances.insert(mInstances.end(), instance, instance + 3);
97 }
98 }
99
100 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
101
102 glGenBuffers(1, &mVertexBuffer);
103
104 ASSERT_GL_NO_ERROR();
105 }
106
setupDrawArraysTest(const std::string & vs)107 void setupDrawArraysTest(const std::string &vs)
108 {
109 const std::string fs = SHADER_SOURCE
110 (
111 precision mediump float;
112 void main()
113 {
114 gl_FragColor = vec4(1.0, 0, 0, 1.0);
115 }
116 );
117
118 mProgram = CompileProgram(vs, fs);
119 ASSERT_NE(0u, mProgram);
120
121 // Set the viewport
122 glViewport(0, 0, getWindowWidth(), getWindowHeight());
123
124 // Clear the color buffer
125 glClear(GL_COLOR_BUFFER_BIT);
126
127 // Use the program object
128 glUseProgram(mProgram);
129 }
130
setupInstancedPointsTest()131 void setupInstancedPointsTest()
132 {
133 mIndices.clear();
134 mIndices.push_back(0);
135 mIndices.push_back(1);
136 mIndices.push_back(2);
137 mIndices.push_back(3);
138
139 // clang-format off
140 const std::string vs = SHADER_SOURCE
141 (
142 attribute vec3 a_position;
143 attribute vec3 a_instancePos;
144 void main()
145 {
146 gl_Position = vec4(a_position.xyz, 1.0);
147 gl_Position = vec4(a_instancePos.xyz, 1.0);
148 gl_PointSize = 6.0;
149 }
150 );
151
152 const std::string fs = SHADER_SOURCE
153 (
154 precision mediump float;
155 void main()
156 {
157 gl_FragColor = vec4(1.0, 0, 0, 1.0);
158 }
159 );
160 // clang-format on
161
162 mProgram = CompileProgram(vs, fs);
163 ASSERT_NE(0u, mProgram);
164
165 // Set the viewport
166 glViewport(0, 0, getWindowWidth(), getWindowHeight());
167
168 // Clear the color buffer
169 glClear(GL_COLOR_BUFFER_BIT);
170
171 // Use the program object
172 glUseProgram(mProgram);
173 }
174
runDrawArraysTest(GLint first,GLsizei count,GLsizei instanceCount,float * offset)175 void runDrawArraysTest(GLint first, GLsizei count, GLsizei instanceCount, float *offset)
176 {
177 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
178 glBufferData(GL_ARRAY_BUFFER, mInstances.size() * sizeof(mInstances[0]), &mInstances[0], GL_STATIC_DRAW);
179 glBindBuffer(GL_ARRAY_BUFFER, 0);
180
181 // Get the attribute locations
182 GLint positionLoc = glGetAttribLocation(mProgram, "a_position");
183 GLint instancePosLoc = glGetAttribLocation(mProgram, "a_instancePos");
184
185 // Load the vertex position
186 glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, mNonIndexedVertices.data());
187 glEnableVertexAttribArray(positionLoc);
188
189 // Load the instance position
190 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
191 glVertexAttribPointer(instancePosLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
192 glBindBuffer(GL_ARRAY_BUFFER, 0);
193 glEnableVertexAttribArray(instancePosLoc);
194
195 // Enable instancing
196 mVertexAttribDivisorANGLE(instancePosLoc, 1);
197
198 // Offset
199 GLint uniformLoc = glGetUniformLocation(mProgram, "u_offset");
200 ASSERT_NE(uniformLoc, -1);
201 glUniform3fv(uniformLoc, 1, offset);
202
203 // Do the instanced draw
204 mDrawArraysInstancedANGLE(GL_TRIANGLES, first, count, instanceCount);
205
206 ASSERT_GL_NO_ERROR();
207 }
208
runDrawElementsTest(std::string vs,bool shouldAttribZeroBeInstanced)209 virtual void runDrawElementsTest(std::string vs, bool shouldAttribZeroBeInstanced)
210 {
211 const std::string fs = SHADER_SOURCE
212 (
213 precision mediump float;
214 void main()
215 {
216 gl_FragColor = vec4(1.0, 0, 0, 1.0);
217 }
218 );
219
220 GLuint program = CompileProgram(vs, fs);
221 ASSERT_NE(program, 0u);
222
223 // Get the attribute locations
224 GLint positionLoc = glGetAttribLocation(program, "a_position");
225 GLint instancePosLoc = glGetAttribLocation(program, "a_instancePos");
226
227 // If this ASSERT fails then the vertex shader code should be refactored
228 ASSERT_EQ(shouldAttribZeroBeInstanced, (instancePosLoc == 0));
229
230 // Set the viewport
231 glViewport(0, 0, getWindowWidth(), getWindowHeight());
232
233 // Clear the color buffer
234 glClear(GL_COLOR_BUFFER_BIT);
235
236 // Use the program object
237 glUseProgram(program);
238
239 // Load the vertex position
240 glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, mQuadVertices.data());
241 glEnableVertexAttribArray(positionLoc);
242
243 // Load the instance position
244 glVertexAttribPointer(instancePosLoc, 3, GL_FLOAT, GL_FALSE, 0, mInstances.data());
245 glEnableVertexAttribArray(instancePosLoc);
246
247 // Enable instancing
248 mVertexAttribDivisorANGLE(instancePosLoc, 1);
249
250 // Do the instanced draw
251 mDrawElementsInstancedANGLE(GL_TRIANGLES, static_cast<GLsizei>(mIndices.size()),
252 GL_UNSIGNED_SHORT, mIndices.data(),
253 static_cast<GLsizei>(mInstances.size()) / 3);
254
255 ASSERT_GL_NO_ERROR();
256
257 checkQuads();
258 }
259
checkQuads()260 void checkQuads()
261 {
262 // Check that various pixels are the expected color.
263 for (unsigned int quadIndex = 0; quadIndex < 4; ++quadIndex)
264 {
265 unsigned int baseOffset = quadIndex * 3;
266
267 int quadx = static_cast<int>(((mInstances[baseOffset + 0]) * 0.5f + 0.5f) * getWindowWidth());
268 int quady = static_cast<int>(((mInstances[baseOffset + 1]) * 0.5f + 0.5f) * getWindowHeight());
269
270 EXPECT_PIXEL_EQ(quadx, quady, 255, 0, 0, 255);
271 }
272 }
273
274 // Loaded entry points
275 PFNGLVERTEXATTRIBDIVISORANGLEPROC mVertexAttribDivisorANGLE;
276 PFNGLDRAWARRAYSINSTANCEDANGLEPROC mDrawArraysInstancedANGLE;
277 PFNGLDRAWELEMENTSINSTANCEDANGLEPROC mDrawElementsInstancedANGLE;
278
279 // Vertex data
280 std::vector<GLfloat> mQuadVertices;
281 std::vector<GLfloat> mNonIndexedVertices;
282 std::vector<GLfloat> mTexcoords;
283 std::vector<GLfloat> mInstances;
284 std::vector<GLushort> mIndices;
285
286 const GLfloat quadRadius = 0.30f;
287
288 GLuint mProgram;
289 GLuint mVertexBuffer;
290 };
291
292 class InstancingTestAllConfigs : public InstancingTest
293 {
294 protected:
InstancingTestAllConfigs()295 InstancingTestAllConfigs() {}
296 };
297
298 class InstancingTestNo9_3 : public InstancingTest
299 {
300 protected:
InstancingTestNo9_3()301 InstancingTestNo9_3() {}
302 };
303
304 class InstancingTestPoints : public InstancingTest
305 {
306 protected:
InstancingTestPoints()307 InstancingTestPoints() {}
308 };
309
310 // This test uses a vertex shader with the first attribute (attribute zero) instanced.
311 // On D3D9 and D3D11 FL9_3, this triggers a special codepath that rearranges the input layout sent to D3D,
312 // to ensure that slot/stream zero of the input layout doesn't contain per-instance data.
TEST_P(InstancingTestAllConfigs,AttributeZeroInstanced)313 TEST_P(InstancingTestAllConfigs, AttributeZeroInstanced)
314 {
315 const std::string vs = SHADER_SOURCE
316 (
317 attribute vec3 a_instancePos;
318 attribute vec3 a_position;
319 void main()
320 {
321 gl_Position = vec4(a_position.xyz + a_instancePos.xyz, 1.0);
322 }
323 );
324
325 runDrawElementsTest(vs, true);
326 }
327
328 // Same as AttributeZeroInstanced, but attribute zero is not instanced.
329 // This ensures the general instancing codepath (i.e. without rearranging the input layout) works as expected.
TEST_P(InstancingTestAllConfigs,AttributeZeroNotInstanced)330 TEST_P(InstancingTestAllConfigs, AttributeZeroNotInstanced)
331 {
332 const std::string vs = SHADER_SOURCE
333 (
334 attribute vec3 a_position;
335 attribute vec3 a_instancePos;
336 void main()
337 {
338 gl_Position = vec4(a_position.xyz + a_instancePos.xyz, 1.0);
339 }
340 );
341
342 runDrawElementsTest(vs, false);
343 }
344
345 // Tests that the "first" parameter to glDrawArraysInstancedANGLE is only an offset into
346 // the non-instanced vertex attributes.
TEST_P(InstancingTestNo9_3,DrawArraysWithOffset)347 TEST_P(InstancingTestNo9_3, DrawArraysWithOffset)
348 {
349 const std::string vs = SHADER_SOURCE
350 (
351 attribute vec3 a_position;
352 attribute vec3 a_instancePos;
353 uniform vec3 u_offset;
354 void main()
355 {
356 gl_Position = vec4(a_position.xyz + a_instancePos.xyz + u_offset, 1.0);
357 }
358 );
359
360 setupDrawArraysTest(vs);
361
362 float offset1[3] = { 0, 0, 0 };
363 runDrawArraysTest(0, 6, 2, offset1);
364
365 float offset2[3] = { 0.0f, 1.0f, 0 };
366 runDrawArraysTest(6, 6, 2, offset2);
367
368 checkQuads();
369 }
370
371 // This test verifies instancing with GL_POINTS with glDrawArraysInstanced works.
372 // On D3D11 FL9_3, this triggers a special codepath that emulates instanced points rendering.
TEST_P(InstancingTestPoints,DrawArrays)373 TEST_P(InstancingTestPoints, DrawArrays)
374 {
375 // Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details
376 // On Win7, the D3D SDK Layers emits a false warning for these tests.
377 // This doesn't occur on Windows 10 (Version 1511) though.
378 ignoreD3D11SDKLayersWarnings();
379
380 setupInstancedPointsTest();
381
382 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
383 glBufferData(GL_ARRAY_BUFFER, mInstances.size() * sizeof(mInstances[0]), &mInstances[0],
384 GL_STATIC_DRAW);
385 glBindBuffer(GL_ARRAY_BUFFER, 0);
386
387 // Get the attribute locations
388 GLint positionLoc = glGetAttribLocation(mProgram, "a_position");
389 GLint instancePosLoc = glGetAttribLocation(mProgram, "a_instancePos");
390
391 // Load the vertex position
392 GLfloat pos[3] = {0, 0, 0};
393 glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, pos);
394 glEnableVertexAttribArray(positionLoc);
395
396 // Load the instance position
397 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
398 glVertexAttribPointer(instancePosLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
399 glBindBuffer(GL_ARRAY_BUFFER, 0);
400 glEnableVertexAttribArray(instancePosLoc);
401
402 // Enable instancing
403 mVertexAttribDivisorANGLE(instancePosLoc, 1);
404
405 // Do the instanced draw
406 mDrawArraysInstancedANGLE(GL_POINTS, 0, 1, static_cast<GLsizei>(mInstances.size()) / 3);
407
408 ASSERT_GL_NO_ERROR();
409
410 checkQuads();
411 }
412
413 // This test verifies instancing with GL_POINTS with glDrawElementsInstanced works.
414 // On D3D11 FL9_3, this triggers a special codepath that emulates instanced points rendering.
TEST_P(InstancingTestPoints,DrawElements)415 TEST_P(InstancingTestPoints, DrawElements)
416 {
417 // Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details
418 // On Win7, the D3D SDK Layers emits a false warning for these tests.
419 // This doesn't occur on Windows 10 (Version 1511) though.
420 ignoreD3D11SDKLayersWarnings();
421
422 setupInstancedPointsTest();
423
424 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
425 glBufferData(GL_ARRAY_BUFFER, mInstances.size() * sizeof(mInstances[0]), &mInstances[0],
426 GL_STATIC_DRAW);
427 glBindBuffer(GL_ARRAY_BUFFER, 0);
428
429 // Get the attribute locations
430 GLint positionLoc = glGetAttribLocation(mProgram, "a_position");
431 GLint instancePosLoc = glGetAttribLocation(mProgram, "a_instancePos");
432
433 // Load the vertex position
434 GLfloat pos[3] = {0, 0, 0};
435 glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, pos);
436 glEnableVertexAttribArray(positionLoc);
437
438 // Load the instance position
439 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
440 glVertexAttribPointer(instancePosLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
441 glBindBuffer(GL_ARRAY_BUFFER, 0);
442 glEnableVertexAttribArray(instancePosLoc);
443
444 // Enable instancing
445 mVertexAttribDivisorANGLE(instancePosLoc, 1);
446
447 // Do the instanced draw
448 mDrawElementsInstancedANGLE(GL_POINTS, static_cast<GLsizei>(mIndices.size()), GL_UNSIGNED_SHORT,
449 mIndices.data(), static_cast<GLsizei>(mInstances.size()) / 3);
450
451 ASSERT_GL_NO_ERROR();
452
453 checkQuads();
454 }
455
456 // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
457 // We test on D3D9 and D3D11 9_3 because they use special codepaths when attribute zero is instanced, unlike D3D11.
458 ANGLE_INSTANTIATE_TEST(InstancingTestAllConfigs,
459 ES2_D3D9(),
460 ES2_D3D11(),
461 ES2_D3D11_FL9_3(),
462 ES2_OPENGL(),
463 ES2_OPENGLES());
464
465 // TODO(jmadill): Figure out the situation with DrawInstanced on FL 9_3
466 ANGLE_INSTANTIATE_TEST(InstancingTestNo9_3, ES2_D3D9(), ES2_D3D11());
467
468 ANGLE_INSTANTIATE_TEST(InstancingTestPoints, ES2_D3D11(), ES2_D3D11_FL9_3());
469