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 "random_utils.h"
9 #include "Vector.h"
10
11 using namespace angle;
12
13 namespace
14 {
15
16 class TransformFeedbackTest : public ANGLETest
17 {
18 protected:
TransformFeedbackTest()19 TransformFeedbackTest()
20 : mProgram(0),
21 mTransformFeedbackBuffer(0),
22 mTransformFeedback(0)
23 {
24 setWindowWidth(128);
25 setWindowHeight(128);
26 setConfigRedBits(8);
27 setConfigGreenBits(8);
28 setConfigBlueBits(8);
29 setConfigAlphaBits(8);
30 }
31
SetUp()32 void SetUp() override
33 {
34 ANGLETest::SetUp();
35
36 glGenBuffers(1, &mTransformFeedbackBuffer);
37 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBuffer);
38 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBufferSize, NULL,
39 GL_STATIC_DRAW);
40
41 glGenTransformFeedbacks(1, &mTransformFeedback);
42
43 ASSERT_GL_NO_ERROR();
44 }
45
TearDown()46 void TearDown() override
47 {
48 if (mProgram != 0)
49 {
50 glDeleteProgram(mProgram);
51 mProgram = 0;
52 }
53
54 if (mTransformFeedbackBuffer != 0)
55 {
56 glDeleteBuffers(1, &mTransformFeedbackBuffer);
57 mTransformFeedbackBuffer = 0;
58 }
59
60 if (mTransformFeedback != 0)
61 {
62 glDeleteTransformFeedbacks(1, &mTransformFeedback);
63 mTransformFeedback = 0;
64 }
65
66 ANGLETest::TearDown();
67 }
68
compileDefaultProgram(const std::vector<std::string> & tfVaryings,GLenum bufferMode)69 void compileDefaultProgram(const std::vector<std::string> &tfVaryings, GLenum bufferMode)
70 {
71 ASSERT_EQ(0u, mProgram);
72
73 const std::string vertexShaderSource = SHADER_SOURCE
74 (
75 precision highp float;
76 attribute vec4 position;
77
78 void main()
79 {
80 gl_Position = position;
81 }
82 );
83
84 const std::string fragmentShaderSource = SHADER_SOURCE
85 (
86 precision highp float;
87
88 void main()
89 {
90 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
91 }
92 );
93
94 mProgram = CompileProgramWithTransformFeedback(vertexShaderSource, fragmentShaderSource,
95 tfVaryings, bufferMode);
96 ASSERT_NE(0u, mProgram);
97 }
98
99 GLuint mProgram;
100
101 static const size_t mTransformFeedbackBufferSize = 1 << 24;
102 GLuint mTransformFeedbackBuffer;
103 GLuint mTransformFeedback;
104 };
105
TEST_P(TransformFeedbackTest,ZeroSizedViewport)106 TEST_P(TransformFeedbackTest, ZeroSizedViewport)
107 {
108 // Set the program's transform feedback varyings (just gl_Position)
109 std::vector<std::string> tfVaryings;
110 tfVaryings.push_back("gl_Position");
111 compileDefaultProgram(tfVaryings, GL_INTERLEAVED_ATTRIBS);
112
113 glUseProgram(mProgram);
114
115 // Bind the buffer for transform feedback output and start transform feedback
116 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mTransformFeedbackBuffer);
117 glBeginTransformFeedback(GL_TRIANGLES);
118
119 // Create a query to check how many primitives were written
120 GLuint primitivesWrittenQuery = 0;
121 glGenQueries(1, &primitivesWrittenQuery);
122 glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, primitivesWrittenQuery);
123
124 // Set a viewport that would result in no pixels being written to the framebuffer and draw
125 // a quad
126 glViewport(0, 0, 0, 0);
127
128 drawQuad(mProgram, "position", 0.5f);
129
130 // End the query and transform feedkback
131 glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
132 glEndTransformFeedback();
133
134 glUseProgram(0);
135
136 // Check how many primitives were written and verify that some were written even if
137 // no pixels were rendered
138 GLuint primitivesWritten = 0;
139 glGetQueryObjectuiv(primitivesWrittenQuery, GL_QUERY_RESULT_EXT, &primitivesWritten);
140 EXPECT_GL_NO_ERROR();
141
142 EXPECT_EQ(2u, primitivesWritten);
143 }
144
145 // Test that rebinding a buffer with the same offset resets the offset (no longer appending from the
146 // old position)
TEST_P(TransformFeedbackTest,BufferRebinding)147 TEST_P(TransformFeedbackTest, BufferRebinding)
148 {
149 glDisable(GL_DEPTH_TEST);
150
151 // Set the program's transform feedback varyings (just gl_Position)
152 std::vector<std::string> tfVaryings;
153 tfVaryings.push_back("gl_Position");
154 compileDefaultProgram(tfVaryings, GL_INTERLEAVED_ATTRIBS);
155
156 glUseProgram(mProgram);
157
158 // Make sure the buffer has zero'd data
159 std::vector<float> data(mTransformFeedbackBufferSize / sizeof(float), 0.0f);
160 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBuffer);
161 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBufferSize, data.data(),
162 GL_STATIC_DRAW);
163
164
165 // Create a query to check how many primitives were written
166 GLuint primitivesWrittenQuery = 0;
167 glGenQueries(1, &primitivesWrittenQuery);
168 glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, primitivesWrittenQuery);
169
170 const float finalZ = 0.95f;
171
172 RNG rng;
173
174 const size_t loopCount = 64;
175 for (size_t loopIdx = 0; loopIdx < loopCount; loopIdx++)
176 {
177 // Bind the buffer for transform feedback output and start transform feedback
178 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mTransformFeedbackBuffer);
179 glBeginTransformFeedback(GL_TRIANGLES);
180
181 float z = (loopIdx + 1 == loopCount) ? finalZ : rng.randomFloatBetween(0.1f, 0.5f);
182 drawQuad(mProgram, "position", z);
183
184 glEndTransformFeedback();
185 }
186
187 // End the query and transform feedback
188 glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
189
190 glUseProgram(0);
191
192 // Check how many primitives were written and verify that some were written even if
193 // no pixels were rendered
194 GLuint primitivesWritten = 0;
195 glGetQueryObjectuiv(primitivesWrittenQuery, GL_QUERY_RESULT_EXT, &primitivesWritten);
196 EXPECT_GL_NO_ERROR();
197
198 EXPECT_EQ(loopCount * 2, primitivesWritten);
199
200 // Check the buffer data
201 const float *bufferData = static_cast<float *>(glMapBufferRange(
202 GL_TRANSFORM_FEEDBACK_BUFFER, 0, mTransformFeedbackBufferSize, GL_MAP_READ_BIT));
203
204 for (size_t vertexIdx = 0; vertexIdx < 6; vertexIdx++)
205 {
206 // Check the third (Z) component of each vertex written and make sure it has the final
207 // value
208 EXPECT_NEAR(finalZ, bufferData[vertexIdx * 4 + 2], 0.0001);
209 }
210
211 for (size_t dataIdx = 24; dataIdx < mTransformFeedbackBufferSize / sizeof(float); dataIdx++)
212 {
213 EXPECT_EQ(data[dataIdx], bufferData[dataIdx]) << "Buffer overrun detected.";
214 }
215
216 glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
217
218 EXPECT_GL_NO_ERROR();
219 }
220
221 // Test that XFB can write back vertices to a buffer and that we can draw from this buffer afterward.
TEST_P(TransformFeedbackTest,RecordAndDraw)222 TEST_P(TransformFeedbackTest, RecordAndDraw)
223 {
224 // TODO(jmadill): Figure out why this fails on Intel.
225 if (IsIntel() && GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
226 {
227 std::cout << "Test skipped on Intel." << std::endl;
228 return;
229 }
230
231 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
232 glClear(GL_COLOR_BUFFER_BIT);
233
234 // Set the program's transform feedback varyings (just gl_Position)
235 std::vector<std::string> tfVaryings;
236 tfVaryings.push_back("gl_Position");
237 compileDefaultProgram(tfVaryings, GL_INTERLEAVED_ATTRIBS);
238
239 glUseProgram(mProgram);
240
241 GLint positionLocation = glGetAttribLocation(mProgram, "position");
242
243 // First pass: draw 6 points to the XFB buffer
244 glEnable(GL_RASTERIZER_DISCARD);
245
246 const GLfloat vertices[] =
247 {
248 -1.0f, 1.0f, 0.5f,
249 -1.0f, -1.0f, 0.5f,
250 1.0f, -1.0f, 0.5f,
251
252 -1.0f, 1.0f, 0.5f,
253 1.0f, -1.0f, 0.5f,
254 1.0f, 1.0f, 0.5f,
255 };
256
257 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, vertices);
258 glEnableVertexAttribArray(positionLocation);
259
260 // Bind the buffer for transform feedback output and start transform feedback
261 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mTransformFeedbackBuffer);
262 glBeginTransformFeedback(GL_POINTS);
263
264 // Create a query to check how many primitives were written
265 GLuint primitivesWrittenQuery = 0;
266 glGenQueries(1, &primitivesWrittenQuery);
267 glBeginQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, primitivesWrittenQuery);
268
269 glDrawArrays(GL_POINTS, 0, 6);
270
271 glDisableVertexAttribArray(positionLocation);
272 glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, NULL);
273 // End the query and transform feedkback
274 glEndQuery(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
275 glEndTransformFeedback();
276
277 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, 0);
278
279 glDisable(GL_RASTERIZER_DISCARD);
280
281 // Check how many primitives were written and verify that some were written even if
282 // no pixels were rendered
283 GLuint primitivesWritten = 0;
284 glGetQueryObjectuiv(primitivesWrittenQuery, GL_QUERY_RESULT_EXT, &primitivesWritten);
285 EXPECT_GL_NO_ERROR();
286
287 EXPECT_EQ(6u, primitivesWritten);
288
289 // Nothing should have been drawn to the framebuffer
290 EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 0, 0, 0, 0);
291
292 // Second pass: draw from the feedback buffer
293
294 glBindBuffer(GL_ARRAY_BUFFER, mTransformFeedbackBuffer);
295 glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, 0);
296 glEnableVertexAttribArray(positionLocation);
297
298 glDrawArrays(GL_TRIANGLES, 0, 6);
299
300 EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 0, 0, 255);
301 EXPECT_GL_NO_ERROR();
302 }
303
304 // Test that buffer binding happens only on the current transform feedback object
TEST_P(TransformFeedbackTest,BufferBinding)305 TEST_P(TransformFeedbackTest, BufferBinding)
306 {
307 // Reset any state
308 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
309 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
310
311 // Generate a new buffer
312 GLuint scratchBuffer = 0;
313 glGenBuffers(1, &scratchBuffer);
314
315 EXPECT_GL_NO_ERROR();
316
317 // Bind TF 0 and a buffer
318 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
319 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBuffer);
320
321 EXPECT_GL_NO_ERROR();
322
323 // Check that the buffer ID matches the one that was just bound
324 GLint currentBufferBinding = 0;
325 glGetIntegerv(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, ¤tBufferBinding);
326 EXPECT_EQ(static_cast<GLuint>(currentBufferBinding), mTransformFeedbackBuffer);
327
328 EXPECT_GL_NO_ERROR();
329
330 // Check that the buffer ID for the newly bound transform feedback is zero
331 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback);
332
333 glGetIntegerv(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, ¤tBufferBinding);
334 EXPECT_EQ(0, currentBufferBinding);
335
336 EXPECT_GL_NO_ERROR();
337
338 // Bind a buffer to this TF
339 glBindBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, scratchBuffer, 0, 32);
340
341 glGetIntegeri_v(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, 0, ¤tBufferBinding);
342 EXPECT_EQ(static_cast<GLuint>(currentBufferBinding), scratchBuffer);
343
344 EXPECT_GL_NO_ERROR();
345
346 // Rebind the original TF and check it's bindings
347 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
348
349 glGetIntegeri_v(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING, 0, ¤tBufferBinding);
350 EXPECT_EQ(0, currentBufferBinding);
351
352 EXPECT_GL_NO_ERROR();
353
354 // Clean up
355 glDeleteBuffers(1, &scratchBuffer);
356 }
357
358 // Test that we can capture varyings only used in the vertex shader.
TEST_P(TransformFeedbackTest,VertexOnly)359 TEST_P(TransformFeedbackTest, VertexOnly)
360 {
361 const std::string &vertexShaderSource =
362 "#version 300 es\n"
363 "in vec2 position;\n"
364 "in float attrib;\n"
365 "out float varyingAttrib;\n"
366 "void main() {\n"
367 " gl_Position = vec4(position, 0, 1);\n"
368 " varyingAttrib = attrib;\n"
369 "}";
370
371 const std::string &fragmentShaderSource =
372 "#version 300 es\n"
373 "out mediump vec4 color;\n"
374 "void main() {\n"
375 " color = vec4(0.0, 1.0, 0.0, 1.0);\n"
376 "}";
377
378 std::vector<std::string> tfVaryings;
379 tfVaryings.push_back("varyingAttrib");
380
381 mProgram = CompileProgramWithTransformFeedback(vertexShaderSource, fragmentShaderSource,
382 tfVaryings, GL_INTERLEAVED_ATTRIBS);
383 ASSERT_NE(0u, mProgram);
384
385 glUseProgram(mProgram);
386
387 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback);
388 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mTransformFeedbackBuffer);
389
390 std::vector<float> attribData;
391 for (unsigned int cnt = 0; cnt < 100; ++cnt)
392 {
393 attribData.push_back(static_cast<float>(cnt));
394 }
395
396 GLint attribLocation = glGetAttribLocation(mProgram, "attrib");
397 ASSERT_NE(-1, attribLocation);
398
399 glVertexAttribPointer(attribLocation, 1, GL_FLOAT, GL_FALSE, 4, &attribData[0]);
400 glEnableVertexAttribArray(attribLocation);
401
402 glBeginTransformFeedback(GL_TRIANGLES);
403 drawQuad(mProgram, "position", 0.5f);
404 glEndTransformFeedback();
405 ASSERT_GL_NO_ERROR();
406
407 glUseProgram(0);
408
409 GLvoid *mappedBuffer =
410 glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(float) * 6, GL_MAP_READ_BIT);
411 ASSERT_NE(nullptr, mappedBuffer);
412
413 float *mappedFloats = static_cast<float *>(mappedBuffer);
414 for (unsigned int cnt = 0; cnt < 6; ++cnt)
415 {
416 EXPECT_EQ(attribData[cnt], mappedFloats[cnt]);
417 }
418
419 EXPECT_GL_NO_ERROR();
420 }
421
422 // Test that multiple paused transform feedbacks do not generate errors or crash
TEST_P(TransformFeedbackTest,MultiplePaused)423 TEST_P(TransformFeedbackTest, MultiplePaused)
424 {
425 const size_t drawSize = 1024;
426 std::vector<float> transformFeedbackData(drawSize);
427 for (size_t i = 0; i < drawSize; i++)
428 {
429 transformFeedbackData[i] = static_cast<float>(i + 1);
430 }
431
432 // Initialize the buffers to zero
433 size_t bufferSize = drawSize;
434 std::vector<float> bufferInitialData(bufferSize, 0);
435
436 const size_t transformFeedbackCount = 8;
437
438 // clang-format off
439 const std::string vertexShaderSource = SHADER_SOURCE
440 ( #version 300 es\n
441 in highp vec4 position;
442 in float transformFeedbackInput;
443 out float transformFeedbackOutput;
444 void main(void)
445 {
446 gl_Position = position;
447 transformFeedbackOutput = transformFeedbackInput;
448 }
449 );
450
451 const std::string fragmentShaderSource = SHADER_SOURCE
452 ( #version 300 es\n
453 out mediump vec4 color;
454 void main(void)
455 {
456 color = vec4(1.0, 1.0, 1.0, 1.0);
457 }
458 );
459 // clang-format on
460
461 std::vector<std::string> tfVaryings;
462 tfVaryings.push_back("transformFeedbackOutput");
463
464 mProgram = CompileProgramWithTransformFeedback(vertexShaderSource, fragmentShaderSource,
465 tfVaryings, GL_INTERLEAVED_ATTRIBS);
466 ASSERT_NE(0u, mProgram);
467 glUseProgram(mProgram);
468
469 GLint positionLocation = glGetAttribLocation(mProgram, "position");
470 glDisableVertexAttribArray(positionLocation);
471 glVertexAttrib4f(positionLocation, 0.0f, 0.0f, 0.0f, 1.0f);
472
473 GLint tfInputLocation = glGetAttribLocation(mProgram, "transformFeedbackInput");
474 glEnableVertexAttribArray(tfInputLocation);
475 glVertexAttribPointer(tfInputLocation, 1, GL_FLOAT, false, 0, &transformFeedbackData[0]);
476
477 glDepthMask(GL_FALSE);
478 glEnable(GL_DEPTH_TEST);
479 ASSERT_GL_NO_ERROR();
480
481 GLuint transformFeedbacks[transformFeedbackCount];
482 glGenTransformFeedbacks(transformFeedbackCount, transformFeedbacks);
483
484 GLuint buffers[transformFeedbackCount];
485 glGenBuffers(transformFeedbackCount, buffers);
486
487 for (size_t i = 0; i < transformFeedbackCount; i++)
488 {
489 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, transformFeedbacks[i]);
490
491 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, buffers[i]);
492 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bufferSize * sizeof(GLfloat),
493 &bufferInitialData[0], GL_DYNAMIC_DRAW);
494 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buffers[i]);
495 ASSERT_GL_NO_ERROR();
496
497 glBeginTransformFeedback(GL_POINTS);
498
499 glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(drawSize));
500
501 glPauseTransformFeedback();
502
503 EXPECT_GL_NO_ERROR();
504 }
505
506 for (size_t i = 0; i < transformFeedbackCount; i++)
507 {
508 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, transformFeedbacks[i]);
509 glEndTransformFeedback();
510 glDeleteTransformFeedbacks(1, &transformFeedbacks[i]);
511
512 EXPECT_GL_NO_ERROR();
513 }
514 }
515 // Test that running multiple simultaneous queries and transform feedbacks from multiple EGL
516 // contexts returns the correct results. Helps expose bugs in ANGLE's virtual contexts.
TEST_P(TransformFeedbackTest,MultiContext)517 TEST_P(TransformFeedbackTest, MultiContext)
518 {
519 #if defined(ANGLE_PLATFORM_APPLE)
520 if ((IsNVIDIA() || IsAMD()) && GetParam() == ES3_OPENGL())
521 {
522 std::cout << "Test skipped on NVidia and AMD OpenGL on OSX." << std::endl;
523 return;
524 }
525 #endif
526
527 #if defined(ANGLE_PLATFORM_LINUX)
528 if (IsAMD() && GetParam() == ES3_OPENGL())
529 {
530 std::cout << "Test skipped on AMD OpenGL on Linux." << std::endl;
531 return;
532 }
533 #endif
534
535 EGLint contextAttributes[] = {
536 EGL_CONTEXT_MAJOR_VERSION_KHR,
537 GetParam().majorVersion,
538 EGL_CONTEXT_MINOR_VERSION_KHR,
539 GetParam().minorVersion,
540 EGL_NONE,
541 };
542
543 EGLWindow *window = getEGLWindow();
544
545 EGLDisplay display = window->getDisplay();
546 EGLConfig config = window->getConfig();
547 EGLSurface surface = window->getSurface();
548
549 const size_t passCount = 5;
550 struct ContextInfo
551 {
552 EGLContext context;
553 GLuint program;
554 GLuint query;
555 GLuint buffer;
556 size_t primitiveCounts[passCount];
557 };
558 ContextInfo contexts[32];
559
560 const size_t maxDrawSize = 1024;
561
562 std::vector<float> transformFeedbackData(maxDrawSize);
563 for (size_t i = 0; i < maxDrawSize; i++)
564 {
565 transformFeedbackData[i] = static_cast<float>(i + 1);
566 }
567
568 // Initialize the buffers to zero
569 size_t bufferSize = maxDrawSize * passCount;
570 std::vector<float> bufferInitialData(bufferSize, 0);
571
572 for (auto &context : contexts)
573 {
574 context.context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttributes);
575 ASSERT_NE(context.context, EGL_NO_CONTEXT);
576
577 eglMakeCurrent(display, surface, surface, context.context);
578
579 // clang-format off
580 const std::string vertexShaderSource = SHADER_SOURCE
581 ( #version 300 es\n
582 in highp vec4 position;
583 in float transformFeedbackInput;
584 out float transformFeedbackOutput;
585 void main(void)
586 {
587 gl_Position = position;
588 transformFeedbackOutput = transformFeedbackInput;
589 }
590 );
591
592 const std::string fragmentShaderSource = SHADER_SOURCE
593 ( #version 300 es\n
594 out mediump vec4 color;
595 void main(void)
596 {
597 color = vec4(1.0, 1.0, 1.0, 1.0);
598 }
599 );
600 // clang-format on
601
602 std::vector<std::string> tfVaryings;
603 tfVaryings.push_back("transformFeedbackOutput");
604
605 context.program = CompileProgramWithTransformFeedback(
606 vertexShaderSource, fragmentShaderSource, tfVaryings, GL_INTERLEAVED_ATTRIBS);
607 ASSERT_NE(context.program, 0u);
608 glUseProgram(context.program);
609
610 GLint positionLocation = glGetAttribLocation(context.program, "position");
611 glDisableVertexAttribArray(positionLocation);
612 glVertexAttrib4f(positionLocation, 0.0f, 0.0f, 0.0f, 1.0f);
613
614 GLint tfInputLocation = glGetAttribLocation(context.program, "transformFeedbackInput");
615 glEnableVertexAttribArray(tfInputLocation);
616 glVertexAttribPointer(tfInputLocation, 1, GL_FLOAT, false, 0, &transformFeedbackData[0]);
617
618 glDepthMask(GL_FALSE);
619 glEnable(GL_DEPTH_TEST);
620 glGenQueriesEXT(1, &context.query);
621 glBeginQueryEXT(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN, context.query);
622
623 ASSERT_GL_NO_ERROR();
624
625 glGenBuffers(1, &context.buffer);
626 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, context.buffer);
627 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, bufferSize * sizeof(GLfloat),
628 &bufferInitialData[0], GL_DYNAMIC_DRAW);
629 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, context.buffer);
630
631 ASSERT_GL_NO_ERROR();
632
633 // For each pass, draw between 0 and maxDrawSize primitives
634 for (auto &primCount : context.primitiveCounts)
635 {
636 primCount = rand() % maxDrawSize;
637 }
638
639 glBeginTransformFeedback(GL_POINTS);
640 }
641
642 for (size_t pass = 0; pass < passCount; pass++)
643 {
644 for (const auto &context : contexts)
645 {
646 eglMakeCurrent(display, surface, surface, context.context);
647
648 glDrawArrays(GL_POINTS, 0, static_cast<GLsizei>(context.primitiveCounts[pass]));
649 }
650 }
651
652 for (const auto &context : contexts)
653 {
654 eglMakeCurrent(display, surface, surface, context.context);
655
656 glEndTransformFeedback();
657
658 glEndQueryEXT(GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN);
659
660 GLuint result = 0;
661 glGetQueryObjectuivEXT(context.query, GL_QUERY_RESULT_EXT, &result);
662
663 EXPECT_GL_NO_ERROR();
664
665 size_t totalPrimCount = 0;
666 for (const auto &primCount : context.primitiveCounts)
667 {
668 totalPrimCount += primCount;
669 }
670 EXPECT_EQ(static_cast<GLuint>(totalPrimCount), result);
671
672 const float *bufferData = reinterpret_cast<float *>(glMapBufferRange(
673 GL_TRANSFORM_FEEDBACK_BUFFER, 0, bufferSize * sizeof(GLfloat), GL_MAP_READ_BIT));
674
675 size_t curBufferIndex = 0;
676 for (const auto &primCount : context.primitiveCounts)
677 {
678 for (size_t prim = 0; prim < primCount; prim++)
679 {
680 EXPECT_EQ(bufferData[curBufferIndex], prim + 1);
681 curBufferIndex++;
682 }
683 }
684
685 while (curBufferIndex < bufferSize)
686 {
687 EXPECT_EQ(bufferData[curBufferIndex], 0.0f);
688 curBufferIndex++;
689 }
690
691 glUnmapBuffer(GL_TRANSFORM_FEEDBACK_BUFFER);
692 }
693
694 eglMakeCurrent(display, surface, surface, window->getContext());
695
696 for (auto &context : contexts)
697 {
698 eglDestroyContext(display, context.context);
699 context.context = EGL_NO_CONTEXT;
700 }
701 }
702
703 // Test that when two vec2s are packed into the same register, we can still capture both of them.
TEST_P(TransformFeedbackTest,PackingBug)704 TEST_P(TransformFeedbackTest, PackingBug)
705 {
706 // TODO(jmadill): With points and rasterizer discard?
707 const std::string &vertexShaderSource =
708 "#version 300 es\n"
709 "in vec2 inAttrib1;\n"
710 "in vec2 inAttrib2;\n"
711 "out vec2 outAttrib1;\n"
712 "out vec2 outAttrib2;\n"
713 "in vec2 position;\n"
714 "void main() {"
715 " outAttrib1 = inAttrib1;\n"
716 " outAttrib2 = inAttrib2;\n"
717 " gl_Position = vec4(position, 0, 1);\n"
718 "}";
719
720 const std::string &fragmentShaderSource =
721 "#version 300 es\n"
722 "precision mediump float;\n"
723 "out vec4 color;\n"
724 "void main() {\n"
725 " color = vec4(0);\n"
726 "}";
727
728 std::vector<std::string> tfVaryings;
729 tfVaryings.push_back("outAttrib1");
730 tfVaryings.push_back("outAttrib2");
731
732 mProgram = CompileProgramWithTransformFeedback(vertexShaderSource, fragmentShaderSource,
733 tfVaryings, GL_INTERLEAVED_ATTRIBS);
734 ASSERT_NE(0u, mProgram);
735
736 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBuffer);
737 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(Vector2) * 2 * 6, nullptr, GL_STREAM_DRAW);
738
739 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback);
740 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mTransformFeedbackBuffer);
741
742 GLint attrib1Loc = glGetAttribLocation(mProgram, "inAttrib1");
743 GLint attrib2Loc = glGetAttribLocation(mProgram, "inAttrib2");
744
745 std::vector<Vector2> attrib1Data;
746 std::vector<Vector2> attrib2Data;
747 int counter = 0;
748 for (size_t i = 0; i < 6; i++) {
749 attrib1Data.push_back(Vector2(counter + 0.0f, counter + 1.0f));
750 attrib2Data.push_back(Vector2(counter + 2.0f, counter + 3.0f));
751 counter += 4;
752 }
753
754 glEnableVertexAttribArray(attrib1Loc);
755 glEnableVertexAttribArray(attrib2Loc);
756
757 glVertexAttribPointer(attrib1Loc, 2, GL_FLOAT, GL_FALSE, 0, attrib1Data.data());
758 glVertexAttribPointer(attrib2Loc, 2, GL_FLOAT, GL_FALSE, 0, attrib2Data.data());
759
760 glUseProgram(mProgram);
761 glBeginTransformFeedback(GL_TRIANGLES);
762 drawQuad(mProgram, "position", 0.5f);
763 glEndTransformFeedback();
764 glUseProgram(0);
765 ASSERT_GL_NO_ERROR();
766
767 const GLvoid *mapPointer =
768 glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(Vector2) * 2 * 6, GL_MAP_READ_BIT);
769 ASSERT_NE(nullptr, mapPointer);
770
771 const Vector2 *vecPointer = static_cast<const Vector2 *>(mapPointer);
772 for (unsigned int vectorIndex = 0; vectorIndex < 3; ++vectorIndex)
773 {
774 unsigned int stream1Index = vectorIndex * 2;
775 unsigned int stream2Index = vectorIndex * 2 + 1;
776 EXPECT_EQ(attrib1Data[vectorIndex], vecPointer[stream1Index]);
777 EXPECT_EQ(attrib2Data[vectorIndex], vecPointer[stream2Index]);
778 }
779
780 ASSERT_GL_NO_ERROR();
781 }
782
783 // Test that transform feedback varyings that can be optimized out yet do not cause program
784 // compilation to fail
TEST_P(TransformFeedbackTest,OptimizedVaryings)785 TEST_P(TransformFeedbackTest, OptimizedVaryings)
786 {
787 const std::string &vertexShaderSource =
788 "#version 300 es\n"
789 "in vec4 a_vertex;\n"
790 "in vec3 a_normal; \n"
791 "\n"
792 "uniform Transform\n"
793 "{\n"
794 " mat4 u_modelViewMatrix;\n"
795 " mat4 u_projectionMatrix;\n"
796 " mat3 u_normalMatrix;\n"
797 "};\n"
798 "\n"
799 "out vec3 normal;\n"
800 "out vec4 ecPosition;\n"
801 "\n"
802 "void main()\n"
803 "{\n"
804 " normal = normalize(u_normalMatrix * a_normal);\n"
805 " ecPosition = u_modelViewMatrix * a_vertex;\n"
806 " gl_Position = u_projectionMatrix * ecPosition;\n"
807 "}\n";
808
809 const std::string &fragmentShaderSource =
810 "#version 300 es\n"
811 "precision mediump float;\n"
812 "\n"
813 "in vec3 normal;\n"
814 "in vec4 ecPosition;\n"
815 "\n"
816 "out vec4 fragColor;\n"
817 "\n"
818 "void main()\n"
819 "{\n"
820 " fragColor = vec4(normal/2.0+vec3(0.5), 1);\n"
821 "}\n";
822
823 std::vector<std::string> tfVaryings;
824 tfVaryings.push_back("normal");
825 tfVaryings.push_back("ecPosition");
826
827 mProgram = CompileProgramWithTransformFeedback(vertexShaderSource, fragmentShaderSource,
828 tfVaryings, GL_INTERLEAVED_ATTRIBS);
829 ASSERT_NE(0u, mProgram);
830 }
831
832 // Test an edge case where two varyings are unreferenced in the frag shader.
TEST_P(TransformFeedbackTest,TwoUnreferencedInFragShader)833 TEST_P(TransformFeedbackTest, TwoUnreferencedInFragShader)
834 {
835 // TODO(jmadill): With points and rasterizer discard?
836 const std::string &vertexShaderSource =
837 "#version 300 es\n"
838 "in vec3 position;\n"
839 "out vec3 outAttrib1;\n"
840 "out vec3 outAttrib2;\n"
841 "void main() {"
842 " outAttrib1 = position;\n"
843 " outAttrib2 = position;\n"
844 " gl_Position = vec4(position, 1);\n"
845 "}";
846
847 const std::string &fragmentShaderSource =
848 "#version 300 es\n"
849 "precision mediump float;\n"
850 "out vec4 color;\n"
851 "in vec3 outAttrib1;\n"
852 "in vec3 outAttrib2;\n"
853 "void main() {\n"
854 " color = vec4(0);\n"
855 "}";
856
857 std::vector<std::string> tfVaryings;
858 tfVaryings.push_back("outAttrib1");
859 tfVaryings.push_back("outAttrib2");
860
861 mProgram = CompileProgramWithTransformFeedback(vertexShaderSource, fragmentShaderSource,
862 tfVaryings, GL_INTERLEAVED_ATTRIBS);
863 ASSERT_NE(0u, mProgram);
864
865 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBuffer);
866 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(Vector3) * 2 * 6, nullptr, GL_STREAM_DRAW);
867
868 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback);
869 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mTransformFeedbackBuffer);
870
871 glUseProgram(mProgram);
872 glBeginTransformFeedback(GL_TRIANGLES);
873 drawQuad(mProgram, "position", 0.5f);
874 glEndTransformFeedback();
875 glUseProgram(0);
876 ASSERT_GL_NO_ERROR();
877
878 const GLvoid *mapPointer =
879 glMapBufferRange(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sizeof(Vector2) * 2 * 6, GL_MAP_READ_BIT);
880 ASSERT_NE(nullptr, mapPointer);
881
882 const auto &quadVertices = GetQuadVertices();
883
884 const Vector3 *vecPointer = static_cast<const Vector3 *>(mapPointer);
885 for (unsigned int vectorIndex = 0; vectorIndex < 3; ++vectorIndex)
886 {
887 unsigned int stream1Index = vectorIndex * 2;
888 unsigned int stream2Index = vectorIndex * 2 + 1;
889 EXPECT_EQ(quadVertices[vectorIndex], vecPointer[stream1Index]);
890 EXPECT_EQ(quadVertices[vectorIndex], vecPointer[stream2Index]);
891 }
892
893 ASSERT_GL_NO_ERROR();
894 }
895 class TransformFeedbackLifetimeTest : public TransformFeedbackTest
896 {
897 protected:
TransformFeedbackLifetimeTest()898 TransformFeedbackLifetimeTest() : mVertexArray(0) {}
899
SetUp()900 void SetUp() override
901 {
902 ANGLETest::SetUp();
903
904 glGenVertexArrays(1, &mVertexArray);
905 glBindVertexArray(mVertexArray);
906
907 std::vector<std::string> tfVaryings;
908 tfVaryings.push_back("gl_Position");
909 compileDefaultProgram(tfVaryings, GL_SEPARATE_ATTRIBS);
910
911 glGenBuffers(1, &mTransformFeedbackBuffer);
912 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBuffer);
913 glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, mTransformFeedbackBufferSize, NULL,
914 GL_DYNAMIC_DRAW);
915 glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, 0);
916
917 glGenTransformFeedbacks(1, &mTransformFeedback);
918
919 ASSERT_GL_NO_ERROR();
920 }
921
TearDown()922 void TearDown() override
923 {
924 glDeleteVertexArrays(1, &mVertexArray);
925 TransformFeedbackTest::TearDown();
926 }
927
928 GLuint mVertexArray;
929 };
930
931 // Tests a bug with state syncing and deleted transform feedback buffers.
TEST_P(TransformFeedbackLifetimeTest,DeletedBuffer)932 TEST_P(TransformFeedbackLifetimeTest, DeletedBuffer)
933 {
934 // First stream vertex data to mTransformFeedbackBuffer.
935 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback);
936 glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, mTransformFeedbackBuffer);
937
938 glUseProgram(mProgram);
939
940 glBeginTransformFeedback(GL_TRIANGLES);
941 drawQuad(mProgram, "position", 0.5f, 1.0f, true);
942 glEndTransformFeedback();
943
944 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
945
946 // TODO(jmadill): Remove this when http://anglebug.com/1351 is fixed.
947 glBindVertexArray(0);
948 drawQuad(mProgram, "position", 0.5f);
949 glBindVertexArray(1);
950
951 // Next, draw vertices with mTransformFeedbackBuffer. This will link to mVertexArray.
952 glBindBuffer(GL_ARRAY_BUFFER, mTransformFeedbackBuffer);
953 GLint loc = glGetAttribLocation(mProgram, "position");
954 ASSERT_NE(-1, loc);
955 glVertexAttribPointer(loc, 1, GL_FLOAT, GL_FALSE, 4, nullptr);
956 glEnableVertexAttribArray(loc);
957 glBindBuffer(GL_ARRAY_BUFFER, 0);
958 glDrawArrays(GL_TRIANGLES, 0, 3);
959
960 // Delete resources, making a stranded pointer to mVertexArray in mTransformFeedbackBuffer.
961 glDeleteBuffers(1, &mTransformFeedbackBuffer);
962 mTransformFeedbackBuffer = 0;
963 glDeleteVertexArrays(1, &mVertexArray);
964 mVertexArray = 0;
965
966 // Then draw again with transform feedback, dereferencing the stranded pointer.
967 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, mTransformFeedback);
968 glBeginTransformFeedback(GL_TRIANGLES);
969 drawQuad(mProgram, "position", 0.5f, 1.0f, true);
970 glEndTransformFeedback();
971 glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
972
973 ASSERT_GL_NO_ERROR();
974 }
975
976 // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
977 ANGLE_INSTANTIATE_TEST(TransformFeedbackTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
978 ANGLE_INSTANTIATE_TEST(TransformFeedbackLifetimeTest, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
979
980 } // anonymous namespace
981