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 // DrawElementsTest:
7 //   Tests for indexed draws.
8 //
9 
10 #include "test_utils/ANGLETest.h"
11 #include "test_utils/gl_raii.h"
12 
13 using namespace angle;
14 
15 namespace
16 {
17 
18 class DrawElementsTest : public ANGLETest
19 {
20   protected:
DrawElementsTest()21     DrawElementsTest() : mProgram(0u)
22     {
23         setWindowWidth(64);
24         setWindowHeight(64);
25         setConfigRedBits(8);
26         setConfigGreenBits(8);
27         setConfigBlueBits(8);
28         setConfigAlphaBits(8);
29     }
30 
~DrawElementsTest()31     ~DrawElementsTest()
32     {
33         for (GLuint indexBuffer : mIndexBuffers)
34         {
35             if (indexBuffer != 0)
36             {
37                 glDeleteBuffers(1, &indexBuffer);
38             }
39         }
40 
41         for (GLuint vertexArray : mVertexArrays)
42         {
43             if (vertexArray != 0)
44             {
45                 glDeleteVertexArrays(1, &vertexArray);
46             }
47         }
48 
49         for (GLuint vertexBuffer : mVertexBuffers)
50         {
51             if (vertexBuffer != 0)
52             {
53                 glDeleteBuffers(1, &vertexBuffer);
54             }
55         }
56 
57         if (mProgram != 0u)
58         {
59             glDeleteProgram(mProgram);
60         }
61     }
62 
63     std::vector<GLuint> mIndexBuffers;
64     std::vector<GLuint> mVertexArrays;
65     std::vector<GLuint> mVertexBuffers;
66     GLuint mProgram;
67 };
68 
69 class WebGLDrawElementsTest : public DrawElementsTest
70 {
71   public:
WebGLDrawElementsTest()72     WebGLDrawElementsTest() { setWebGLCompatibilityEnabled(true); }
73 };
74 
75 // Test no error is generated when using client-side arrays, indices = nullptr and count = 0
TEST_P(DrawElementsTest,ClientSideNullptrArrayZeroCount)76 TEST_P(DrawElementsTest, ClientSideNullptrArrayZeroCount)
77 {
78     constexpr char kVS[] =
79         "attribute vec3 a_pos;\n"
80         "void main()\n"
81         "{\n"
82         "    gl_Position = vec4(a_pos, 1.0);\n"
83         "}\n";
84 
85     ANGLE_GL_PROGRAM(program, kVS, essl1_shaders::fs::Blue());
86 
87     GLint posLocation = glGetAttribLocation(program.get(), "a_pos");
88     ASSERT_NE(-1, posLocation);
89     glUseProgram(program.get());
90 
91     const auto &vertices = GetQuadVertices();
92 
93     GLBuffer vertexBuffer;
94     glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer.get());
95     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
96                  GL_STATIC_DRAW);
97 
98     glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
99     glEnableVertexAttribArray(posLocation);
100     ASSERT_GL_NO_ERROR();
101 
102     // "If drawElements is called with a count greater than zero, and no WebGLBuffer is bound to the
103     // ELEMENT_ARRAY_BUFFER binding point, an INVALID_OPERATION error is generated."
104     glDrawElements(GL_TRIANGLES, 1, GL_UNSIGNED_BYTE, nullptr);
105     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
106 
107     // count == 0 so it's fine to have no element array buffer bound.
108     glDrawElements(GL_TRIANGLES, 0, GL_UNSIGNED_BYTE, nullptr);
109     ASSERT_GL_NO_ERROR();
110 }
111 
112 // Test a state desync that can occur when using a streaming index buffer in GL in concert with
113 // deleting the applied index buffer.
TEST_P(DrawElementsTest,DeletingAfterStreamingIndexes)114 TEST_P(DrawElementsTest, DeletingAfterStreamingIndexes)
115 {
116     // Init program
117     constexpr char kVS[] =
118         "attribute vec2 position;\n"
119         "attribute vec2 testFlag;\n"
120         "varying vec2 v_data;\n"
121         "void main() {\n"
122         "  gl_Position = vec4(position, 0, 1);\n"
123         "  v_data = testFlag;\n"
124         "}";
125 
126     constexpr char kFS[] =
127         "varying highp vec2 v_data;\n"
128         "void main() {\n"
129         "  gl_FragColor = vec4(v_data, 0, 1);\n"
130         "}";
131 
132     mProgram = CompileProgram(kVS, kFS);
133     ASSERT_NE(0u, mProgram);
134     glUseProgram(mProgram);
135 
136     GLint positionLocation = glGetAttribLocation(mProgram, "position");
137     ASSERT_NE(-1, positionLocation);
138 
139     GLint testFlagLocation = glGetAttribLocation(mProgram, "testFlag");
140     ASSERT_NE(-1, testFlagLocation);
141 
142     mIndexBuffers.resize(3u);
143     glGenBuffers(3, &mIndexBuffers[0]);
144 
145     mVertexArrays.resize(2);
146     glGenVertexArrays(2, &mVertexArrays[0]);
147 
148     mVertexBuffers.resize(2);
149     glGenBuffers(2, &mVertexBuffers[0]);
150 
151     std::vector<GLuint> indexData[2];
152     indexData[0].push_back(0);
153     indexData[0].push_back(1);
154     indexData[0].push_back(2);
155     indexData[0].push_back(2);
156     indexData[0].push_back(3);
157     indexData[0].push_back(0);
158 
159     indexData[1] = indexData[0];
160     for (GLuint &item : indexData[1])
161     {
162         item += 4u;
163     }
164 
165     std::vector<GLfloat> positionData = {// quad verts
166                                          -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
167                                          // Repeat position data
168                                          -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
169 
170     std::vector<GLfloat> testFlagData = {// red
171                                          1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
172                                          // green
173                                          0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f};
174 
175     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffers[0]);
176     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indexData[0].size(), &indexData[0][0],
177                  GL_STATIC_DRAW);
178 
179     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffers[2]);
180     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indexData[0].size(), &indexData[0][0],
181                  GL_STATIC_DRAW);
182 
183     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffers[1]);
184     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indexData[1].size(), &indexData[1][0],
185                  GL_STATIC_DRAW);
186 
187     // Initialize first vertex array with second index buffer
188     glBindVertexArray(mVertexArrays[0]);
189 
190     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffers[1]);
191     glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffers[0]);
192     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * positionData.size(), &positionData[0],
193                  GL_STATIC_DRAW);
194     glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
195     glEnableVertexAttribArray(positionLocation);
196 
197     glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffers[1]);
198     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * testFlagData.size(), &testFlagData[0],
199                  GL_STATIC_DRAW);
200     glVertexAttribPointer(testFlagLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
201     glEnableVertexAttribArray(testFlagLocation);
202 
203     // Initialize second vertex array with first index buffer
204     glBindVertexArray(mVertexArrays[1]);
205 
206     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffers[0]);
207 
208     glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffers[0]);
209     glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
210     glEnableVertexAttribArray(positionLocation);
211 
212     glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffers[1]);
213     glVertexAttribPointer(testFlagLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
214     glEnableVertexAttribArray(testFlagLocation);
215 
216     ASSERT_GL_NO_ERROR();
217 
218     glBindVertexArray(mVertexArrays[0]);
219     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
220     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
221 
222     glBindVertexArray(mVertexArrays[1]);
223     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
224     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
225 
226     glBindVertexArray(mVertexArrays[0]);
227     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
228     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
229 
230     // Trigger the bug here.
231     glDeleteBuffers(1, &mIndexBuffers[2]);
232 
233     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
234     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
235 
236     ASSERT_GL_NO_ERROR();
237 }
238 
239 // Test drawing to part of the indices in an index buffer, and then all of them.
TEST_P(DrawElementsTest,PartOfIndexBufferThenAll)240 TEST_P(DrawElementsTest, PartOfIndexBufferThenAll)
241 {
242     // Init program
243     constexpr char kVS[] =
244         "attribute vec2 position;\n"
245         "attribute vec2 testFlag;\n"
246         "varying vec2 v_data;\n"
247         "void main() {\n"
248         "  gl_Position = vec4(position, 0, 1);\n"
249         "  v_data = testFlag;\n"
250         "}";
251 
252     constexpr char kFS[] =
253         "varying highp vec2 v_data;\n"
254         "void main() {\n"
255         "  gl_FragColor = vec4(v_data, 0, 1);\n"
256         "}";
257 
258     mProgram = CompileProgram(kVS, kFS);
259     ASSERT_NE(0u, mProgram);
260     glUseProgram(mProgram);
261 
262     GLint positionLocation = glGetAttribLocation(mProgram, "position");
263     ASSERT_NE(-1, positionLocation);
264 
265     GLint testFlagLocation = glGetAttribLocation(mProgram, "testFlag");
266     ASSERT_NE(-1, testFlagLocation);
267 
268     mIndexBuffers.resize(1);
269     glGenBuffers(1, &mIndexBuffers[0]);
270 
271     mVertexArrays.resize(1);
272     glGenVertexArrays(1, &mVertexArrays[0]);
273 
274     mVertexBuffers.resize(2);
275     glGenBuffers(2, &mVertexBuffers[0]);
276 
277     std::vector<GLubyte> indexData[2];
278     indexData[0].push_back(0);
279     indexData[0].push_back(1);
280     indexData[0].push_back(2);
281     indexData[0].push_back(2);
282     indexData[0].push_back(3);
283     indexData[0].push_back(0);
284     indexData[0].push_back(4);
285     indexData[0].push_back(5);
286     indexData[0].push_back(6);
287     indexData[0].push_back(6);
288     indexData[0].push_back(7);
289     indexData[0].push_back(4);
290 
291     // Make a copy:
292     indexData[1] = indexData[0];
293 
294     std::vector<GLfloat> positionData = {// quad verts
295                                          -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
296                                          // Repeat position data
297                                          -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
298 
299     std::vector<GLfloat> testFlagData = {// red
300                                          1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
301                                          // green
302                                          0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f};
303 
304     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffers[0]);
305     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLubyte) * indexData[0].size(), &indexData[0][0],
306                  GL_STATIC_DRAW);
307 
308     glBindVertexArray(mVertexArrays[0]);
309 
310     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffers[0]);
311     glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffers[0]);
312     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * positionData.size(), &positionData[0],
313                  GL_STATIC_DRAW);
314     glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
315     glEnableVertexAttribArray(positionLocation);
316 
317     glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffers[1]);
318     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * testFlagData.size(), &testFlagData[0],
319                  GL_STATIC_DRAW);
320     glVertexAttribPointer(testFlagLocation, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nullptr);
321     glEnableVertexAttribArray(testFlagLocation);
322 
323     ASSERT_GL_NO_ERROR();
324 
325     // Draw with just the second set of 6 items, then first 6, and then the entire index buffer
326     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, reinterpret_cast<const void *>(6));
327     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
328 
329     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, nullptr);
330     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
331 
332     glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_BYTE, nullptr);
333     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
334 
335     // Reload the buffer again with a copy of the same data
336     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLubyte) * indexData[1].size(), &indexData[1][0],
337                  GL_STATIC_DRAW);
338 
339     // Draw with just the first 6 indices, and then with the entire index buffer
340     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, nullptr);
341     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
342 
343     glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_BYTE, nullptr);
344     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
345 
346     // Reload the buffer again with a copy of the same data
347     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLubyte) * indexData[0].size(), &indexData[0][0],
348                  GL_STATIC_DRAW);
349 
350     // This time, do not check color between draws (which causes a flush):
351     // Draw with just the second set of 6 items, then first 6, and then the entire index buffer
352     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, reinterpret_cast<const void *>(6));
353     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, nullptr);
354     glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_BYTE, nullptr);
355     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
356 
357     ASSERT_GL_NO_ERROR();
358 }
359 
360 // Test that the offset in the index buffer is forced to be a multiple of the element size
TEST_P(WebGLDrawElementsTest,DrawElementsTypeAlignment)361 TEST_P(WebGLDrawElementsTest, DrawElementsTypeAlignment)
362 {
363     constexpr char kVS[] =
364         "attribute vec3 a_pos;\n"
365         "void main()\n"
366         "{\n"
367         "    gl_Position = vec4(a_pos, 1.0);\n"
368         "}\n";
369 
370     ANGLE_GL_PROGRAM(program, kVS, essl1_shaders::fs::Blue());
371 
372     GLint posLocation = glGetAttribLocation(program, "a_pos");
373     ASSERT_NE(-1, posLocation);
374     glUseProgram(program);
375 
376     const auto &vertices = GetQuadVertices();
377 
378     GLBuffer vertexBuffer;
379     glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
380     glBufferData(GL_ARRAY_BUFFER, sizeof(vertices[0]) * vertices.size(), vertices.data(),
381                  GL_STATIC_DRAW);
382 
383     glVertexAttribPointer(posLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
384     glEnableVertexAttribArray(posLocation);
385 
386     GLBuffer indexBuffer;
387     const GLubyte indices1[] = {0, 0, 0, 0, 0, 0};
388     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
389     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices1), indices1, GL_STATIC_DRAW);
390 
391     ASSERT_GL_NO_ERROR();
392 
393     const char *zeroIndices = nullptr;
394 
395     glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices);
396     ASSERT_GL_NO_ERROR();
397 
398     const GLushort indices2[] = {0, 0, 0, 0, 0, 0};
399     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
400     glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices2), indices2, GL_STATIC_DRAW);
401 
402     glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, zeroIndices + 1);
403     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
404 }
405 
406 ANGLE_INSTANTIATE_TEST_ES3(DrawElementsTest);
407 ANGLE_INSTANTIATE_TEST_ES2(WebGLDrawElementsTest);
408 }  // namespace
409