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 constexpr char kInactiveVS[] = R"(precision highp float;
43 void main(void) {
44 gl_Position = vec4(0.5, 0.5, 0.5, 1.0);
45 })";
46
47 static constexpr char kInactiveDeferredVS[] = R"(attribute vec4 a_position;
48 attribute vec2 a_texCoord;
49 varying vec2 v_texCoord;
50 void main()
51 {
52 gl_Position = a_position;
53 v_texCoord = a_texCoord;
54 })";
55
56 static constexpr char kInactiveDeferredFS[] = R"(precision mediump float;
57 varying vec2 v_texCoord;
58 uniform sampler2D s_texture;
59 void main()
60 {
61 gl_FragColor = vec4(0.4, 0.4, 0.4, 1.0);
62 gl_FragColor = texture2D(s_texture, v_texCoord);
63 })";
64
65 static constexpr char kActiveDeferredVS[] = R"(attribute vec4 a_position;
66 attribute vec2 a_texCoord;
67 varying vec2 v_texCoord;
68 void main()
69 {
70 gl_Position = a_position;
71 v_texCoord = a_texCoord;
72 })";
73
74 // Create shaders, program but defer compiling & linking, use before capture starts
75 // (Inactive)
76 lateLinkTestVertShaderInactive = glCreateShader(GL_VERTEX_SHADER);
77 const char *lateLinkTestVsSourceArrayInactive[1] = {kInactiveDeferredVS};
78 glShaderSource(lateLinkTestVertShaderInactive, 1, lateLinkTestVsSourceArrayInactive, 0);
79 lateLinkTestFragShaderInactive = glCreateShader(GL_FRAGMENT_SHADER);
80 const char *lateLinkTestFsSourceArrayInactive[1] = {kInactiveDeferredFS};
81 glShaderSource(lateLinkTestFragShaderInactive, 1, lateLinkTestFsSourceArrayInactive, 0);
82 lateLinkTestProgramInactive = glCreateProgram();
83 glAttachShader(lateLinkTestProgramInactive, lateLinkTestVertShaderInactive);
84 glAttachShader(lateLinkTestProgramInactive, lateLinkTestFragShaderInactive);
85
86 // Create inactive program having shader shared with deferred linked program
87 glCompileShader(lateLinkTestVertShaderInactive);
88 glCompileShader(lateLinkTestFragShaderInactive);
89 glLinkProgram(lateLinkTestProgramInactive);
90 ASSERT_GL_NO_ERROR();
91
92 // Create vertex shader and program but defer compiling & linking until capture time
93 // (Active) Use fragment shader attached to inactive program
94 lateLinkTestVertShaderActive = glCreateShader(GL_VERTEX_SHADER);
95 const char *lateLinkTestVsSourceArrayActive[1] = {kActiveDeferredVS};
96 glShaderSource(lateLinkTestVertShaderActive, 1, lateLinkTestVsSourceArrayActive, 0);
97 lateLinkTestProgramActive = glCreateProgram();
98 glAttachShader(lateLinkTestProgramActive, lateLinkTestVertShaderActive);
99 glAttachShader(lateLinkTestProgramActive, lateLinkTestFragShaderInactive);
100 ASSERT_GL_NO_ERROR();
101
102 // Create shader that is unused during capture
103 inactiveProgram = glCreateProgram();
104 inactiveShader = glCreateShader(GL_VERTEX_SHADER);
105 const char *inactiveSourceArray[1] = {kInactiveVS};
106 glShaderSource(inactiveShader, 1, inactiveSourceArray, 0);
107 glCompileShader(inactiveShader);
108 glAttachShader(inactiveProgram, inactiveShader);
109
110 // Create Shader Program before capture begins to use during capture
111 activeBeforeProgram = glCreateProgram();
112 activeBeforeVertShader = glCreateShader(GL_VERTEX_SHADER);
113 activeBeforeFragShader = glCreateShader(GL_FRAGMENT_SHADER);
114 const char *activeVsSourceArray[1] = {kActiveVS};
115 glShaderSource(activeBeforeVertShader, 1, activeVsSourceArray, 0);
116 glCompileShader(activeBeforeVertShader);
117 glAttachShader(activeBeforeProgram, activeBeforeVertShader);
118 const char *activeFsSourceArray[1] = {kActiveFS};
119 glShaderSource(activeBeforeFragShader, 1, activeFsSourceArray, 0);
120 glCompileShader(activeBeforeFragShader);
121 glAttachShader(activeBeforeProgram, activeBeforeFragShader);
122 glLinkProgram(activeBeforeProgram);
123
124 // Get the attr/sampler locations
125 mPositionLoc = glGetAttribLocation(activeBeforeProgram, "a_position");
126 mTexCoordLoc = glGetAttribLocation(activeBeforeProgram, "a_texCoord");
127 mSamplerLoc = glGetUniformLocation(activeBeforeProgram, "s_texture");
128 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
129
130 // Bind the texture object before capture begins
131 glGenTextures(1, &textureNeverBound);
132 glGenTextures(1, &textureBoundBeforeCapture);
133 glBindTexture(GL_TEXTURE_2D, textureBoundBeforeCapture);
134 ASSERT_GL_NO_ERROR();
135
136 const size_t width = 2;
137 const size_t height = 2;
138 GLubyte pixels[width * height * 3] = {
139 255, 0, 0, // Red
140 0, 255, 0, // Green
141 0, 0, 255, // Blue
142 255, 255, 0, // Yellow
143 };
144 // Populate the pre-capture bound texture
145 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
146 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
147 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
148 }
149
testTearDown()150 void testTearDown() override
151 {
152 // Not reached during capture as we hit the End frame earlier.
153
154 if (!mFBOs.empty())
155 {
156 glDeleteFramebuffers(static_cast<GLsizei>(mFBOs.size()), mFBOs.data());
157 }
158
159 glDeleteTextures(1, &textureNeverBound);
160 glDeleteTextures(1, &textureBoundBeforeCapture);
161 glDeleteProgram(inactiveProgram);
162 glDeleteProgram(activeBeforeProgram);
163 glDeleteShader(inactiveShader);
164 glDeleteShader(activeBeforeVertShader);
165 glDeleteShader(activeBeforeFragShader);
166 glDeleteProgram(lateLinkTestProgramInactive);
167 glDeleteProgram(lateLinkTestProgramActive);
168 glDeleteShader(lateLinkTestVertShaderInactive);
169 glDeleteShader(lateLinkTestFragShaderInactive);
170 glDeleteShader(lateLinkTestVertShaderActive);
171 }
172
173 static constexpr char kActiveVS[] = R"(attribute vec4 a_position;
174 attribute vec2 a_texCoord;
175 varying vec2 v_texCoord;
176 void main()
177 {
178 gl_Position = a_position;
179 v_texCoord = a_texCoord;
180 })";
181
182 static constexpr char kActiveFS[] = R"(precision mediump float;
183 varying vec2 v_texCoord;
184 uniform sampler2D s_texture;
185 void main()
186 {
187 gl_FragColor = texture2D(s_texture, v_texCoord);
188 })";
189
190 void frame1();
191 void frame2();
192 void frame3();
193 void frame4();
194
195 std::vector<GLuint> mFBOs;
196
197 // For testing deferred compile/link
198 GLuint lateLinkTestVertShaderInactive;
199 GLuint lateLinkTestFragShaderInactive;
200 GLuint lateLinkTestProgramInactive;
201 GLuint lateLinkTestVertShaderActive;
202 GLuint lateLinkTestProgramActive;
203
204 GLuint inactiveProgram;
205 GLuint inactiveShader;
206
207 GLuint activeBeforeProgram;
208 GLuint activeBeforeVertShader;
209 GLuint activeBeforeFragShader;
210
211 GLuint textureNeverBound;
212 GLuint textureBoundBeforeCapture;
213 GLuint mPositionLoc;
214 GLuint mTexCoordLoc;
215 GLint mSamplerLoc;
216 };
217
frame1()218 void CapturedTest::frame1()
219 {
220 glClearColor(0.25f, 0.5f, 0.5f, 0.5f);
221 glClear(GL_COLOR_BUFFER_BIT);
222 EXPECT_PIXEL_NEAR(0, 0, 64, 128, 128, 128, 1.0);
223
224 GLfloat vertices[] = {
225 -0.75f, 0.25f, 0.0f, // Position 0
226 0.0f, 0.0f, // TexCoord 0
227 -0.75f, -0.75f, 0.0f, // Position 1
228 0.0f, 1.0f, // TexCoord 1
229 0.25f, -0.75f, 0.0f, // Position 2
230 1.0f, 1.0f, // TexCoord 2
231 0.25f, 0.25f, 0.0f, // Position 3
232 1.0f, 0.0f // TexCoord 3
233 };
234
235 GLushort indices[] = {0, 1, 2, 0, 2, 3};
236
237 glClear(GL_COLOR_BUFFER_BIT);
238 glUseProgram(activeBeforeProgram);
239 glVertexAttribPointer(mPositionLoc, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vertices);
240 glVertexAttribPointer(mTexCoordLoc, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vertices + 3);
241 glEnableVertexAttribArray(mPositionLoc);
242 glEnableVertexAttribArray(mTexCoordLoc);
243 glUniform1i(mSamplerLoc, 0);
244
245 // Draw without binding texture during capture
246 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
247 EXPECT_PIXEL_EQ(20, 20, 0, 0, 255, 255);
248
249 glDeleteVertexArrays(1, &mPositionLoc);
250 glDeleteVertexArrays(1, &mTexCoordLoc);
251 }
252
frame2()253 void CapturedTest::frame2()
254 {
255 // Draw using texture created and bound during capture
256
257 GLuint activeDuringProgram;
258 GLuint activeDuringVertShader;
259 GLuint activeDuringFragShader;
260 GLuint positionLoc;
261 GLuint texCoordLoc;
262 GLuint textureBoundDuringCapture;
263 GLint samplerLoc;
264
265 activeDuringProgram = glCreateProgram();
266 activeDuringVertShader = glCreateShader(GL_VERTEX_SHADER);
267 activeDuringFragShader = glCreateShader(GL_FRAGMENT_SHADER);
268 const char *activeVsSourceArray[1] = {kActiveVS};
269 glShaderSource(activeDuringVertShader, 1, activeVsSourceArray, 0);
270 glCompileShader(activeDuringVertShader);
271 glAttachShader(activeDuringProgram, activeDuringVertShader);
272 const char *activeFsSourceArray[1] = {kActiveFS};
273 glShaderSource(activeDuringFragShader, 1, activeFsSourceArray, 0);
274 glCompileShader(activeDuringFragShader);
275 glAttachShader(activeDuringProgram, activeDuringFragShader);
276 glLinkProgram(activeDuringProgram);
277
278 // Get the attr/sampler locations
279 positionLoc = glGetAttribLocation(activeDuringProgram, "a_position");
280 texCoordLoc = glGetAttribLocation(activeDuringProgram, "a_texCoord");
281 samplerLoc = glGetUniformLocation(activeDuringProgram, "s_texture");
282 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
283
284 // Bind the texture object during capture
285 glGenTextures(1, &textureBoundDuringCapture);
286 glBindTexture(GL_TEXTURE_2D, textureBoundDuringCapture);
287 ASSERT_GL_NO_ERROR();
288
289 const size_t width = 2;
290 const size_t height = 2;
291 GLubyte pixels[width * height * 3] = {
292 255, 255, 0, // Yellow
293 0, 0, 255, // Blue
294 0, 255, 0, // Green
295 255, 0, 0, // Red
296 };
297
298 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
299 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
300 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
301
302 GLfloat vertices[] = {
303 -0.25f, 0.75f, 0.0f, // Position 0
304 0.0f, 0.0f, // TexCoord 0
305 -0.25f, -0.25f, 0.0f, // Position 1
306 0.0f, 1.0f, // TexCoord 1
307 0.75f, -0.25f, 0.0f, // Position 2
308 1.0f, 1.0f, // TexCoord 2
309 0.75f, 0.75f, 0.0f, // Position 3
310 1.0f, 0.0f // TexCoord 3
311 };
312 GLushort indices[] = {0, 1, 2, 0, 2, 3};
313
314 glClear(GL_COLOR_BUFFER_BIT);
315 glUseProgram(activeDuringProgram);
316 glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vertices);
317 glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vertices + 3);
318 glEnableVertexAttribArray(positionLoc);
319 glEnableVertexAttribArray(texCoordLoc);
320 glUniform1i(samplerLoc, 0);
321
322 // Draw using texture bound during capture
323 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
324 EXPECT_PIXEL_EQ(108, 108, 0, 0, 255, 255);
325
326 glDeleteTextures(1, &textureBoundDuringCapture);
327 glDeleteVertexArrays(1, &positionLoc);
328 glDeleteVertexArrays(1, &texCoordLoc);
329 glDeleteProgram(activeDuringProgram);
330 glDeleteShader(activeDuringVertShader);
331 glDeleteShader(activeDuringFragShader);
332 }
333
frame3()334 void CapturedTest::frame3()
335 {
336 // TODO: using local objects (with RAII helpers) here that create and destroy objects within the
337 // frame. Maybe move some of this to test Setup.
338
339 constexpr char kVS[] = R"(precision highp float;
340 attribute vec3 attr1;
341 void main(void) {
342 gl_Position = vec4(attr1, 1.0);
343 })";
344
345 constexpr char kFS[] = R"(precision highp float;
346 void main(void) {
347 gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
348 })";
349
350 GLBuffer emptyBuffer;
351 glBindBuffer(GL_ARRAY_BUFFER, emptyBuffer);
352
353 ANGLE_GL_PROGRAM(program, kVS, kFS);
354 glBindAttribLocation(program, 0, "attr1");
355 glLinkProgram(program);
356 ASSERT_TRUE(CheckLinkStatusAndReturnProgram(program, true));
357 glUseProgram(program);
358
359 // Use non-existing attribute 1.
360 glEnableVertexAttribArray(1);
361 glVertexAttribPointer(1, 3, GL_UNSIGNED_BYTE, false, 1, 0);
362
363 glDrawArrays(GL_TRIANGLES, 0, 3);
364 EXPECT_GL_NO_ERROR();
365
366 // Note: RAII destructors called here causing additional GL calls.
367 }
368
frame4()369 void CapturedTest::frame4()
370 {
371 GLuint positionLoc;
372 GLuint texCoordLoc;
373 GLuint lateLinkTestTexture;
374 GLint samplerLoc;
375
376 // Deferred compile/link
377 glCompileShader(lateLinkTestVertShaderActive);
378 glLinkProgram(lateLinkTestProgramActive);
379 ASSERT_GL_NO_ERROR();
380
381 // Get the attr/sampler locations
382 positionLoc = glGetAttribLocation(lateLinkTestProgramActive, "a_position");
383 texCoordLoc = glGetAttribLocation(lateLinkTestProgramActive, "a_texCoord");
384 samplerLoc = glGetUniformLocation(lateLinkTestProgramActive, "s_texture");
385 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
386
387 // Bind the texture object during capture
388 glGenTextures(1, &lateLinkTestTexture);
389 glBindTexture(GL_TEXTURE_2D, lateLinkTestTexture);
390 ASSERT_GL_NO_ERROR();
391
392 const size_t width = 2;
393 const size_t height = 2;
394 GLubyte pixels[width * height * 3] = {
395 255, 255, 0, // Yellow
396 0, 0, 255, // Blue
397 0, 255, 0, // Green
398 255, 0, 0, // Red
399 };
400
401 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels);
402 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
403 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
404
405 GLfloat vertices[] = {
406 -0.25f, 0.75f, 0.0f, // Position 0
407 0.0f, 0.0f, // TexCoord 0
408 -0.25f, -0.25f, 0.0f, // Position 1
409 0.0f, 1.0f, // TexCoord 1
410 0.75f, -0.25f, 0.0f, // Position 2
411 1.0f, 1.0f, // TexCoord 2
412 0.75f, 0.75f, 0.0f, // Position 3
413 1.0f, 0.0f // TexCoord 3
414 };
415 GLushort indices[] = {0, 1, 2, 0, 2, 3};
416
417 glClear(GL_COLOR_BUFFER_BIT);
418 glUseProgram(lateLinkTestProgramActive);
419 glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vertices);
420 glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), vertices + 3);
421 glEnableVertexAttribArray(positionLoc);
422 glEnableVertexAttribArray(texCoordLoc);
423 glUniform1i(samplerLoc, 0);
424
425 // Draw shaders & program created before capture
426 glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
427 EXPECT_PIXEL_EQ(108, 108, 0, 0, 255, 255);
428 }
429
430 // Test captured by capture_tests.py
TEST_P(CapturedTest,MultiFrame)431 TEST_P(CapturedTest, MultiFrame)
432 {
433 // Swap before the first frame so that setup gets its own frame
434 swapBuffers();
435 frame1();
436
437 swapBuffers();
438 frame2();
439
440 swapBuffers();
441 frame3();
442
443 swapBuffers();
444 frame4();
445
446 // Empty frames to reach capture end.
447 for (int i = 0; i < 10; i++)
448 {
449 swapBuffers();
450 }
451 // Note: test teardown adds an additonal swap in
452 // ANGLETestBase::ANGLETestPreTearDown() when --angle-per-test-capture-label
453 }
454
455 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CapturedTest);
456 // Capture is only supported on the Vulkan backend
457 ANGLE_INSTANTIATE_TEST(CapturedTest, ES3_VULKAN());
458
459 } // anonymous namespace
460