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 #include "test_utils/gl_raii.h"
9
10 using namespace angle;
11
12 namespace
13 {
14 enum Geometry
15 {
16 Quad,
17 Point,
18 TriFan,
19 };
20 enum Storage
21 {
22 Buffer,
23 Memory
24 };
25 enum Draw
26 {
27 Indexed,
28 NonIndexed
29 };
30 enum Vendor
31 {
32 Angle,
33 Ext
34 };
35 } // namespace
36
37 class InstancingTest : public ANGLETest
38 {
39 protected:
InstancingTest()40 InstancingTest()
41 {
42 setWindowWidth(256);
43 setWindowHeight(256);
44 setConfigRedBits(8);
45 setConfigGreenBits(8);
46 setConfigBlueBits(8);
47 setConfigAlphaBits(8);
48 }
49
testTearDown()50 void testTearDown() override
51 {
52 glDeleteBuffers(1, &mInstanceBuffer);
53 glDeleteProgram(mProgram[0]);
54 glDeleteProgram(mProgram[1]);
55 }
56
testSetUp()57 void testSetUp() override
58 {
59 for (unsigned i = 0; i < kMaxDrawn; ++i)
60 {
61 mInstanceData[i] = i * kDrawSize;
62 }
63 glGenBuffers(1, &mInstanceBuffer);
64 glBindBuffer(GL_ARRAY_BUFFER, mInstanceBuffer);
65 glBufferData(GL_ARRAY_BUFFER, sizeof(mInstanceData), mInstanceData, GL_STATIC_DRAW);
66 glBindBuffer(GL_ARRAY_BUFFER, 0);
67
68 const std::string inst = "attribute float a_instance;";
69 const std::string pos = "attribute vec2 a_position;";
70 const std::string main = R"(
71 void main()
72 {
73 gl_PointSize = 6.0;
74 gl_Position = vec4(a_position.x, a_position.y + a_instance, 0, 1);
75 }
76 )";
77
78 // attrib 0 is instanced
79 const std::string inst0 = inst + pos + main;
80 mProgram[0] = CompileProgram(inst0.c_str(), essl1_shaders::fs::Red());
81 ASSERT_NE(0u, mProgram[0]);
82 ASSERT_EQ(0, glGetAttribLocation(mProgram[0], "a_instance"));
83 ASSERT_EQ(1, glGetAttribLocation(mProgram[0], "a_position"));
84
85 // attrib 1 is instanced
86 const std::string inst1 = pos + inst + main;
87 mProgram[1] = CompileProgram(inst1.c_str(), essl1_shaders::fs::Red());
88 ASSERT_NE(0u, mProgram[1]);
89 ASSERT_EQ(1, glGetAttribLocation(mProgram[1], "a_instance"));
90 ASSERT_EQ(0, glGetAttribLocation(mProgram[1], "a_position"));
91
92 glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
93 }
94
runTest(unsigned numInstance,unsigned divisor,const int instanceAttrib,Geometry geometry,Draw draw,Storage storage,Vendor vendor,unsigned offset)95 void runTest(unsigned numInstance,
96 unsigned divisor,
97 const int instanceAttrib, // which attrib is instanced: 0 or 1
98 Geometry geometry,
99 Draw draw,
100 Storage storage,
101 Vendor vendor,
102 unsigned offset) // for NonIndexed/DrawArrays only
103 {
104 if (vendor == Angle)
105 {
106 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_instanced_arrays"));
107 }
108 else if (vendor == Ext)
109 {
110 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_instanced_arrays"));
111 }
112
113 // TODO: Fix these. http://anglebug.com/3129
114 ANGLE_SKIP_TEST_IF(IsD3D9() && draw == Indexed && geometry == Point);
115 ANGLE_SKIP_TEST_IF(IsD3D9() && IsAMD());
116
117 // The window is divided into kMaxDrawn slices of size kDrawSize.
118 // The slice drawn into is determined by the instance datum.
119 // The instance data array selects all the slices in order.
120 // 'lastDrawn' is the index (zero-based) of the last slice into which we draw.
121 const unsigned lastDrawn = (numInstance - 1) / divisor;
122
123 // Ensure the numInstance and divisor parameters are valid.
124 ASSERT_TRUE(lastDrawn < kMaxDrawn);
125
126 ASSERT_TRUE(instanceAttrib == 0 || instanceAttrib == 1);
127 const int positionAttrib = 1 - instanceAttrib;
128
129 glUseProgram(mProgram[instanceAttrib]);
130
131 glBindBuffer(GL_ARRAY_BUFFER, storage == Buffer ? mInstanceBuffer : 0);
132 glVertexAttribPointer(instanceAttrib, 1, GL_FLOAT, GL_FALSE, 0,
133 storage == Buffer ? nullptr : mInstanceData);
134 glEnableVertexAttribArray(instanceAttrib);
135 if (vendor == Angle)
136 glVertexAttribDivisorANGLE(instanceAttrib, divisor);
137 else if (vendor == Ext)
138 glVertexAttribDivisorEXT(instanceAttrib, divisor);
139
140 glBindBuffer(GL_ARRAY_BUFFER, 0);
141 const void *vertices;
142 switch (geometry)
143 {
144 case Point:
145 vertices = kPointVertices;
146 break;
147 case Quad:
148 vertices = kQuadVertices;
149 break;
150 case TriFan:
151 vertices = kTriFanVertices;
152 break;
153 }
154 glVertexAttribPointer(positionAttrib, 2, GL_FLOAT, GL_FALSE, 0, vertices);
155 glEnableVertexAttribArray(positionAttrib);
156 if (vendor == Angle)
157 glVertexAttribDivisorANGLE(positionAttrib, 0);
158 else if (vendor == Ext)
159 glVertexAttribDivisorEXT(positionAttrib, 0);
160
161 glClear(GL_COLOR_BUFFER_BIT);
162
163 if (geometry == Point)
164 {
165 if (draw == Indexed)
166 if (vendor == Angle)
167 glDrawElementsInstancedANGLE(GL_POINTS, ArraySize(kPointIndices),
168 GL_UNSIGNED_SHORT, kPointIndices, numInstance);
169 else
170 glDrawElementsInstancedEXT(GL_POINTS, ArraySize(kPointIndices),
171 GL_UNSIGNED_SHORT, kPointIndices, numInstance);
172 else if (vendor == Angle)
173 glDrawArraysInstancedANGLE(GL_POINTS, offset, 4 /*vertices*/, numInstance);
174 else
175 glDrawArraysInstancedEXT(GL_POINTS, offset, 4 /*vertices*/, numInstance);
176 }
177 else if (geometry == Quad)
178 {
179 if (draw == Indexed)
180 if (vendor == Angle)
181 glDrawElementsInstancedANGLE(GL_TRIANGLES, ArraySize(kQuadIndices),
182 GL_UNSIGNED_SHORT, kQuadIndices, numInstance);
183 else
184 glDrawElementsInstancedEXT(GL_TRIANGLES, ArraySize(kQuadIndices),
185 GL_UNSIGNED_SHORT, kQuadIndices, numInstance);
186 else if (vendor == Angle)
187 glDrawArraysInstancedANGLE(GL_TRIANGLES, offset, 6 /*vertices*/, numInstance);
188 else
189 glDrawArraysInstancedEXT(GL_TRIANGLES, offset, 6 /*vertices*/, numInstance);
190 }
191 else if (geometry == TriFan)
192 {
193 if (draw == Indexed)
194 {
195 if (storage == Buffer)
196 {
197 GLBuffer indexBuffer;
198 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
199 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(kTriFanIndices), kTriFanIndices,
200 GL_STATIC_DRAW);
201
202 if (vendor == Angle)
203 glDrawElementsInstancedANGLE(GL_TRIANGLE_FAN, ArraySize(kTriFanIndices),
204 GL_UNSIGNED_BYTE, 0, numInstance);
205 else
206 glDrawElementsInstancedEXT(GL_TRIANGLE_FAN, ArraySize(kTriFanIndices),
207 GL_UNSIGNED_BYTE, 0, numInstance);
208 }
209 else
210 {
211 if (vendor == Angle)
212 glDrawElementsInstancedANGLE(GL_TRIANGLE_FAN, ArraySize(kTriFanIndices),
213 GL_UNSIGNED_BYTE, kTriFanIndices, numInstance);
214 else
215 glDrawElementsInstancedEXT(GL_TRIANGLE_FAN, ArraySize(kTriFanIndices),
216 GL_UNSIGNED_BYTE, kTriFanIndices, numInstance);
217 }
218 }
219 else if (vendor == Angle)
220 glDrawArraysInstancedANGLE(GL_TRIANGLE_FAN, offset, 8 /*vertices*/, numInstance);
221 else
222 glDrawArraysInstancedEXT(GL_TRIANGLE_FAN, offset, 8 /*vertices*/, numInstance);
223 }
224
225 ASSERT_GL_NO_ERROR();
226 checkDrawing(lastDrawn);
227 }
228
checkDrawing(unsigned lastDrawn)229 void checkDrawing(unsigned lastDrawn)
230 {
231 for (unsigned i = 0; i < kMaxDrawn; ++i)
232 {
233 float y =
234 -1.0f + static_cast<float>(kDrawSize) / 2.0f + static_cast<float>(i * kDrawSize);
235 int iy = static_cast<int>((y + 1.0f) / 2.0f * getWindowHeight());
236 for (unsigned j = 0; j < 8; j += 2)
237 {
238 int ix = static_cast<int>((kPointVertices[j] + 1.0f) / 2.0f * getWindowWidth());
239 EXPECT_PIXEL_COLOR_EQ(ix, iy, i <= lastDrawn ? GLColor::red : GLColor::blue)
240 << std::endl;
241 }
242 }
243 }
244
245 GLuint mProgram[2];
246 GLuint mInstanceBuffer;
247
248 static constexpr unsigned kMaxDrawn = 16;
249 static constexpr float kDrawSize = 2.0 / kMaxDrawn;
250 GLfloat mInstanceData[kMaxDrawn];
251
252 // clang-format off
253
254 // Vertices 0-5 are two triangles that form a quad filling the first "slice" of the window.
255 // See above about slices. Vertices 4-9 are the same two triangles.
256 static constexpr GLfloat kQuadVertices[] = {
257 -1, -1,
258 1, -1,
259 -1, -1 + kDrawSize,
260 1, -1,
261 1, -1 + kDrawSize,
262 -1, -1 + kDrawSize,
263 1, -1,
264 1, -1,
265 -1, -1 + kDrawSize,
266 -1, -1,
267 };
268
269 // Vertices 0-7 form a quad (triangle fan) filling the first "slice" of the window.
270 // Vertices 8-15 are the same.
271 static constexpr GLfloat kTriFanVertices[] = {
272 -1, -1,
273 1, -1,
274 1, -1 + 0.5f * kDrawSize,
275 1, -1 + kDrawSize,
276 0.5f, -1 + kDrawSize,
277 0, -1 + kDrawSize,
278 -0.5f, -1 + kDrawSize,
279 -1, -1 + kDrawSize,
280
281 -1, -1,
282 1, -1,
283 1, -1 + 0.5f * kDrawSize,
284 1, -1 + kDrawSize,
285 0.5f, -1 + kDrawSize,
286 0, -1 + kDrawSize,
287 -0.5f, -1 + kDrawSize,
288 -1, -1 + kDrawSize,
289 };
290
291 // Points 0-3 are spread across the first "slice."
292 // Points 2-4 are the same.
293 static constexpr GLfloat kPointVertices[] = {
294 -0.6f, -1 + kDrawSize / 2.0,
295 -0.2f, -1 + kDrawSize / 2.0,
296 0.2f, -1 + kDrawSize / 2.0,
297 0.6f, -1 + kDrawSize / 2.0,
298 -0.2f, -1 + kDrawSize / 2.0,
299 -0.6f, -1 + kDrawSize / 2.0,
300 };
301 // clang-format on
302
303 // Same two triangles as described above.
304 static constexpr GLushort kQuadIndices[] = {2, 9, 7, 5, 6, 4};
305
306 // Same triangle fan as described above.
307 static constexpr GLubyte kTriFanIndices[] = {0, 9, 10, 3, 4, 5, 14, 7};
308
309 // Same four points as described above.
310 static constexpr GLushort kPointIndices[] = {1, 5, 3, 2};
311 };
312
313 constexpr unsigned InstancingTest::kMaxDrawn;
314 constexpr float InstancingTest::kDrawSize;
315 constexpr GLfloat InstancingTest::kQuadVertices[];
316 constexpr GLfloat InstancingTest::kTriFanVertices[];
317 constexpr GLfloat InstancingTest::kPointVertices[];
318 constexpr GLushort InstancingTest::kQuadIndices[];
319 constexpr GLubyte InstancingTest::kTriFanIndices[];
320 constexpr GLushort InstancingTest::kPointIndices[];
321
322 #define TEST_INDEXED(attrib, geometry, storage, vendor) \
323 TEST_P(InstancingTest, IndexedAttrib##attrib##geometry##storage##vendor) \
324 { \
325 runTest(11, 2, attrib, geometry, Indexed, storage, vendor, 0); \
326 }
327
328 #define TEST_NONINDEXED(attrib, geometry, storage, vendor, offset) \
329 TEST_P(InstancingTest, NonIndexedAttrib##attrib##geometry##storage##vendor##Offset##offset) \
330 { \
331 runTest(11, 2, attrib, geometry, NonIndexed, storage, vendor, offset); \
332 }
333
334 #define TEST_DIVISOR(numInstance, divisor) \
335 TEST_P(InstancingTest, Instances##numInstance##Divisor##divisor) \
336 { \
337 runTest(numInstance, divisor, 1, Quad, NonIndexed, Buffer, Angle, 0); \
338 }
339
340 // D3D9 has a special codepath that rearranges the input layout sent to D3D,
341 // to ensure that slot/stream zero of the input layout doesn't contain per-instance data, so
342 // we test with attribute 0 being instanced, as will as attribute 1 being instanced.
343 //
344 // Tests with a non-zero 'offset' check that "first" parameter to glDrawArraysInstancedANGLE is only
345 // an offset into the non-instanced vertex attributes.
346 TEST_INDEXED(0, TriFan, Buffer, Angle)
347 TEST_INDEXED(0, TriFan, Memory, Angle)
348 TEST_INDEXED(1, TriFan, Buffer, Angle)
349 TEST_INDEXED(1, TriFan, Memory, Angle)
350 TEST_INDEXED(0, TriFan, Buffer, Ext)
351 TEST_INDEXED(0, TriFan, Memory, Ext)
352 TEST_INDEXED(1, TriFan, Buffer, Ext)
353 TEST_INDEXED(1, TriFan, Memory, Ext)
354 TEST_INDEXED(0, Quad, Buffer, Angle)
355 TEST_INDEXED(0, Quad, Memory, Angle)
356 TEST_INDEXED(1, Quad, Buffer, Angle)
357 TEST_INDEXED(1, Quad, Memory, Angle)
358 TEST_INDEXED(0, Point, Buffer, Angle)
359 TEST_INDEXED(0, Point, Memory, Angle)
360 TEST_INDEXED(1, Point, Buffer, Angle)
361 TEST_INDEXED(1, Point, Memory, Angle)
362 TEST_INDEXED(0, Quad, Buffer, Ext)
363 TEST_INDEXED(0, Quad, Memory, Ext)
364 TEST_INDEXED(1, Quad, Buffer, Ext)
365 TEST_INDEXED(1, Quad, Memory, Ext)
366 TEST_INDEXED(0, Point, Buffer, Ext)
367 TEST_INDEXED(0, Point, Memory, Ext)
368 TEST_INDEXED(1, Point, Buffer, Ext)
369 TEST_INDEXED(1, Point, Memory, Ext)
370
371 // offset should be 0 or 4 for quads, 0 or 8 for triangle fan
372 TEST_NONINDEXED(0, TriFan, Buffer, Angle, 0)
373 TEST_NONINDEXED(0, TriFan, Buffer, Angle, 8)
374 TEST_NONINDEXED(0, TriFan, Memory, Angle, 0)
375 TEST_NONINDEXED(0, TriFan, Memory, Angle, 8)
376 TEST_NONINDEXED(1, TriFan, Buffer, Angle, 0)
377 TEST_NONINDEXED(1, TriFan, Buffer, Angle, 8)
378 TEST_NONINDEXED(1, TriFan, Memory, Angle, 0)
379 TEST_NONINDEXED(1, TriFan, Memory, Angle, 8)
380 TEST_NONINDEXED(0, TriFan, Buffer, Ext, 0)
381 TEST_NONINDEXED(0, TriFan, Buffer, Ext, 8)
382 TEST_NONINDEXED(0, TriFan, Memory, Ext, 0)
383 TEST_NONINDEXED(0, TriFan, Memory, Ext, 8)
384 TEST_NONINDEXED(1, TriFan, Buffer, Ext, 0)
385 TEST_NONINDEXED(1, TriFan, Buffer, Ext, 8)
386 TEST_NONINDEXED(1, TriFan, Memory, Ext, 0)
387 TEST_NONINDEXED(1, TriFan, Memory, Ext, 8)
388 TEST_NONINDEXED(0, Quad, Buffer, Angle, 0)
389 TEST_NONINDEXED(0, Quad, Buffer, Angle, 4)
390 TEST_NONINDEXED(0, Quad, Memory, Angle, 0)
391 TEST_NONINDEXED(0, Quad, Memory, Angle, 4)
392 TEST_NONINDEXED(1, Quad, Buffer, Angle, 0)
393 TEST_NONINDEXED(1, Quad, Buffer, Angle, 4)
394 TEST_NONINDEXED(1, Quad, Memory, Angle, 0)
395 TEST_NONINDEXED(1, Quad, Memory, Angle, 4)
396 TEST_NONINDEXED(0, Quad, Buffer, Ext, 0)
397 TEST_NONINDEXED(0, Quad, Buffer, Ext, 4)
398 TEST_NONINDEXED(0, Quad, Memory, Ext, 0)
399 TEST_NONINDEXED(0, Quad, Memory, Ext, 4)
400 TEST_NONINDEXED(1, Quad, Buffer, Ext, 0)
401 TEST_NONINDEXED(1, Quad, Buffer, Ext, 4)
402 TEST_NONINDEXED(1, Quad, Memory, Ext, 0)
403 TEST_NONINDEXED(1, Quad, Memory, Ext, 4)
404
405 // offset should be 0 or 2 for points
406 TEST_NONINDEXED(0, Point, Buffer, Angle, 0)
407 TEST_NONINDEXED(0, Point, Buffer, Angle, 2)
408 TEST_NONINDEXED(0, Point, Memory, Angle, 0)
409 TEST_NONINDEXED(0, Point, Memory, Angle, 2)
410 TEST_NONINDEXED(1, Point, Buffer, Angle, 0)
411 TEST_NONINDEXED(1, Point, Buffer, Angle, 2)
412 TEST_NONINDEXED(1, Point, Memory, Angle, 0)
413 TEST_NONINDEXED(1, Point, Memory, Angle, 2)
414 TEST_NONINDEXED(0, Point, Buffer, Ext, 0)
415 TEST_NONINDEXED(0, Point, Buffer, Ext, 2)
416 TEST_NONINDEXED(0, Point, Memory, Ext, 0)
417 TEST_NONINDEXED(0, Point, Memory, Ext, 2)
418 TEST_NONINDEXED(1, Point, Buffer, Ext, 0)
419 TEST_NONINDEXED(1, Point, Buffer, Ext, 2)
420 TEST_NONINDEXED(1, Point, Memory, Ext, 0)
421 TEST_NONINDEXED(1, Point, Memory, Ext, 2)
422
423 // The following tests produce each value of 'lastDrawn' in runTest() from 1 to kMaxDrawn, a few
424 // different ways.
425 TEST_DIVISOR(1, 1)
426 TEST_DIVISOR(1, 2)
427 TEST_DIVISOR(2, 1)
428 TEST_DIVISOR(3, 1)
429 TEST_DIVISOR(3, 2)
430 TEST_DIVISOR(4, 1)
431 TEST_DIVISOR(5, 1)
432 TEST_DIVISOR(5, 2)
433 TEST_DIVISOR(6, 1)
434 TEST_DIVISOR(6, 2)
435 TEST_DIVISOR(7, 1)
436 TEST_DIVISOR(7, 2)
437 TEST_DIVISOR(8, 1)
438 TEST_DIVISOR(8, 2)
439 TEST_DIVISOR(8, 4)
440 TEST_DIVISOR(9, 1)
441 TEST_DIVISOR(9, 2)
442 TEST_DIVISOR(10, 1)
443 TEST_DIVISOR(11, 1)
444 TEST_DIVISOR(11, 2)
445 TEST_DIVISOR(12, 1)
446 TEST_DIVISOR(12, 11)
447 TEST_DIVISOR(13, 1)
448 TEST_DIVISOR(13, 2)
449 TEST_DIVISOR(14, 1)
450 TEST_DIVISOR(15, 1)
451 TEST_DIVISOR(15, 2)
452 TEST_DIVISOR(16, 1)
453 TEST_DIVISOR(16, 3)
454 TEST_DIVISOR(16, 7)
455 TEST_DIVISOR(17, 2)
456 TEST_DIVISOR(20, 2)
457 TEST_DIVISOR(21, 2)
458 TEST_DIVISOR(23, 2)
459 TEST_DIVISOR(25, 5)
460 TEST_DIVISOR(25, 33)
461 TEST_DIVISOR(26, 2)
462 TEST_DIVISOR(26, 3)
463 TEST_DIVISOR(27, 2)
464 TEST_DIVISOR(27, 4)
465 TEST_DIVISOR(28, 3)
466 TEST_DIVISOR(29, 2)
467 TEST_DIVISOR(29, 11)
468 TEST_DIVISOR(30, 4)
469 TEST_DIVISOR(31, 6)
470 TEST_DIVISOR(32, 2)
471 TEST_DIVISOR(32, 3)
472 TEST_DIVISOR(32, 8)
473 TEST_DIVISOR(34, 3)
474 TEST_DIVISOR(34, 30)
475
476 // Test line loop instanced draw
TEST_P(InstancingTest,LineLoop)477 TEST_P(InstancingTest, LineLoop)
478 {
479 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_instanced_arrays"));
480
481 constexpr char kVS[] = R"(
482 attribute vec2 a_position;
483 // x,y = offset, z = scale
484 attribute vec3 a_transform;
485
486 attribute vec4 a_color;
487
488 varying vec4 v_color;
489
490 invariant gl_Position;
491 void main()
492 {
493 vec2 v_position = a_transform.z * a_position + a_transform.xy;
494 gl_Position = vec4(v_position, 0.0, 1.0);
495
496 v_color = a_color;
497 })";
498
499 constexpr char kFS[] = R"(
500 precision highp float;
501 varying vec4 v_color;
502 void main()
503 {
504 gl_FragColor = v_color;
505 })";
506
507 ANGLE_GL_PROGRAM(program, kVS, kFS);
508 glBindAttribLocation(program, 0, "a_position");
509 glBindAttribLocation(program, 1, "a_transform");
510 glBindAttribLocation(program, 2, "a_color");
511 glLinkProgram(program);
512 glUseProgram(program);
513 ASSERT_GL_NO_ERROR();
514
515 constexpr GLfloat vertices[] = {
516 0.1, 0.1, -0.1, 0.1, -0.1, -0.1, 0.1, -0.1,
517 };
518
519 constexpr GLfloat transform[] = {
520 0, 0, 9, 0.2, 0.1, 2, 0.5, -0.2, 3, -0.8, -0.5, 1, -0.4, 0.4, 6,
521 };
522
523 constexpr GLsizei instances = ArraySize(transform) / 3;
524
525 const GLfloat colors[instances * 3] = {
526 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0,
527 };
528
529 constexpr GLushort lineloopAsStripIndices[] = {0, 1, 2, 3, 0};
530
531 std::vector<GLColor> expectedPixels(getWindowWidth() * getWindowHeight());
532
533 // Draw in non-instanced way
534 glClearColor(0, 0, 0, 1);
535 glClear(GL_COLOR_BUFFER_BIT);
536
537 glEnableVertexAttribArray(0);
538 glDisableVertexAttribArray(1);
539
540 glVertexAttribDivisorANGLE(0, 0);
541
542 glBindBuffer(GL_ARRAY_BUFFER, 0);
543 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
544 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, vertices);
545
546 for (size_t i = 0; i < instances; ++i)
547 {
548 glVertexAttrib3fv(1, transform + 3 * i);
549 glVertexAttrib3fv(2, colors + 3 * i);
550
551 glDrawElements(GL_LINE_STRIP, ArraySize(lineloopAsStripIndices), GL_UNSIGNED_SHORT,
552 lineloopAsStripIndices);
553 }
554
555 glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
556 expectedPixels.data());
557 ASSERT_GL_NO_ERROR();
558
559 // Draw in instanced way:
560 glClear(GL_COLOR_BUFFER_BIT);
561
562 GLBuffer vertexBuffer[3];
563 GLBuffer indexBuffer;
564
565 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
566 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(lineloopAsStripIndices), lineloopAsStripIndices,
567 GL_STATIC_DRAW);
568
569 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[0]);
570 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
571 glEnableVertexAttribArray(0);
572 glVertexAttribDivisorANGLE(0, 0);
573 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
574
575 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[1]);
576 glBufferData(GL_ARRAY_BUFFER, sizeof(transform), transform, GL_STATIC_DRAW);
577 glEnableVertexAttribArray(1);
578 glVertexAttribDivisorANGLE(1, 1);
579 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);
580
581 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer[2]);
582 glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
583 glEnableVertexAttribArray(2);
584 glVertexAttribDivisorANGLE(2, 1);
585 glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
586
587 glDrawArraysInstancedANGLE(GL_LINE_LOOP, 0, ArraySize(vertices) / 2, instances);
588
589 std::vector<GLColor> actualPixels(getWindowWidth() * getWindowHeight());
590 glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
591 actualPixels.data());
592 EXPECT_EQ(expectedPixels, actualPixels);
593
594 glClear(GL_COLOR_BUFFER_BIT);
595 glDrawElementsInstancedANGLE(GL_LINE_LOOP, ArraySize(lineloopAsStripIndices) - 1,
596 GL_UNSIGNED_SHORT, 0, instances);
597
598 glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE,
599 actualPixels.data());
600 EXPECT_EQ(expectedPixels, actualPixels);
601 }
602
603 class InstancingTestES3 : public InstancingTest
604 {
605 public:
InstancingTestES3()606 InstancingTestES3() {}
607 };
608
609 class InstancingTestES31 : public InstancingTest
610 {
611 public:
InstancingTestES31()612 InstancingTestES31() {}
613 };
614
615 // Verify that VertexAttribDivisor can update both binding divisor and attribBinding.
TEST_P(InstancingTestES31,UpdateAttribBindingByVertexAttribDivisor)616 TEST_P(InstancingTestES31, UpdateAttribBindingByVertexAttribDivisor)
617 {
618 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ANGLE_instanced_arrays"));
619
620 glUseProgram(mProgram[0]);
621
622 // Get the attribute locations
623 GLint positionLoc = glGetAttribLocation(mProgram[0], "a_position");
624 GLint instancePosLoc = glGetAttribLocation(mProgram[0], "a_instance");
625 ASSERT_NE(-1, positionLoc);
626 ASSERT_NE(-1, instancePosLoc);
627 ASSERT_GL_NO_ERROR();
628
629 GLuint vao;
630 glGenVertexArrays(1, &vao);
631 glBindVertexArray(vao);
632
633 GLBuffer quadBuffer;
634 glBindBuffer(GL_ARRAY_BUFFER, quadBuffer);
635 glBufferData(GL_ARRAY_BUFFER, sizeof(kQuadVertices), kQuadVertices, GL_STATIC_DRAW);
636
637 const unsigned numInstance = 4;
638 const unsigned divisor = 1;
639 const unsigned lastDrawn = (numInstance - 1) / divisor;
640
641 // Ensure the numInstance and divisor parameters are valid.
642 ASSERT_TRUE(lastDrawn < kMaxDrawn);
643
644 // Set the formats by VertexAttribFormat
645 glVertexAttribFormat(positionLoc, 2, GL_FLOAT, GL_FALSE, 0);
646 glVertexAttribFormat(instancePosLoc, 1, GL_FLOAT, GL_FALSE, 0);
647 glEnableVertexAttribArray(positionLoc);
648 glEnableVertexAttribArray(instancePosLoc);
649
650 const GLint positionBinding = instancePosLoc;
651 const GLint instanceBinding = positionLoc;
652
653 // Load the vertex position into the binding indexed positionBinding (== instancePosLoc)
654 // Load the instance position into the binding indexed instanceBinding (== positionLoc)
655 glBindVertexBuffer(positionBinding, quadBuffer, 0, 2 * sizeof(kQuadVertices[0]));
656 glBindVertexBuffer(instanceBinding, mInstanceBuffer, 0, sizeof(mInstanceData[0]));
657
658 // The attribute indexed positionLoc is using the binding indexed positionBinding
659 // The attribute indexed instancePosLoc is using the binding indexed instanceBinding
660 glVertexAttribBinding(positionLoc, positionBinding);
661 glVertexAttribBinding(instancePosLoc, instanceBinding);
662
663 // Enable instancing on the binding indexed instanceBinding
664 glVertexBindingDivisor(instanceBinding, divisor);
665
666 // Do the first instanced draw
667 glClear(GL_COLOR_BUFFER_BIT);
668 glDrawElementsInstanced(GL_TRIANGLES, ArraySize(kQuadIndices), GL_UNSIGNED_SHORT, kQuadIndices,
669 numInstance);
670 checkDrawing(lastDrawn);
671
672 // Disable instancing.
673 glVertexBindingDivisor(instanceBinding, 0);
674
675 // Load the vertex position into the binding indexed positionLoc.
676 // Load the instance position into the binding indexed instancePosLoc.
677 glBindVertexBuffer(positionLoc, quadBuffer, 0, 2 * sizeof(kQuadVertices[0]));
678 glBindVertexBuffer(instancePosLoc, mInstanceBuffer, 0, sizeof(mInstanceData[0]));
679
680 // The attribute indexed positionLoc is using the binding indexed positionLoc.
681 glVertexAttribBinding(positionLoc, positionLoc);
682
683 // Call VertexAttribDivisor to both enable instancing on instancePosLoc and set the attribute
684 // indexed instancePosLoc using the binding indexed instancePosLoc.
685 glVertexAttribDivisor(instancePosLoc, divisor);
686
687 // Do the second instanced draw
688 glClear(GL_COLOR_BUFFER_BIT);
689 glDrawElementsInstanced(GL_TRIANGLES, ArraySize(kQuadIndices), GL_UNSIGNED_SHORT, kQuadIndices,
690 numInstance);
691 checkDrawing(lastDrawn);
692
693 glDeleteVertexArrays(1, &vao);
694 }
695
696 // Verify that a large divisor that also changes doesn't cause issues and renders correctly.
TEST_P(InstancingTestES3,LargeDivisor)697 TEST_P(InstancingTestES3, LargeDivisor)
698 {
699 // http://anglebug.com/4092
700 ANGLE_SKIP_TEST_IF(isSwiftshader());
701 constexpr char kVS[] = R"(#version 300 es
702 layout(location = 0) in vec4 a_position;
703 layout(location = 1) in vec4 a_color;
704 out vec4 v_color;
705 void main()
706 {
707 gl_Position = a_position;
708 gl_PointSize = 4.0f;
709 v_color = a_color;
710 })";
711
712 constexpr char kFS[] = R"(#version 300 es
713 precision highp float;
714 in vec4 v_color;
715 out vec4 my_FragColor;
716 void main()
717 {
718 my_FragColor = v_color;
719 })";
720
721 ANGLE_GL_PROGRAM(program, kVS, kFS);
722 glUseProgram(program);
723
724 glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
725
726 GLBuffer buf;
727 glBindBuffer(GL_ARRAY_BUFFER, buf);
728 std::vector<GLfloat> vertices;
729 for (size_t i = 0u; i < 4u; ++i)
730 {
731 vertices.push_back(0.0f + i * 0.25f);
732 vertices.push_back(0.0f);
733 vertices.push_back(0.0f);
734 vertices.push_back(1.0f);
735 }
736 glBufferData(GL_ARRAY_BUFFER, vertices.size() * 4u, vertices.data(), GL_DYNAMIC_DRAW);
737
738 glEnableVertexAttribArray(0);
739 glVertexAttribPointer(0, 4, GL_FLOAT, false, 0, nullptr);
740 ASSERT_GL_NO_ERROR();
741
742 GLBuffer colorBuf;
743 glBindBuffer(GL_ARRAY_BUFFER, colorBuf);
744
745 std::array<GLColor, 4> ubyteColors = {GLColor::red, GLColor::green};
746 std::vector<float> floatColors;
747 for (const GLColor &color : ubyteColors)
748 {
749 floatColors.push_back(color.R / 255.0f);
750 floatColors.push_back(color.G / 255.0f);
751 floatColors.push_back(color.B / 255.0f);
752 floatColors.push_back(color.A / 255.0f);
753 }
754 glBufferData(GL_ARRAY_BUFFER, floatColors.size() * 4u, floatColors.data(), GL_DYNAMIC_DRAW);
755
756 const GLuint kColorDivisor = 65536u * 2u;
757 glEnableVertexAttribArray(1);
758 glVertexAttribPointer(1, 4, GL_FLOAT, false, 0, nullptr);
759 glVertexAttribDivisor(1, kColorDivisor);
760
761 std::array<GLuint, 1u> indices = {0u};
762 std::array<GLuint, 3u> divisorsToTry = {256u, 65536u, 65536u * 2u};
763
764 for (GLuint divisorToTry : divisorsToTry)
765 {
766 glClear(GL_COLOR_BUFFER_BIT);
767 glVertexAttribDivisor(0, divisorToTry);
768
769 GLuint instanceCount = divisorToTry + 1u;
770 unsigned int pointsRendered = (instanceCount - 1u) / divisorToTry + 1u;
771
772 glDrawElementsInstanced(GL_POINTS, indices.size(), GL_UNSIGNED_INT, indices.data(),
773 instanceCount);
774 ASSERT_GL_NO_ERROR();
775
776 // Check that the intended number of points has been rendered.
777 for (unsigned int pointIndex = 0u; pointIndex < pointsRendered + 1u; ++pointIndex)
778 {
779 GLint pointx = static_cast<GLint>((pointIndex * 0.125f + 0.5f) * getWindowWidth());
780 GLint pointy = static_cast<GLint>(0.5f * getWindowHeight());
781
782 if (pointIndex < pointsRendered)
783 {
784 GLuint pointColorIndex = (pointIndex * divisorToTry) / kColorDivisor;
785 EXPECT_PIXEL_COLOR_EQ(pointx, pointy, ubyteColors[pointColorIndex]);
786 }
787 else
788 {
789 // Clear color.
790 EXPECT_PIXEL_COLOR_EQ(pointx, pointy, GLColor::blue);
791 }
792 }
793 }
794 }
795
796 // This is a regression test. If VertexAttribDivisor was returned as a signed integer, it would be
797 // incorrectly clamped down to the maximum signed integer.
TEST_P(InstancingTestES3,LargestDivisor)798 TEST_P(InstancingTestES3, LargestDivisor)
799 {
800 // http://anglebug.com/4092
801 ANGLE_SKIP_TEST_IF(isSwiftshader());
802 constexpr GLuint kLargeDivisor = std::numeric_limits<GLuint>::max();
803 glVertexAttribDivisor(0, kLargeDivisor);
804
805 GLuint divisor = 0;
806 glGetVertexAttribIuiv(0, GL_VERTEX_ATTRIB_ARRAY_DIVISOR, &divisor);
807 EXPECT_EQ(kLargeDivisor, divisor)
808 << "Vertex attrib divisor read was not the same that was passed in.";
809 }
810
811 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InstancingTestES3);
812 ANGLE_INSTANTIATE_TEST_ES3(InstancingTestES3);
813
814 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InstancingTestES31);
815 ANGLE_INSTANTIATE_TEST_ES31(InstancingTestES31);
816
817 ANGLE_INSTANTIATE_TEST_ES2(InstancingTest);
818