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 // IndexBufferOffsetTest.cpp: Test glDrawElements with an offset and an index buffer
8
9 #include "test_utils/ANGLETest.h"
10 #include "test_utils/gl_raii.h"
11 #include "util/test_utils.h"
12
13 using namespace angle;
14
15 class IndexBufferOffsetTest : public ANGLETest
16 {
17 protected:
IndexBufferOffsetTest()18 IndexBufferOffsetTest()
19 {
20 setWindowWidth(128);
21 setWindowHeight(128);
22 setConfigRedBits(8);
23 setConfigGreenBits(8);
24 setConfigBlueBits(8);
25 setConfigAlphaBits(8);
26 }
27
testSetUp()28 void testSetUp() override
29 {
30 constexpr char kVS[] =
31 R"(precision highp float;
32 attribute vec2 position;
33
34 void main()
35 {
36 gl_Position = vec4(position, 0.0, 1.0);
37 })";
38
39 constexpr char kFS[] =
40 R"(precision highp float;
41 uniform vec4 color;
42
43 void main()
44 {
45 gl_FragColor = color;
46 })";
47
48 mProgram = CompileProgram(kVS, kFS);
49 ASSERT_NE(0u, mProgram);
50
51 mColorUniformLocation = glGetUniformLocation(mProgram, "color");
52 mPositionAttributeLocation = glGetAttribLocation(mProgram, "position");
53
54 const GLfloat vertices[] = {-1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f};
55 glGenBuffers(1, &mVertexBuffer);
56 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
57 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices[0], GL_STATIC_DRAW);
58
59 glGenBuffers(1, &mIndexBuffer);
60 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
61 }
62
testTearDown()63 void testTearDown() override
64 {
65 glDeleteBuffers(1, &mVertexBuffer);
66 glDeleteBuffers(1, &mIndexBuffer);
67 glDeleteProgram(mProgram);
68 }
69
preTestUpdateBuffer(GLuint framebuffer,GLuint texture,GLuint buffer,GLsizei size)70 void preTestUpdateBuffer(GLuint framebuffer, GLuint texture, GLuint buffer, GLsizei size)
71 {
72 GLsizei uboSize = std::max(size, 16);
73 const std::vector<uint32_t> initialData((uboSize + 3) / 4, 0x1234567u);
74
75 glBindTexture(GL_TEXTURE_2D, texture);
76 glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
77
78 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
79 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture,
80 0);
81
82 glBindBuffer(GL_UNIFORM_BUFFER, buffer);
83 glBufferData(GL_UNIFORM_BUFFER, uboSize, initialData.data(), GL_DYNAMIC_DRAW);
84 glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer);
85
86 constexpr char kVerifyUBO[] = R"(#version 300 es
87 precision mediump float;
88 uniform block {
89 uint data;
90 } ubo;
91 out vec4 colorOut;
92 void main()
93 {
94 if (ubo.data == 0x1234567u)
95 colorOut = vec4(0, 1.0, 0, 1.0);
96 else
97 colorOut = vec4(1.0, 0, 0, 1.0);
98 })";
99
100 ANGLE_GL_PROGRAM(verifyUbo, essl3_shaders::vs::Simple(), kVerifyUBO);
101
102 glDisable(GL_BLEND);
103 drawQuad(verifyUbo, essl3_shaders::PositionAttrib(), 0.5);
104
105 EXPECT_GL_NO_ERROR();
106
107 glBindFramebuffer(GL_FRAMEBUFFER, 0);
108 }
109
runTest(GLenum type,int typeWidth,void * indexDataIn,bool smallUpdates,bool useBuffersAsUboFirst)110 void runTest(GLenum type,
111 int typeWidth,
112 void *indexDataIn,
113 bool smallUpdates,
114 bool useBuffersAsUboFirst)
115 {
116 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
117 glClear(GL_COLOR_BUFFER_BIT);
118
119 size_t indexDataWidth = 6 * typeWidth;
120
121 std::vector<GLubyte> indexData(6 * 3 * sizeof(GLuint), 0);
122 memcpy(indexData.data() + indexDataWidth, indexDataIn, indexDataWidth);
123
124 GLFramebuffer elementUpdateFbo;
125 GLTexture elementUpdateTex;
126
127 if (useBuffersAsUboFirst)
128 {
129 preTestUpdateBuffer(elementUpdateFbo, elementUpdateTex, mIndexBuffer,
130 3 * indexDataWidth);
131 }
132 else
133 {
134 glBufferData(GL_ELEMENT_ARRAY_BUFFER, 3 * indexDataWidth, nullptr, GL_DYNAMIC_DRAW);
135 }
136
137 if (smallUpdates)
138 {
139 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indexDataWidth, indexData.data());
140 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, indexDataWidth, indexDataWidth,
141 indexData.data() + indexDataWidth);
142 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 2 * indexDataWidth, indexDataWidth,
143 indexData.data() + 2 * indexDataWidth);
144 }
145 else
146 {
147 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, 3 * indexDataWidth, indexData.data());
148 }
149
150 glUseProgram(mProgram);
151
152 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
153 glVertexAttribPointer(mPositionAttributeLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);
154 glEnableVertexAttribArray(mPositionAttributeLocation);
155
156 glUniform4f(mColorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
157
158 for (int i = 0; i < 16; i++)
159 {
160 glDrawElements(GL_TRIANGLES, 6, type, reinterpret_cast<void *>(indexDataWidth));
161 EXPECT_PIXEL_COLOR_EQ(64, 64, GLColor::red);
162 }
163
164 if (smallUpdates)
165 {
166 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, indexDataWidth, indexDataWidth,
167 indexData.data());
168 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 2 * indexDataWidth, indexDataWidth,
169 indexData.data() + indexDataWidth);
170 }
171 else
172 {
173 glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, indexDataWidth, 2 * indexDataWidth,
174 indexData.data());
175 }
176
177 glUniform4f(mColorUniformLocation, 0.0f, 1.0f, 0.0f, 1.0f);
178 glDrawElements(GL_TRIANGLES, 6, type, reinterpret_cast<void *>(indexDataWidth * 2));
179 EXPECT_PIXEL_COLOR_EQ(64, 64, GLColor::green);
180
181 if (useBuffersAsUboFirst)
182 {
183 glBindFramebuffer(GL_FRAMEBUFFER, elementUpdateFbo);
184 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
185 }
186
187 EXPECT_GL_NO_ERROR();
188 swapBuffers();
189 }
190
191 GLuint mProgram;
192 GLint mColorUniformLocation;
193 GLint mPositionAttributeLocation;
194 GLuint mVertexBuffer;
195 GLuint mIndexBuffer;
196 };
197
198 class IndexBufferOffsetTestES3 : public IndexBufferOffsetTest
199 {};
200
201 // Test using an offset for an UInt8 index buffer
TEST_P(IndexBufferOffsetTest,UInt8Index)202 TEST_P(IndexBufferOffsetTest, UInt8Index)
203 {
204 GLubyte indexData[] = {0, 1, 2, 1, 2, 3};
205 runTest(GL_UNSIGNED_BYTE, 1, indexData, false, false);
206 }
207
208 // Test using an offset for an UInt16 index buffer
TEST_P(IndexBufferOffsetTest,UInt16Index)209 TEST_P(IndexBufferOffsetTest, UInt16Index)
210 {
211 GLushort indexData[] = {0, 1, 2, 1, 2, 3};
212 runTest(GL_UNSIGNED_SHORT, 2, indexData, false, false);
213 }
214
215 // Test using an offset for an UInt32 index buffer
TEST_P(IndexBufferOffsetTest,UInt32Index)216 TEST_P(IndexBufferOffsetTest, UInt32Index)
217 {
218 ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 &&
219 !IsGLExtensionEnabled("GL_OES_element_index_uint"));
220
221 GLuint indexData[] = {0, 1, 2, 1, 2, 3};
222 runTest(GL_UNSIGNED_INT, 4, indexData, false, false);
223 }
224
225 // Test using an offset for an UInt8 index buffer with small buffer updates
TEST_P(IndexBufferOffsetTest,UInt8IndexSmallUpdates)226 TEST_P(IndexBufferOffsetTest, UInt8IndexSmallUpdates)
227 {
228 GLubyte indexData[] = {0, 1, 2, 1, 2, 3};
229 runTest(GL_UNSIGNED_BYTE, 1, indexData, true, false);
230 }
231
232 // Test using an offset for an UInt16 index buffer with small buffer updates
TEST_P(IndexBufferOffsetTest,UInt16IndexSmallUpdates)233 TEST_P(IndexBufferOffsetTest, UInt16IndexSmallUpdates)
234 {
235 GLushort indexData[] = {0, 1, 2, 1, 2, 3};
236 runTest(GL_UNSIGNED_SHORT, 2, indexData, true, false);
237 }
238
239 // Test using an offset for an UInt32 index buffer with small buffer updates
TEST_P(IndexBufferOffsetTest,UInt32IndexSmallUpdates)240 TEST_P(IndexBufferOffsetTest, UInt32IndexSmallUpdates)
241 {
242 ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 &&
243 !IsGLExtensionEnabled("GL_OES_element_index_uint"));
244
245 GLuint indexData[] = {0, 1, 2, 1, 2, 3};
246 runTest(GL_UNSIGNED_INT, 4, indexData, true, false);
247 }
248
249 // Test using an offset for an UInt8 index buffer after uploading data to a buffer that is in use
TEST_P(IndexBufferOffsetTestES3,UseAsUBOThenUpdateThenUInt8Index)250 TEST_P(IndexBufferOffsetTestES3, UseAsUBOThenUpdateThenUInt8Index)
251 {
252 // http://anglebug.com/5950
253 ANGLE_SKIP_TEST_IF(IsAMD() && IsVulkan() && IsWindows());
254
255 // http://anglebug.com/5957
256 ANGLE_SKIP_TEST_IF(IsVulkan() && (IsPixel2() || IsPixel2XL()));
257
258 GLubyte indexData[] = {0, 1, 2, 1, 2, 3};
259 runTest(GL_UNSIGNED_BYTE, 1, indexData, false, true);
260 }
261
262 // Test using an offset for an UInt16 index buffer after uploading data to a buffer that is in use
TEST_P(IndexBufferOffsetTestES3,UseAsUBOThenUpdateThenUInt16Index)263 TEST_P(IndexBufferOffsetTestES3, UseAsUBOThenUpdateThenUInt16Index)
264 {
265 // http://anglebug.com/5957
266 ANGLE_SKIP_TEST_IF(IsVulkan() && (IsPixel2() || IsPixel2XL()));
267
268 GLushort indexData[] = {0, 1, 2, 1, 2, 3};
269 runTest(GL_UNSIGNED_SHORT, 2, indexData, false, true);
270 }
271
272 // Test using an offset for an UInt32 index buffer after uploading data to a buffer that is in use
TEST_P(IndexBufferOffsetTestES3,UseAsUBOThenUpdateThenUInt32Index)273 TEST_P(IndexBufferOffsetTestES3, UseAsUBOThenUpdateThenUInt32Index)
274 {
275 ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 &&
276 !IsGLExtensionEnabled("GL_OES_element_index_uint"));
277
278 // http://anglebug.com/5957
279 ANGLE_SKIP_TEST_IF(IsVulkan() && (IsPixel2() || IsPixel2XL()));
280
281 GLuint indexData[] = {0, 1, 2, 1, 2, 3};
282 runTest(GL_UNSIGNED_INT, 4, indexData, false, true);
283 }
284
285 // Test using an offset for an UInt8 index buffer after uploading data to a buffer that is in use,
286 // with small buffer updates
TEST_P(IndexBufferOffsetTestES3,UseAsUBOThenUpdateThenUInt8IndexSmallUpdates)287 TEST_P(IndexBufferOffsetTestES3, UseAsUBOThenUpdateThenUInt8IndexSmallUpdates)
288 {
289 // http://anglebug.com/5950
290 ANGLE_SKIP_TEST_IF(IsAMD() && IsVulkan() && IsWindows());
291
292 // http://anglebug.com/5957
293 ANGLE_SKIP_TEST_IF(IsVulkan() && (IsPixel2() || IsPixel2XL()));
294
295 GLubyte indexData[] = {0, 1, 2, 1, 2, 3};
296 runTest(GL_UNSIGNED_BYTE, 1, indexData, true, true);
297 }
298
299 // Test using an offset for an UInt16 index buffer after uploading data to a buffer that is in use,
300 // with small buffer updates
TEST_P(IndexBufferOffsetTestES3,UseAsUBOThenUpdateThenUInt16IndexSmallUpdates)301 TEST_P(IndexBufferOffsetTestES3, UseAsUBOThenUpdateThenUInt16IndexSmallUpdates)
302 {
303 // http://anglebug.com/5957
304 ANGLE_SKIP_TEST_IF(IsVulkan() && (IsPixel2() || IsPixel2XL()));
305
306 GLushort indexData[] = {0, 1, 2, 1, 2, 3};
307 runTest(GL_UNSIGNED_SHORT, 2, indexData, true, true);
308 }
309
310 // Test using an offset for an UInt32 index buffer after uploading data to a buffer that is in use,
311 // with small buffer updates
TEST_P(IndexBufferOffsetTestES3,UseAsUBOThenUpdateThenUInt32IndexSmallUpdates)312 TEST_P(IndexBufferOffsetTestES3, UseAsUBOThenUpdateThenUInt32IndexSmallUpdates)
313 {
314 ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 &&
315 !IsGLExtensionEnabled("GL_OES_element_index_uint"));
316
317 // http://anglebug.com/5957
318 ANGLE_SKIP_TEST_IF(IsVulkan() && (IsPixel2() || IsPixel2XL()));
319
320 GLuint indexData[] = {0, 1, 2, 1, 2, 3};
321 runTest(GL_UNSIGNED_INT, 4, indexData, true, true);
322 }
323
324 // Uses index buffer offset and 2 drawElement calls one of the other, makes sure the second
325 // drawElement call will use the correct offset.
TEST_P(IndexBufferOffsetTest,DrawAtDifferentOffsets)326 TEST_P(IndexBufferOffsetTest, DrawAtDifferentOffsets)
327 {
328 GLushort indexData[] = {0, 1, 2, 1, 2, 3};
329 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
330 glClear(GL_COLOR_BUFFER_BIT);
331
332 size_t indexDataWidth = 6 * sizeof(GLushort);
333
334 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexDataWidth, indexData, GL_DYNAMIC_DRAW);
335 glUseProgram(mProgram);
336
337 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
338 glVertexAttribPointer(mPositionAttributeLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);
339 glEnableVertexAttribArray(mPositionAttributeLocation);
340
341 glUniform4f(mColorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
342
343 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, 0);
344 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT,
345 reinterpret_cast<void *>(indexDataWidth / 2));
346
347 // Check the upper left triangle
348 EXPECT_PIXEL_COLOR_EQ(0, getWindowHeight() / 4, GLColor::red);
349
350 // Check the down right triangle
351 EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red);
352
353 EXPECT_GL_NO_ERROR();
354 }
355
356 // Uses index buffer offset and 2 drawElement calls one of the other, one has aligned
357 // offset and one doesn't
TEST_P(IndexBufferOffsetTest,DrawAtDifferentOffsetAlignments)358 TEST_P(IndexBufferOffsetTest, DrawAtDifferentOffsetAlignments)
359 {
360 GLubyte indexData8[] = {0, 1, 0, 1, 2, 3};
361 GLushort indexData16[] = {0, 1, 2, 1, 2, 3};
362 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
363 glClear(GL_COLOR_BUFFER_BIT);
364
365 size_t indexDataWidth16 = 6 * sizeof(GLushort);
366
367 GLuint buffer[2];
368 glGenBuffers(2, buffer);
369
370 glUseProgram(mProgram);
371 glUniform4f(mColorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
372
373 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
374 glVertexAttribPointer(mPositionAttributeLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);
375 glEnableVertexAttribArray(mPositionAttributeLocation);
376
377 // 8 bit index with aligned offset
378 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[0]);
379 glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexData8), indexData8, GL_DYNAMIC_DRAW);
380
381 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, reinterpret_cast<void *>(2));
382
383 // 16 bits index buffer, which unaligned offset
384 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer[1]);
385 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexDataWidth16, indexData16, GL_DYNAMIC_DRAW);
386
387 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_SHORT,
388 reinterpret_cast<void *>(indexDataWidth16 / 2));
389
390 // Check the upper left triangle
391 EXPECT_PIXEL_COLOR_EQ(0, getWindowHeight() / 4, GLColor::red);
392
393 // Check the down right triangle
394 EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red);
395
396 EXPECT_GL_NO_ERROR();
397 }
398
399 // Uses index buffer offset and 2 drawElement calls one of the other with different counts,
400 // makes sure the second drawElement call will have its data available.
TEST_P(IndexBufferOffsetTest,DrawWithDifferentCountsSameOffset)401 TEST_P(IndexBufferOffsetTest, DrawWithDifferentCountsSameOffset)
402 {
403 GLubyte indexData[] = {99, 0, 1, 2, 1, 2, 3};
404 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
405 glClear(GL_COLOR_BUFFER_BIT);
406
407 size_t indexDataWidth = 7 * sizeof(GLubyte);
408
409 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexDataWidth, indexData, GL_DYNAMIC_DRAW);
410 glUseProgram(mProgram);
411
412 glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
413 glVertexAttribPointer(mPositionAttributeLocation, 2, GL_FLOAT, GL_FALSE, 0, 0);
414 glEnableVertexAttribArray(mPositionAttributeLocation);
415
416 glUniform4f(mColorUniformLocation, 1.0f, 0.0f, 0.0f, 1.0f);
417
418 // The first draw draws the first triangle, and the second draws a quad.
419 glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_BYTE, reinterpret_cast<void *>(1));
420 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, reinterpret_cast<void *>(1));
421
422 // Check the upper left triangle
423 EXPECT_PIXEL_COLOR_EQ(0, getWindowHeight() / 4, GLColor::red);
424
425 // Check the down right triangle
426 EXPECT_PIXEL_COLOR_EQ(getWindowWidth() - 1, getWindowHeight() - 1, GLColor::red);
427
428 EXPECT_GL_NO_ERROR();
429 }
430
431 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(IndexBufferOffsetTest);
432
433 ANGLE_INSTANTIATE_TEST_ES3(IndexBufferOffsetTestES3);
434