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