xref: /aosp_15_r20/external/angle/src/tests/capture_tests/CapturedTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2023 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 
9 #include "test_utils/gl_raii.h"
10 #include "util/random_utils.h"
11 #include "util/shader_utils.h"
12 #include "util/test_utils.h"
13 
14 using namespace angle;
15 
16 namespace
17 {
18 class CapturedTest : public ANGLETest<>
19 {
20   protected:
CapturedTest()21     CapturedTest()
22     {
23         setWindowWidth(128);
24         setWindowHeight(128);
25         setConfigRedBits(8);
26         setConfigGreenBits(8);
27         setConfigBlueBits(8);
28         setConfigAlphaBits(8);
29         setConfigDepthBits(24);
30         setConfigStencilBits(8);
31     }
32 
testSetUp()33     void testSetUp() override
34     {
35         // Calls not captured because we setup Start frame to MEC.
36 
37         // TODO: why are these framebuffers not showing up in the capture?
38 
39         mFBOs.resize(2, 0);
40         glGenFramebuffers(2, mFBOs.data());
41     }
42 
testTearDown()43     void testTearDown() override
44     {
45         // Not reached during capture as we hit the End frame earlier.
46 
47         if (!mFBOs.empty())
48         {
49             glDeleteFramebuffers(static_cast<GLsizei>(mFBOs.size()), mFBOs.data());
50         }
51     }
52 
53     std::vector<GLuint> mFBOs;
54 };
55 
56 class MultiFrame
57 {
58   public:
testSetUp()59     void testSetUp()
60     {
61         constexpr char kInactiveVS[] = R"(precision highp float;
62 void main(void) {
63    gl_Position = vec4(0.5, 0.5, 0.5, 1.0);
64 })";
65 
66         static constexpr char kInactiveDeferredVS[] = R"(attribute vec4 a_position;
67 attribute vec2 a_texCoord;
68 varying vec2 v_texCoord;
69 void main()
70 {
71     gl_Position = a_position;
72     v_texCoord = a_texCoord;
73 })";
74 
75         static constexpr char kInactiveDeferredFS[] = R"(precision mediump float;
76 varying vec2 v_texCoord;
77 uniform sampler2D s_texture;
78 void main()
79 {
80     gl_FragColor = vec4(0.4, 0.4, 0.4, 1.0);
81     gl_FragColor = texture2D(s_texture, v_texCoord);
82 })";
83 
84         static constexpr char kActiveDeferredVS[] = R"(attribute vec4 a_position;
85 attribute vec2 a_texCoord;
86 varying vec2 v_texCoord;
87 void main()
88 {
89     gl_Position = a_position;
90     v_texCoord = a_texCoord;
91 })";
92 
93         // Create shaders, program but defer compiling & linking, use before capture starts
94         // (Inactive)
95         lateLinkTestVertShaderInactive                   = glCreateShader(GL_VERTEX_SHADER);
96         const char *lateLinkTestVsSourceArrayInactive[1] = {kInactiveDeferredVS};
97         glShaderSource(lateLinkTestVertShaderInactive, 1, lateLinkTestVsSourceArrayInactive, 0);
98         lateLinkTestFragShaderInactive                   = glCreateShader(GL_FRAGMENT_SHADER);
99         const char *lateLinkTestFsSourceArrayInactive[1] = {kInactiveDeferredFS};
100         glShaderSource(lateLinkTestFragShaderInactive, 1, lateLinkTestFsSourceArrayInactive, 0);
101         lateLinkTestProgramInactive = glCreateProgram();
102         glAttachShader(lateLinkTestProgramInactive, lateLinkTestVertShaderInactive);
103         glAttachShader(lateLinkTestProgramInactive, lateLinkTestFragShaderInactive);
104 
105         // Create inactive program having shader shared with deferred linked program
106         glCompileShader(lateLinkTestVertShaderInactive);
107         glCompileShader(lateLinkTestFragShaderInactive);
108         glLinkProgram(lateLinkTestProgramInactive);
109         ASSERT_GL_NO_ERROR();
110 
111         // Create vertex shader and program but defer compiling & linking until capture time
112         // (Active) Use fragment shader attached to inactive program
113         lateLinkTestVertShaderActive                   = glCreateShader(GL_VERTEX_SHADER);
114         const char *lateLinkTestVsSourceArrayActive[1] = {kActiveDeferredVS};
115         glShaderSource(lateLinkTestVertShaderActive, 1, lateLinkTestVsSourceArrayActive, 0);
116         lateLinkTestProgramActive = glCreateProgram();
117         glAttachShader(lateLinkTestProgramActive, lateLinkTestVertShaderActive);
118         glAttachShader(lateLinkTestProgramActive, lateLinkTestFragShaderInactive);
119         ASSERT_GL_NO_ERROR();
120 
121         // Create shader that is unused during capture
122         inactiveProgram                    = glCreateProgram();
123         inactiveShader                     = glCreateShader(GL_VERTEX_SHADER);
124         const char *inactiveSourceArray[1] = {kInactiveVS};
125         glShaderSource(inactiveShader, 1, inactiveSourceArray, 0);
126         glCompileShader(inactiveShader);
127         glAttachShader(inactiveProgram, inactiveShader);
128 
129         // Create Shader Program before capture begins to use during capture
130         activeBeforeProgram                = glCreateProgram();
131         activeBeforeVertShader             = glCreateShader(GL_VERTEX_SHADER);
132         activeBeforeFragShader             = glCreateShader(GL_FRAGMENT_SHADER);
133         const char *activeVsSourceArray[1] = {kActiveVS};
134         glShaderSource(activeBeforeVertShader, 1, activeVsSourceArray, 0);
135         glCompileShader(activeBeforeVertShader);
136         glAttachShader(activeBeforeProgram, activeBeforeVertShader);
137         const char *activeFsSourceArray[1] = {kActiveFS};
138         glShaderSource(activeBeforeFragShader, 1, activeFsSourceArray, 0);
139         glCompileShader(activeBeforeFragShader);
140         glAttachShader(activeBeforeProgram, activeBeforeFragShader);
141         glLinkProgram(activeBeforeProgram);
142 
143         // Get the attr/sampler locations
144         mPositionLoc = glGetAttribLocation(activeBeforeProgram, "a_position");
145         mTexCoordLoc = glGetAttribLocation(activeBeforeProgram, "a_texCoord");
146         mSamplerLoc  = glGetUniformLocation(activeBeforeProgram, "s_texture");
147         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
148 
149         // Bind the texture object before capture begins
150         glGenTextures(1, &textureNeverBound);
151         glGenTextures(1, &textureBoundBeforeCapture);
152         glBindTexture(GL_TEXTURE_2D, textureBoundBeforeCapture);
153         ASSERT_GL_NO_ERROR();
154 
155         const size_t width                 = 2;
156         const size_t height                = 2;
157         GLubyte pixels[width * height * 3] = {
158             255, 0,   0,    // Red
159             0,   255, 0,    // Green
160             0,   0,   255,  // Blue
161             255, 255, 0,    // Yellow
162         };
163         // Populate the pre-capture bound texture
164         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
165         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
166         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
167     }
168 
testTearDown()169     void testTearDown()
170     {
171         glDeleteTextures(1, &textureNeverBound);
172         glDeleteTextures(1, &textureBoundBeforeCapture);
173         glDeleteProgram(inactiveProgram);
174         glDeleteProgram(activeBeforeProgram);
175         glDeleteShader(inactiveShader);
176         glDeleteShader(activeBeforeVertShader);
177         glDeleteShader(activeBeforeFragShader);
178         glDeleteProgram(lateLinkTestProgramInactive);
179         glDeleteProgram(lateLinkTestProgramActive);
180         glDeleteShader(lateLinkTestVertShaderInactive);
181         glDeleteShader(lateLinkTestFragShaderInactive);
182         glDeleteShader(lateLinkTestVertShaderActive);
183     }
184 
185     static constexpr char kActiveVS[] = R"(attribute vec4 a_position;
186 attribute vec2 a_texCoord;
187 varying vec2 v_texCoord;
188 void main()
189 {
190     gl_Position = a_position;
191     v_texCoord = a_texCoord;
192 })";
193 
194     static constexpr char kActiveFS[] = R"(precision mediump float;
195 varying vec2 v_texCoord;
196 uniform sampler2D s_texture;
197 void main()
198 {
199     gl_FragColor = texture2D(s_texture, v_texCoord);
200 })";
201 
202     void frame1();
203     void frame2();
204     void frame3();
205     void frame4();
206 
207     // For testing deferred compile/link
208     GLuint lateLinkTestVertShaderInactive;
209     GLuint lateLinkTestFragShaderInactive;
210     GLuint lateLinkTestProgramInactive;
211     GLuint lateLinkTestVertShaderActive;
212     GLuint lateLinkTestProgramActive;
213 
214     GLuint inactiveProgram;
215     GLuint inactiveShader;
216 
217     GLuint activeBeforeProgram;
218     GLuint activeBeforeVertShader;
219     GLuint activeBeforeFragShader;
220 
221     GLuint textureNeverBound;
222     GLuint textureBoundBeforeCapture;
223     GLuint mPositionLoc;
224     GLuint mTexCoordLoc;
225     GLint mSamplerLoc;
226 };
227 
frame1()228 void MultiFrame::frame1()
229 {
230     glClearColor(0.25f, 0.5f, 0.5f, 0.5f);
231     glClear(GL_COLOR_BUFFER_BIT);
232     EXPECT_PIXEL_NEAR(0, 0, 64, 128, 128, 128, 1.0);
233 
234     static GLfloat vertices[] = {
235         -0.75f, 0.25f,  0.0f,  // Position 0
236         0.0f,   0.0f,          // TexCoord 0
237         -0.75f, -0.75f, 0.0f,  // Position 1
238         0.0f,   1.0f,          // TexCoord 1
239         0.25f,  -0.75f, 0.0f,  // Position 2
240         1.0f,   1.0f,          // TexCoord 2
241         0.25f,  0.25f,  0.0f,  // Position 3
242         1.0f,   0.0f           // TexCoord 3
243     };
244 
245     GLushort indices[] = {0, 1, 2, 0, 2, 3};
246 
247     glClear(GL_COLOR_BUFFER_BIT);
248     glUseProgram(activeBeforeProgram);
249     glVertexAttribPointer(mPositionLoc, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vertices);
250     glVertexAttribPointer(mTexCoordLoc, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vertices + 3);
251     glEnableVertexAttribArray(mPositionLoc);
252     glEnableVertexAttribArray(mTexCoordLoc);
253     glUniform1i(mSamplerLoc, 0);
254 
255     // Draw without binding texture during capture
256     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
257     EXPECT_PIXEL_EQ(20, 20, 0, 0, 255, 255);
258 
259     glDeleteVertexArrays(1, &mPositionLoc);
260     glDeleteVertexArrays(1, &mTexCoordLoc);
261 }
262 
frame2()263 void MultiFrame::frame2()
264 {
265     // Draw using texture created and bound during capture
266 
267     GLuint activeDuringProgram;
268     GLuint activeDuringVertShader;
269     GLuint activeDuringFragShader;
270     GLuint positionLoc;
271     GLuint texCoordLoc;
272     GLuint textureBoundDuringCapture;
273     GLint samplerLoc;
274 
275     activeDuringProgram                = glCreateProgram();
276     activeDuringVertShader             = glCreateShader(GL_VERTEX_SHADER);
277     activeDuringFragShader             = glCreateShader(GL_FRAGMENT_SHADER);
278     const char *activeVsSourceArray[1] = {kActiveVS};
279     glShaderSource(activeDuringVertShader, 1, activeVsSourceArray, 0);
280     glCompileShader(activeDuringVertShader);
281     glAttachShader(activeDuringProgram, activeDuringVertShader);
282     const char *activeFsSourceArray[1] = {kActiveFS};
283     glShaderSource(activeDuringFragShader, 1, activeFsSourceArray, 0);
284     glCompileShader(activeDuringFragShader);
285     glAttachShader(activeDuringProgram, activeDuringFragShader);
286     glLinkProgram(activeDuringProgram);
287 
288     // Get the attr/sampler locations
289     positionLoc = glGetAttribLocation(activeDuringProgram, "a_position");
290     texCoordLoc = glGetAttribLocation(activeDuringProgram, "a_texCoord");
291     samplerLoc  = glGetUniformLocation(activeDuringProgram, "s_texture");
292     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
293 
294     // Bind the texture object during capture
295     glGenTextures(1, &textureBoundDuringCapture);
296     glBindTexture(GL_TEXTURE_2D, textureBoundDuringCapture);
297     ASSERT_GL_NO_ERROR();
298 
299     const size_t width                 = 2;
300     const size_t height                = 2;
301     GLubyte pixels[width * height * 3] = {
302         255, 255, 0,    // Yellow
303         0,   0,   255,  // Blue
304         0,   255, 0,    // Green
305         255, 0,   0,    // Red
306     };
307 
308     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
309     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
310     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
311 
312     static GLfloat vertices[] = {
313         -0.25f, 0.75f,  0.0f,  // Position 0
314         0.0f,   0.0f,          // TexCoord 0
315         -0.25f, -0.25f, 0.0f,  // Position 1
316         0.0f,   1.0f,          // TexCoord 1
317         0.75f,  -0.25f, 0.0f,  // Position 2
318         1.0f,   1.0f,          // TexCoord 2
319         0.75f,  0.75f,  0.0f,  // Position 3
320         1.0f,   0.0f           // TexCoord 3
321     };
322     GLushort indices[] = {0, 1, 2, 0, 2, 3};
323 
324     glClear(GL_COLOR_BUFFER_BIT);
325     glUseProgram(activeDuringProgram);
326     glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vertices);
327     glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vertices + 3);
328     glEnableVertexAttribArray(positionLoc);
329     glEnableVertexAttribArray(texCoordLoc);
330     glUniform1i(samplerLoc, 0);
331 
332     // Draw using texture bound during capture
333     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
334     EXPECT_PIXEL_EQ(108, 108, 0, 0, 255, 255);
335 
336     glDeleteTextures(1, &textureBoundDuringCapture);
337     glDeleteVertexArrays(1, &positionLoc);
338     glDeleteVertexArrays(1, &texCoordLoc);
339     glDeleteProgram(activeDuringProgram);
340     glDeleteShader(activeDuringVertShader);
341     glDeleteShader(activeDuringFragShader);
342 }
343 
frame3()344 void MultiFrame::frame3()
345 {
346     // TODO: using local objects (with RAII helpers) here that create and destroy objects within the
347     // frame. Maybe move some of this to test Setup.
348 
349     constexpr char kVS[] = R"(precision highp float;
350 attribute vec3 attr1;
351 void main(void) {
352    gl_Position = vec4(attr1, 1.0);
353 })";
354 
355     constexpr char kFS[] = R"(precision highp float;
356 void main(void) {
357    gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
358 })";
359 
360     GLBuffer emptyBuffer;
361     glBindBuffer(GL_ARRAY_BUFFER, emptyBuffer);
362 
363     ANGLE_GL_PROGRAM(program, kVS, kFS);
364     glBindAttribLocation(program, 0, "attr1");
365     glLinkProgram(program);
366     ASSERT_TRUE(CheckLinkStatusAndReturnProgram(program, true));
367     glUseProgram(program);
368 
369     // Use non-existing attribute 1.
370     glEnableVertexAttribArray(1);
371     glVertexAttribPointer(1, 3, GL_UNSIGNED_BYTE, false, 1, 0);
372 
373     glDrawArrays(GL_TRIANGLES, 0, 3);
374     EXPECT_GL_NO_ERROR();
375 
376     // Note: RAII destructors called here causing additional GL calls.
377 }
378 
frame4()379 void MultiFrame::frame4()
380 {
381     GLuint positionLoc;
382     GLuint texCoordLoc;
383     GLuint lateLinkTestTexture;
384     GLint samplerLoc;
385 
386     // Deferred compile/link
387     glCompileShader(lateLinkTestVertShaderActive);
388     glLinkProgram(lateLinkTestProgramActive);
389     ASSERT_GL_NO_ERROR();
390 
391     // Get the attr/sampler locations
392     positionLoc = glGetAttribLocation(lateLinkTestProgramActive, "a_position");
393     texCoordLoc = glGetAttribLocation(lateLinkTestProgramActive, "a_texCoord");
394     samplerLoc  = glGetUniformLocation(lateLinkTestProgramActive, "s_texture");
395     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
396 
397     // Bind the texture object during capture
398     glGenTextures(1, &lateLinkTestTexture);
399     glBindTexture(GL_TEXTURE_2D, lateLinkTestTexture);
400     ASSERT_GL_NO_ERROR();
401 
402     const size_t width                 = 2;
403     const size_t height                = 2;
404     GLubyte pixels[width * height * 3] = {
405         255, 255, 0,    // Yellow
406         0,   0,   255,  // Blue
407         0,   255, 0,    // Green
408         255, 0,   0,    // Red
409     };
410 
411     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
412     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
413     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
414 
415     static GLfloat vertices[] = {
416         -0.25f, 0.75f,  0.0f,  // Position 0
417         0.0f,   0.0f,          // TexCoord 0
418         -0.25f, -0.25f, 0.0f,  // Position 1
419         0.0f,   1.0f,          // TexCoord 1
420         0.75f,  -0.25f, 0.0f,  // Position 2
421         1.0f,   1.0f,          // TexCoord 2
422         0.75f,  0.75f,  0.0f,  // Position 3
423         1.0f,   0.0f           // TexCoord 3
424     };
425     GLushort indices[] = {0, 1, 2, 0, 2, 3};
426 
427     glClear(GL_COLOR_BUFFER_BIT);
428     glUseProgram(lateLinkTestProgramActive);
429     glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vertices);
430     glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vertices + 3);
431     glEnableVertexAttribArray(positionLoc);
432     glEnableVertexAttribArray(texCoordLoc);
433     glUniform1i(samplerLoc, 0);
434 
435     // Draw shaders & program created before capture
436     glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
437     EXPECT_PIXEL_EQ(108, 108, 0, 0, 255, 255);
438 
439     // Add an invalid call so it shows up in capture as a comment.
440     // This is unrelated to the rest of the frame, but needs a home.
441     GLuint nonExistentBinding = 666;
442     GLuint nonExistentTexture = 777;
443     glBindTexture(nonExistentBinding, nonExistentTexture);
444     glGetError();
445 
446     // Another unrelated change
447     // Bind a PIXEL_UNPACK_BUFFER buffer so it gets cleared in Reset
448     GLBuffer unpackBuffer;
449     glBindBuffer(GL_PIXEL_UNPACK_BUFFER, unpackBuffer);
450 }
451 
452 // Test captured by capture_tests.py
TEST_P(CapturedTest,MultiFrame)453 TEST_P(CapturedTest, MultiFrame)
454 {
455     MultiFrame multiFrame;
456     multiFrame.testSetUp();
457 
458     // Swap before the first frame so that setup gets its own frame
459     swapBuffers();
460     multiFrame.frame1();
461 
462     swapBuffers();
463     multiFrame.frame2();
464 
465     swapBuffers();
466     multiFrame.frame3();
467 
468     swapBuffers();
469     multiFrame.frame4();
470 
471     // Empty frames to reach capture end.
472     for (int i = 0; i < 10; i++)
473     {
474         swapBuffers();
475     }
476     // Note: test teardown adds an additonal swap in
477     // ANGLETestBase::ANGLETestPreTearDown() when --angle-per-test-capture-label
478 
479     multiFrame.testTearDown();
480 }
481 
482 // Draw using two textures using multiple glActiveTexture calls, ensure they are correctly Reset
TEST_P(CapturedTest,ActiveTextures)483 TEST_P(CapturedTest, ActiveTextures)
484 {
485     static constexpr char kVS[] = R"(attribute vec4 a_position;
486 attribute vec2 a_texCoord;
487 varying vec2 v_texCoord;
488 void main()
489 {
490     gl_Position = a_position;
491     v_texCoord = a_texCoord;
492 })";
493 
494     static constexpr char kFS[] = R"(precision mediump float;
495 varying vec2 v_texCoord;
496 uniform sampler2D s_texture1;
497 uniform sampler2D s_texture2;
498 void main()
499 {
500     gl_FragColor = texture2D(s_texture1, v_texCoord) + texture2D(s_texture2, v_texCoord);
501 })";
502 
503     ANGLE_GL_PROGRAM(program, kVS, kFS);
504     ASSERT_GL_NO_ERROR();
505 
506     glUseProgram(program);
507 
508     // Set the sampler uniforms
509     GLint samplerLoc1 = glGetUniformLocation(program, "s_texture1");
510     GLint samplerLoc2 = glGetUniformLocation(program, "s_texture2");
511     glUniform1i(samplerLoc1, 0);
512     glUniform1i(samplerLoc2, 1);
513 
514     // Bind the texture objects before capture begins
515     constexpr const GLsizei kSize = 4;
516 
517     GLTexture redTexture;
518     GLTexture greenTexture;
519     GLTexture blueTexture;
520 
521     const std::vector<GLColor> kRedData(kSize * kSize, GLColor::red);
522     const std::vector<GLColor> kGreenData(kSize * kSize, GLColor::green);
523     const std::vector<GLColor> kBlueData(kSize * kSize, GLColor::blue);
524 
525     // Red texture
526     glBindTexture(GL_TEXTURE_2D, redTexture);
527     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
528                  kRedData.data());
529     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
530     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
531 
532     // Green texture
533     glBindTexture(GL_TEXTURE_2D, greenTexture);
534     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
535                  kGreenData.data());
536     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
537     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
538 
539     // Blue texture
540     glBindTexture(GL_TEXTURE_2D, blueTexture);
541     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
542                  kBlueData.data());
543     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
544     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
545 
546     ASSERT_GL_NO_ERROR();
547 
548     // First run the program with red and green active
549     glActiveTexture(GL_TEXTURE0);
550     glBindTexture(GL_TEXTURE_2D, redTexture);
551 
552     glActiveTexture(GL_TEXTURE1);
553     glBindTexture(GL_TEXTURE_2D, greenTexture);
554 
555     // Trigger MEC
556     swapBuffers();
557     ASSERT_GL_NO_ERROR();
558 
559     // Draw and verify results
560     drawQuad(program, "a_position", 0.5f);
561     ASSERT_GL_NO_ERROR();
562     EXPECT_PIXEL_EQ(0, 0, 255, 255, 0, 255);
563 
564     // Change the active textures to green and blue
565     glActiveTexture(GL_TEXTURE0);
566     glBindTexture(GL_TEXTURE_2D, greenTexture);
567 
568     glActiveTexture(GL_TEXTURE1);
569     glBindTexture(GL_TEXTURE_2D, blueTexture);
570 
571     // Draw and verify results
572     drawQuad(program, "a_position", 0.5f);
573     ASSERT_GL_NO_ERROR();
574     EXPECT_PIXEL_EQ(0, 0, 0, 255, 255, 255);
575 
576     // Modify the green texture to ensure we bind the right index in Reset
577     const std::vector<GLColor> kWhiteData(kSize * kSize, GLColor::white);
578     glActiveTexture(GL_TEXTURE0);
579     glBindTexture(GL_TEXTURE_2D, greenTexture);
580     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
581                  kWhiteData.data());
582 
583     // Empty frames to reach capture end.
584     for (int i = 0; i < 10; i++)
585     {
586         swapBuffers();
587     }
588 }
589 
590 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CapturedTest);
591 // Capture is only supported on the Vulkan backend
592 ANGLE_INSTANTIATE_TEST(CapturedTest, ES3_VULKAN());
593 }  // anonymous namespace
594