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, &currentBufferBinding);
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, &currentBufferBinding);
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, &currentBufferBinding);
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, &currentBufferBinding);
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