xref: /aosp_15_r20/external/angle/src/tests/perf_tests/TracePerfTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2020 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 // TracePerf:
7 //   Performance test for ANGLE replaying traces.
8 //
9 
10 #include <gtest/gtest.h>
11 #include "common/PackedEnums.h"
12 #include "common/string_utils.h"
13 #include "common/system_utils.h"
14 #include "tests/perf_tests/ANGLEPerfTest.h"
15 #include "tests/perf_tests/ANGLEPerfTestArgs.h"
16 #include "tests/perf_tests/DrawCallPerfParams.h"
17 #include "util/capture/frame_capture_test_utils.h"
18 #include "util/capture/traces_export.h"
19 #include "util/egl_loader_autogen.h"
20 #include "util/png_utils.h"
21 #include "util/test_utils.h"
22 
23 #if defined(ANGLE_PLATFORM_ANDROID)
24 #    include "util/android/AndroidWindow.h"
25 #endif
26 
27 #include <rapidjson/document.h>
28 #include <rapidjson/istreamwrapper.h>
29 
30 #include <cassert>
31 #include <fstream>
32 #include <functional>
33 #include <sstream>
34 
35 // When --minimize-gpu-work is specified, we want to reduce GPU work to minimum and lift up the CPU
36 // overhead to surface so that we can see how much CPU overhead each driver has for each app trace.
37 // On some driver(s) the bufferSubData/texSubImage calls end up dominating the frame time when the
38 // actual GPU work is minimized. Even reducing the texSubImage calls to only update 1x1 area is not
39 // enough. The driver may be implementing copy on write by cloning the entire texture to another
40 // memory storage for texSubImage call. While this information is also important for performance,
41 // they should be evaluated separately in real app usage scenario, or write stand alone tests for
42 // these. For the purpose of CPU overhead and avoid data copy to dominate the trace, I am using this
43 // flag to noop the texSubImage and bufferSubData call when --minimize-gpu-work is specified. Feel
44 // free to disable this when you have other needs. Or it can be turned to another run time option
45 // when desired.
46 #define NOOP_SUBDATA_SUBIMAGE_FOR_MINIMIZE_GPU_WORK
47 
48 using namespace angle;
49 using namespace egl_platform;
50 
51 namespace
52 {
53 constexpr size_t kMaxPath = 1024;
54 
55 struct TracePerfParams final : public RenderTestParams
56 {
57     // Common default options
TracePerfParams__anon2861a1d10111::TracePerfParams58     TracePerfParams(const TraceInfo &traceInfoIn,
59                     GLESDriverType driverType,
60                     EGLenum platformType,
61                     EGLenum deviceType)
62         : traceInfo(traceInfoIn)
63     {
64         majorVersion = traceInfo.contextClientMajorVersion;
65         minorVersion = traceInfo.contextClientMinorVersion;
66         windowWidth  = traceInfo.drawSurfaceWidth;
67         windowHeight = traceInfo.drawSurfaceHeight;
68         colorSpace   = traceInfo.drawSurfaceColorSpace;
69 
70         // Display the frame after every drawBenchmark invocation
71         iterationsPerStep = 1;
72 
73         driver                   = driverType;
74         eglParameters.renderer   = platformType;
75         eglParameters.deviceType = deviceType;
76 
77         ASSERT(!gOffscreen || !gVsync);
78 
79         if (gOffscreen)
80         {
81             surfaceType = SurfaceType::Offscreen;
82         }
83         if (gVsync)
84         {
85             surfaceType = SurfaceType::WindowWithVSync;
86         }
87 
88         // Force on features if we're validating serialization.
89         if (gTraceTestValidation)
90         {
91             // Enable limits when validating traces because we usually turn off capture.
92             eglParameters.enable(Feature::EnableCaptureLimits);
93 
94             // This feature should also be enabled in capture to mirror the replay.
95             eglParameters.enable(Feature::ForceInitShaderVariables);
96         }
97     }
98 
story__anon2861a1d10111::TracePerfParams99     std::string story() const override
100     {
101         std::stringstream strstr;
102         strstr << RenderTestParams::story() << "_" << traceInfo.name;
103         return strstr.str();
104     }
105 
106     TraceInfo traceInfo = {};
107 };
108 
109 class TracePerfTest : public ANGLERenderTest
110 {
111   public:
112     TracePerfTest(std::unique_ptr<const TracePerfParams> params);
113 
114     void startTest() override;
115     void initializeBenchmark() override;
116     void destroyBenchmark() override;
117     void drawBenchmark() override;
118 
119     // TODO(http://anglebug.com/42264418): Add support for creating EGLSurface:
120     // - eglCreatePbufferSurface()
121     // - eglCreateWindowSurface()
122     EGLContext onEglCreateContext(EGLDisplay display,
123                                   EGLConfig config,
124                                   EGLContext share_context,
125                                   EGLint const *attrib_list);
126     void onEglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context);
127     EGLContext onEglGetCurrentContext();
128     EGLImage onEglCreateImage(EGLDisplay display,
129                               EGLContext context,
130                               EGLenum target,
131                               EGLClientBuffer buffer,
132                               const EGLAttrib *attrib_list);
133     EGLImageKHR onEglCreateImageKHR(EGLDisplay display,
134                                     EGLContext context,
135                                     EGLenum target,
136                                     EGLClientBuffer buffer,
137                                     const EGLint *attrib_list);
138     EGLBoolean onEglDestroyImage(EGLDisplay display, EGLImage image);
139     EGLBoolean onEglDestroyImageKHR(EGLDisplay display, EGLImage image);
140     EGLSync onEglCreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list);
141     EGLSync onEglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list);
142     EGLBoolean onEglDestroySync(EGLDisplay dpy, EGLSync sync);
143     EGLBoolean onEglDestroySyncKHR(EGLDisplay dpy, EGLSync sync);
144     EGLint onEglClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTimeKHR timeout);
145     EGLint onEglClientWaitSyncKHR(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTimeKHR timeout);
146     EGLint onEglGetError();
147     EGLDisplay onEglGetCurrentDisplay();
148 
149     void onReplayFramebufferChange(GLenum target, GLuint framebuffer);
150     void onReplayInvalidateFramebuffer(GLenum target,
151                                        GLsizei numAttachments,
152                                        const GLenum *attachments);
153     void onReplayInvalidateSubFramebuffer(GLenum target,
154                                           GLsizei numAttachments,
155                                           const GLenum *attachments,
156                                           GLint x,
157                                           GLint y,
158                                           GLsizei width,
159                                           GLsizei height);
160     void onReplayDrawBuffers(GLsizei n, const GLenum *bufs);
161     void onReplayReadBuffer(GLenum src);
162     void onReplayDiscardFramebufferEXT(GLenum target,
163                                        GLsizei numAttachments,
164                                        const GLenum *attachments);
165 
166     void validateSerializedState(const char *serializedState, const char *fileName, uint32_t line);
167 
168     bool isDefaultFramebuffer(GLenum target) const;
169 
170     double getHostTimeFromGLTime(GLint64 glTime);
171 
frameCount() const172     uint32_t frameCount() const
173     {
174         const TraceInfo &traceInfo = mParams->traceInfo;
175         return traceInfo.frameEnd - traceInfo.frameStart + 1;
176     }
177 
getStepAlignment() const178     int getStepAlignment() const override
179     {
180         // Align step counts to the number of frames in a trace.
181         return static_cast<int>(frameCount());
182     }
183 
TestBody()184     void TestBody() override { run(); }
185 
traceNameIs(const char * name) const186     bool traceNameIs(const char *name) const
187     {
188         return strncmp(name, mParams->traceInfo.name, kTraceInfoMaxNameLen) == 0;
189     }
190 
191   private:
192     struct QueryInfo
193     {
194         GLuint beginTimestampQuery;
195         GLuint endTimestampQuery;
196         GLuint framebuffer;
197     };
198 
199     struct TimeSample
200     {
201         GLint64 glTime;
202         double hostTime;
203     };
204 
205     void sampleTime();
206 
207     enum class ScreenshotType
208     {
209         kFrame,
210         kGrid,  // Grid of frames (framebuffer 0) in offscreen mode
211     };
212     void saveScreenshotIfEnabled(ScreenshotType screenshotType);
213     void saveScreenshot(const std::string &screenshotName);
214 
215     std::unique_ptr<const TracePerfParams> mParams;
216 
217     uint32_t mStartFrame;
218     uint32_t mEndFrame;
219 
220     // For tracking RenderPass/FBO change timing.
221     QueryInfo mCurrentQuery = {};
222     std::vector<QueryInfo> mRunningQueries;
223     std::vector<TimeSample> mTimeline;
224 
225     bool mUseTimestampQueries                                           = false;
226     // Note: more than 2 offscreen buffers can cause races, surface is double buffered so real-world
227     // apps can rely on (now broken) assumptions about GPU completion of a previous frame
228     static constexpr int mMaxOffscreenBufferCount                       = 2;
229     std::array<GLuint, mMaxOffscreenBufferCount> mOffscreenFramebuffers = {0, 0};
230     std::array<GLuint, mMaxOffscreenBufferCount> mOffscreenTextures     = {0, 0};
231     std::array<GLsync, mMaxOffscreenBufferCount> mOffscreenSyncs        = {0, 0};
232     GLuint mOffscreenDepthStencil                                       = 0;
233     int mWindowWidth                                                    = 0;
234     int mWindowHeight                                                   = 0;
235     GLuint mDrawFramebufferBinding                                      = 0;
236     GLuint mReadFramebufferBinding                                      = 0;
237     uint32_t mCurrentFrame                                              = 0;
238     uint32_t mCurrentIteration                                          = 0;
239     uint32_t mCurrentOffscreenGridIteration                             = 0;
240     uint32_t mOffscreenFrameCount                                       = 0;
241     uint32_t mTotalFrameCount                                           = 0;
242     bool mScreenshotSaved                                               = false;
243     int32_t mScreenshotFrame                                            = gScreenshotFrame;
244     std::unique_ptr<TraceLibrary> mTraceReplay;
245 
246     static constexpr int kFpsNumFrames               = 4;
247     std::array<double, kFpsNumFrames> mFpsStartTimes = {0, 0, 0, 0};
248 };
249 
250 TracePerfTest *gCurrentTracePerfTest = nullptr;
251 
252 // Don't forget to include KHRONOS_APIENTRY in override methods. Necessary on Win/x86.
EglCreateContext(EGLDisplay display,EGLConfig config,EGLContext share_context,EGLint const * attrib_list)253 EGLContext KHRONOS_APIENTRY EglCreateContext(EGLDisplay display,
254                                              EGLConfig config,
255                                              EGLContext share_context,
256                                              EGLint const *attrib_list)
257 {
258     return gCurrentTracePerfTest->onEglCreateContext(display, config, share_context, attrib_list);
259 }
260 
EglMakeCurrent(EGLDisplay display,EGLSurface draw,EGLSurface read,EGLContext context)261 void KHRONOS_APIENTRY EglMakeCurrent(EGLDisplay display,
262                                      EGLSurface draw,
263                                      EGLSurface read,
264                                      EGLContext context)
265 {
266     gCurrentTracePerfTest->onEglMakeCurrent(display, draw, read, context);
267 }
268 
EglGetCurrentContext()269 EGLContext KHRONOS_APIENTRY EglGetCurrentContext()
270 {
271     return gCurrentTracePerfTest->onEglGetCurrentContext();
272 }
273 
EglCreateImage(EGLDisplay display,EGLContext context,EGLenum target,EGLClientBuffer buffer,const EGLAttrib * attrib_list)274 EGLImage KHRONOS_APIENTRY EglCreateImage(EGLDisplay display,
275                                          EGLContext context,
276                                          EGLenum target,
277                                          EGLClientBuffer buffer,
278                                          const EGLAttrib *attrib_list)
279 {
280     return gCurrentTracePerfTest->onEglCreateImage(display, context, target, buffer, attrib_list);
281 }
282 
EglCreateImageKHR(EGLDisplay display,EGLContext context,EGLenum target,EGLClientBuffer buffer,const EGLint * attrib_list)283 EGLImageKHR KHRONOS_APIENTRY EglCreateImageKHR(EGLDisplay display,
284                                                EGLContext context,
285                                                EGLenum target,
286                                                EGLClientBuffer buffer,
287                                                const EGLint *attrib_list)
288 {
289     return gCurrentTracePerfTest->onEglCreateImageKHR(display, context, target, buffer,
290                                                       attrib_list);
291 }
292 
EglDestroyImage(EGLDisplay display,EGLImage image)293 EGLBoolean KHRONOS_APIENTRY EglDestroyImage(EGLDisplay display, EGLImage image)
294 {
295     return gCurrentTracePerfTest->onEglDestroyImage(display, image);
296 }
297 
EglDestroyImageKHR(EGLDisplay display,EGLImage image)298 EGLBoolean KHRONOS_APIENTRY EglDestroyImageKHR(EGLDisplay display, EGLImage image)
299 {
300     return gCurrentTracePerfTest->onEglDestroyImageKHR(display, image);
301 }
302 
EglCreateSync(EGLDisplay dpy,EGLenum type,const EGLAttrib * attrib_list)303 EGLSync KHRONOS_APIENTRY EglCreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list)
304 {
305     return gCurrentTracePerfTest->onEglCreateSync(dpy, type, attrib_list);
306 }
307 
EglCreateSyncKHR(EGLDisplay dpy,EGLenum type,const EGLint * attrib_list)308 EGLSync KHRONOS_APIENTRY EglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list)
309 {
310     return gCurrentTracePerfTest->onEglCreateSyncKHR(dpy, type, attrib_list);
311 }
312 
EglDestroySync(EGLDisplay dpy,EGLSync sync)313 EGLBoolean KHRONOS_APIENTRY EglDestroySync(EGLDisplay dpy, EGLSync sync)
314 {
315     return gCurrentTracePerfTest->onEglDestroySync(dpy, sync);
316 }
317 
EglDestroySyncKHR(EGLDisplay dpy,EGLSync sync)318 EGLBoolean KHRONOS_APIENTRY EglDestroySyncKHR(EGLDisplay dpy, EGLSync sync)
319 {
320     return gCurrentTracePerfTest->onEglDestroySyncKHR(dpy, sync);
321 }
322 
EglClientWaitSync(EGLDisplay dpy,EGLSync sync,EGLint flags,EGLTimeKHR timeout)323 EGLint KHRONOS_APIENTRY EglClientWaitSync(EGLDisplay dpy,
324                                           EGLSync sync,
325                                           EGLint flags,
326                                           EGLTimeKHR timeout)
327 {
328     return gCurrentTracePerfTest->onEglClientWaitSync(dpy, sync, flags, timeout);
329 }
EglClientWaitSyncKHR(EGLDisplay dpy,EGLSync sync,EGLint flags,EGLTimeKHR timeout)330 EGLint KHRONOS_APIENTRY EglClientWaitSyncKHR(EGLDisplay dpy,
331                                              EGLSync sync,
332                                              EGLint flags,
333                                              EGLTimeKHR timeout)
334 {
335     return gCurrentTracePerfTest->onEglClientWaitSyncKHR(dpy, sync, flags, timeout);
336 }
337 
EglGetError()338 EGLint KHRONOS_APIENTRY EglGetError()
339 {
340     return gCurrentTracePerfTest->onEglGetError();
341 }
342 
EglGetCurrentDisplay()343 EGLDisplay KHRONOS_APIENTRY EglGetCurrentDisplay()
344 {
345     return gCurrentTracePerfTest->onEglGetCurrentDisplay();
346 }
347 
BindFramebufferProc(GLenum target,GLuint framebuffer)348 void KHRONOS_APIENTRY BindFramebufferProc(GLenum target, GLuint framebuffer)
349 {
350     gCurrentTracePerfTest->onReplayFramebufferChange(target, framebuffer);
351 }
352 
InvalidateFramebufferProc(GLenum target,GLsizei numAttachments,const GLenum * attachments)353 void KHRONOS_APIENTRY InvalidateFramebufferProc(GLenum target,
354                                                 GLsizei numAttachments,
355                                                 const GLenum *attachments)
356 {
357     gCurrentTracePerfTest->onReplayInvalidateFramebuffer(target, numAttachments, attachments);
358 }
359 
InvalidateSubFramebufferProc(GLenum target,GLsizei numAttachments,const GLenum * attachments,GLint x,GLint y,GLsizei width,GLsizei height)360 void KHRONOS_APIENTRY InvalidateSubFramebufferProc(GLenum target,
361                                                    GLsizei numAttachments,
362                                                    const GLenum *attachments,
363                                                    GLint x,
364                                                    GLint y,
365                                                    GLsizei width,
366                                                    GLsizei height)
367 {
368     gCurrentTracePerfTest->onReplayInvalidateSubFramebuffer(target, numAttachments, attachments, x,
369                                                             y, width, height);
370 }
371 
DrawBuffersProc(GLsizei n,const GLenum * bufs)372 void KHRONOS_APIENTRY DrawBuffersProc(GLsizei n, const GLenum *bufs)
373 {
374     gCurrentTracePerfTest->onReplayDrawBuffers(n, bufs);
375 }
376 
ReadBufferProc(GLenum src)377 void KHRONOS_APIENTRY ReadBufferProc(GLenum src)
378 {
379     gCurrentTracePerfTest->onReplayReadBuffer(src);
380 }
381 
DiscardFramebufferEXTProc(GLenum target,GLsizei numAttachments,const GLenum * attachments)382 void KHRONOS_APIENTRY DiscardFramebufferEXTProc(GLenum target,
383                                                 GLsizei numAttachments,
384                                                 const GLenum *attachments)
385 {
386     gCurrentTracePerfTest->onReplayDiscardFramebufferEXT(target, numAttachments, attachments);
387 }
388 
ViewportMinimizedProc(GLint x,GLint y,GLsizei width,GLsizei height)389 void KHRONOS_APIENTRY ViewportMinimizedProc(GLint x, GLint y, GLsizei width, GLsizei height)
390 {
391     glViewport(x, y, 1, 1);
392 }
393 
ScissorMinimizedProc(GLint x,GLint y,GLsizei width,GLsizei height)394 void KHRONOS_APIENTRY ScissorMinimizedProc(GLint x, GLint y, GLsizei width, GLsizei height)
395 {
396     glScissor(x, y, 1, 1);
397 }
398 
399 // Interpose the calls that generate actual GPU work
DrawElementsMinimizedProc(GLenum mode,GLsizei count,GLenum type,const void * indices)400 void KHRONOS_APIENTRY DrawElementsMinimizedProc(GLenum mode,
401                                                 GLsizei count,
402                                                 GLenum type,
403                                                 const void *indices)
404 {
405     glDrawElements(GL_POINTS, 1, type, indices);
406 }
407 
DrawElementsIndirectMinimizedProc(GLenum mode,GLenum type,const void * indirect)408 void KHRONOS_APIENTRY DrawElementsIndirectMinimizedProc(GLenum mode,
409                                                         GLenum type,
410                                                         const void *indirect)
411 {
412     glDrawElementsInstancedBaseVertex(GL_POINTS, 1, type, 0, 1, 0);
413 }
414 
DrawElementsInstancedMinimizedProc(GLenum mode,GLsizei count,GLenum type,const void * indices,GLsizei instancecount)415 void KHRONOS_APIENTRY DrawElementsInstancedMinimizedProc(GLenum mode,
416                                                          GLsizei count,
417                                                          GLenum type,
418                                                          const void *indices,
419                                                          GLsizei instancecount)
420 {
421     glDrawElementsInstanced(GL_POINTS, 1, type, indices, 1);
422 }
423 
DrawElementsBaseVertexMinimizedProc(GLenum mode,GLsizei count,GLenum type,const void * indices,GLint basevertex)424 void KHRONOS_APIENTRY DrawElementsBaseVertexMinimizedProc(GLenum mode,
425                                                           GLsizei count,
426                                                           GLenum type,
427                                                           const void *indices,
428                                                           GLint basevertex)
429 {
430     glDrawElementsBaseVertex(GL_POINTS, 1, type, indices, basevertex);
431 }
432 
DrawElementsInstancedBaseVertexMinimizedProc(GLenum mode,GLsizei count,GLenum type,const void * indices,GLsizei instancecount,GLint basevertex)433 void KHRONOS_APIENTRY DrawElementsInstancedBaseVertexMinimizedProc(GLenum mode,
434                                                                    GLsizei count,
435                                                                    GLenum type,
436                                                                    const void *indices,
437                                                                    GLsizei instancecount,
438                                                                    GLint basevertex)
439 {
440     glDrawElementsInstancedBaseVertex(GL_POINTS, 1, type, indices, 1, basevertex);
441 }
442 
DrawRangeElementsMinimizedProc(GLenum mode,GLuint start,GLuint end,GLsizei count,GLenum type,const void * indices)443 void KHRONOS_APIENTRY DrawRangeElementsMinimizedProc(GLenum mode,
444                                                      GLuint start,
445                                                      GLuint end,
446                                                      GLsizei count,
447                                                      GLenum type,
448                                                      const void *indices)
449 {
450     glDrawRangeElements(GL_POINTS, start, end, 1, type, indices);
451 }
452 
DrawArraysMinimizedProc(GLenum mode,GLint first,GLsizei count)453 void KHRONOS_APIENTRY DrawArraysMinimizedProc(GLenum mode, GLint first, GLsizei count)
454 {
455     glDrawArrays(GL_POINTS, first, 1);
456 }
457 
DrawArraysInstancedMinimizedProc(GLenum mode,GLint first,GLsizei count,GLsizei instancecount)458 void KHRONOS_APIENTRY DrawArraysInstancedMinimizedProc(GLenum mode,
459                                                        GLint first,
460                                                        GLsizei count,
461                                                        GLsizei instancecount)
462 {
463     glDrawArraysInstanced(GL_POINTS, first, 1, 1);
464 }
465 
DrawArraysIndirectMinimizedProc(GLenum mode,const void * indirect)466 void KHRONOS_APIENTRY DrawArraysIndirectMinimizedProc(GLenum mode, const void *indirect)
467 {
468     glDrawArraysInstanced(GL_POINTS, 0, 1, 1);
469 }
470 
DispatchComputeMinimizedProc(GLuint num_groups_x,GLuint num_groups_y,GLuint num_groups_z)471 void KHRONOS_APIENTRY DispatchComputeMinimizedProc(GLuint num_groups_x,
472                                                    GLuint num_groups_y,
473                                                    GLuint num_groups_z)
474 {
475     glDispatchCompute(1, 1, 1);
476 }
477 
DispatchComputeIndirectMinimizedProc(GLintptr indirect)478 void KHRONOS_APIENTRY DispatchComputeIndirectMinimizedProc(GLintptr indirect)
479 {
480     glDispatchCompute(1, 1, 1);
481 }
482 
483 // Interpose the calls that generate data copying work
BufferDataMinimizedProc(GLenum target,GLsizeiptr size,const void * data,GLenum usage)484 void KHRONOS_APIENTRY BufferDataMinimizedProc(GLenum target,
485                                               GLsizeiptr size,
486                                               const void *data,
487                                               GLenum usage)
488 {
489     glBufferData(target, size, nullptr, usage);
490 }
491 
BufferSubDataMinimizedProc(GLenum target,GLintptr offset,GLsizeiptr size,const void * data)492 void KHRONOS_APIENTRY BufferSubDataMinimizedProc(GLenum target,
493                                                  GLintptr offset,
494                                                  GLsizeiptr size,
495                                                  const void *data)
496 {
497 #if !defined(NOOP_SUBDATA_SUBIMAGE_FOR_MINIMIZE_GPU_WORK)
498     glBufferSubData(target, offset, 1, data);
499 #endif
500 }
501 
MapBufferRangeMinimizedProc(GLenum target,GLintptr offset,GLsizeiptr length,GLbitfield access)502 void *KHRONOS_APIENTRY MapBufferRangeMinimizedProc(GLenum target,
503                                                    GLintptr offset,
504                                                    GLsizeiptr length,
505                                                    GLbitfield access)
506 {
507     access |= GL_MAP_UNSYNCHRONIZED_BIT;
508     return glMapBufferRange(target, offset, length, access);
509 }
510 
TexImage2DMinimizedProc(GLenum target,GLint level,GLint internalformat,GLsizei width,GLsizei height,GLint border,GLenum format,GLenum type,const void * pixels)511 void KHRONOS_APIENTRY TexImage2DMinimizedProc(GLenum target,
512                                               GLint level,
513                                               GLint internalformat,
514                                               GLsizei width,
515                                               GLsizei height,
516                                               GLint border,
517                                               GLenum format,
518                                               GLenum type,
519                                               const void *pixels)
520 {
521     GLint unpackBuffer = 0;
522     glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &unpackBuffer);
523     if (unpackBuffer)
524     {
525         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
526     }
527     glTexImage2D(target, level, internalformat, width, height, border, format, type, nullptr);
528     if (unpackBuffer)
529     {
530         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, unpackBuffer);
531     }
532 }
533 
TexSubImage2DMinimizedProc(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLsizei width,GLsizei height,GLenum format,GLenum type,const void * pixels)534 void KHRONOS_APIENTRY TexSubImage2DMinimizedProc(GLenum target,
535                                                  GLint level,
536                                                  GLint xoffset,
537                                                  GLint yoffset,
538                                                  GLsizei width,
539                                                  GLsizei height,
540                                                  GLenum format,
541                                                  GLenum type,
542                                                  const void *pixels)
543 {
544 #if !defined(NOOP_SUBDATA_SUBIMAGE_FOR_MINIMIZE_GPU_WORK)
545     glTexSubImage2D(target, level, xoffset, yoffset, 1, 1, format, type, pixels);
546 #endif
547 }
548 
TexImage3DMinimizedProc(GLenum target,GLint level,GLint internalformat,GLsizei width,GLsizei height,GLsizei depth,GLint border,GLenum format,GLenum type,const void * pixels)549 void KHRONOS_APIENTRY TexImage3DMinimizedProc(GLenum target,
550                                               GLint level,
551                                               GLint internalformat,
552                                               GLsizei width,
553                                               GLsizei height,
554                                               GLsizei depth,
555                                               GLint border,
556                                               GLenum format,
557                                               GLenum type,
558                                               const void *pixels)
559 {
560     GLint unpackBuffer = 0;
561     glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &unpackBuffer);
562     if (unpackBuffer)
563     {
564         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
565     }
566     glTexImage3D(target, level, internalformat, width, height, depth, border, format, type,
567                  nullptr);
568     if (unpackBuffer)
569     {
570         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, unpackBuffer);
571     }
572 }
573 
TexSubImage3DMinimizedProc(GLenum target,GLint level,GLint xoffset,GLint yoffset,GLint zoffset,GLsizei width,GLsizei height,GLsizei depth,GLenum format,GLenum type,const void * pixels)574 void KHRONOS_APIENTRY TexSubImage3DMinimizedProc(GLenum target,
575                                                  GLint level,
576                                                  GLint xoffset,
577                                                  GLint yoffset,
578                                                  GLint zoffset,
579                                                  GLsizei width,
580                                                  GLsizei height,
581                                                  GLsizei depth,
582                                                  GLenum format,
583                                                  GLenum type,
584                                                  const void *pixels)
585 {
586 #if !defined(NOOP_SUBDATA_SUBIMAGE_FOR_MINIMIZE_GPU_WORK)
587     glTexSubImage3D(target, level, xoffset, yoffset, zoffset, 1, 1, 1, format, type, pixels);
588 #endif
589 }
590 
GenerateMipmapMinimizedProc(GLenum target)591 void KHRONOS_APIENTRY GenerateMipmapMinimizedProc(GLenum target)
592 {
593     // Noop it for now. There is a risk that this will leave an incomplete mipmap chain and cause
594     // other issues. If this turns out to be a real issue with app traces, we can turn this into a
595     // glTexImage2D call for each generated level.
596 }
597 
BlitFramebufferMinimizedProc(GLint srcX0,GLint srcY0,GLint srcX1,GLint srcY1,GLint dstX0,GLint dstY0,GLint dstX1,GLint dstY1,GLbitfield mask,GLenum filter)598 void KHRONOS_APIENTRY BlitFramebufferMinimizedProc(GLint srcX0,
599                                                    GLint srcY0,
600                                                    GLint srcX1,
601                                                    GLint srcY1,
602                                                    GLint dstX0,
603                                                    GLint dstY0,
604                                                    GLint dstX1,
605                                                    GLint dstY1,
606                                                    GLbitfield mask,
607                                                    GLenum filter)
608 {
609     glBlitFramebuffer(srcX0, srcY0, srcX0 + 1, srcY0 + 1, dstX0, dstY0, dstX0 + 1, dstY0 + 1, mask,
610                       filter);
611 }
612 
ReadPixelsMinimizedProc(GLint x,GLint y,GLsizei width,GLsizei height,GLenum format,GLenum type,void * pixels)613 void KHRONOS_APIENTRY ReadPixelsMinimizedProc(GLint x,
614                                               GLint y,
615                                               GLsizei width,
616                                               GLsizei height,
617                                               GLenum format,
618                                               GLenum type,
619                                               void *pixels)
620 {
621     glReadPixels(x, y, 1, 1, format, type, pixels);
622 }
623 
BeginTransformFeedbackMinimizedProc(GLenum primitiveMode)624 void KHRONOS_APIENTRY BeginTransformFeedbackMinimizedProc(GLenum primitiveMode)
625 {
626     glBeginTransformFeedback(GL_POINTS);
627 }
628 
TraceLoadProc(const char * procName)629 angle::GenericProc KHRONOS_APIENTRY TraceLoadProc(const char *procName)
630 {
631     // EGL
632     if (strcmp(procName, "eglCreateContext") == 0)
633     {
634         return reinterpret_cast<angle::GenericProc>(EglCreateContext);
635     }
636     if (strcmp(procName, "eglMakeCurrent") == 0)
637     {
638         return reinterpret_cast<angle::GenericProc>(EglMakeCurrent);
639     }
640     if (strcmp(procName, "eglGetCurrentContext") == 0)
641     {
642         return reinterpret_cast<angle::GenericProc>(EglGetCurrentContext);
643     }
644     if (strcmp(procName, "eglCreateImage") == 0)
645     {
646         return reinterpret_cast<angle::GenericProc>(EglCreateImage);
647     }
648     if (strcmp(procName, "eglCreateImageKHR") == 0)
649     {
650         return reinterpret_cast<angle::GenericProc>(EglCreateImageKHR);
651     }
652     if (strcmp(procName, "eglDestroyImage") == 0)
653     {
654         return reinterpret_cast<angle::GenericProc>(EglDestroyImage);
655     }
656     if (strcmp(procName, "eglDestroyImageKHR") == 0)
657     {
658         return reinterpret_cast<angle::GenericProc>(EglDestroyImageKHR);
659     }
660     if (strcmp(procName, "eglCreateSync") == 0)
661     {
662         return reinterpret_cast<angle::GenericProc>(EglCreateSync);
663     }
664     if (strcmp(procName, "eglCreateSyncKHR") == 0)
665     {
666         return reinterpret_cast<angle::GenericProc>(EglCreateSyncKHR);
667     }
668     if (strcmp(procName, "eglDestroySync") == 0)
669     {
670         return reinterpret_cast<angle::GenericProc>(EglDestroySync);
671     }
672     if (strcmp(procName, "eglDestroySyncKHR") == 0)
673     {
674         return reinterpret_cast<angle::GenericProc>(EglDestroySyncKHR);
675     }
676     if (strcmp(procName, "eglClientWaitSync") == 0)
677     {
678         return reinterpret_cast<angle::GenericProc>(EglClientWaitSync);
679     }
680     if (strcmp(procName, "eglClientWaitSyncKHR") == 0)
681     {
682         return reinterpret_cast<angle::GenericProc>(EglClientWaitSyncKHR);
683     }
684     if (strcmp(procName, "eglGetError") == 0)
685     {
686         return reinterpret_cast<angle::GenericProc>(EglGetError);
687     }
688     if (strcmp(procName, "eglGetCurrentDisplay") == 0)
689     {
690         return reinterpret_cast<angle::GenericProc>(EglGetCurrentDisplay);
691     }
692 
693     // GLES
694     if (strcmp(procName, "glBindFramebuffer") == 0 || strcmp(procName, "glBindFramebufferOES") == 0)
695     {
696         return reinterpret_cast<angle::GenericProc>(BindFramebufferProc);
697     }
698     if (strcmp(procName, "glInvalidateFramebuffer") == 0)
699     {
700         return reinterpret_cast<angle::GenericProc>(InvalidateFramebufferProc);
701     }
702     if (strcmp(procName, "glInvalidateSubFramebuffer") == 0)
703     {
704         return reinterpret_cast<angle::GenericProc>(InvalidateSubFramebufferProc);
705     }
706     if (strcmp(procName, "glDrawBuffers") == 0)
707     {
708         return reinterpret_cast<angle::GenericProc>(DrawBuffersProc);
709     }
710     if (strcmp(procName, "glReadBuffer") == 0)
711     {
712         return reinterpret_cast<angle::GenericProc>(ReadBufferProc);
713     }
714     if (strcmp(procName, "glDiscardFramebufferEXT") == 0)
715     {
716         return reinterpret_cast<angle::GenericProc>(DiscardFramebufferEXTProc);
717     }
718 
719     if (gMinimizeGPUWork)
720     {
721         if (strcmp(procName, "glViewport") == 0)
722         {
723             return reinterpret_cast<angle::GenericProc>(ViewportMinimizedProc);
724         }
725 
726         if (strcmp(procName, "glScissor") == 0)
727         {
728             return reinterpret_cast<angle::GenericProc>(ScissorMinimizedProc);
729         }
730 
731         // Interpose the calls that generate actual GPU work
732         if (strcmp(procName, "glDrawElements") == 0)
733         {
734             return reinterpret_cast<angle::GenericProc>(DrawElementsMinimizedProc);
735         }
736         if (strcmp(procName, "glDrawElementsIndirect") == 0)
737         {
738             return reinterpret_cast<angle::GenericProc>(DrawElementsIndirectMinimizedProc);
739         }
740         if (strcmp(procName, "glDrawElementsInstanced") == 0 ||
741             strcmp(procName, "glDrawElementsInstancedEXT") == 0)
742         {
743             return reinterpret_cast<angle::GenericProc>(DrawElementsInstancedMinimizedProc);
744         }
745         if (strcmp(procName, "glDrawElementsBaseVertex") == 0 ||
746             strcmp(procName, "glDrawElementsBaseVertexEXT") == 0 ||
747             strcmp(procName, "glDrawElementsBaseVertexOES") == 0)
748         {
749             return reinterpret_cast<angle::GenericProc>(DrawElementsBaseVertexMinimizedProc);
750         }
751         if (strcmp(procName, "glDrawElementsInstancedBaseVertex") == 0 ||
752             strcmp(procName, "glDrawElementsInstancedBaseVertexEXT") == 0 ||
753             strcmp(procName, "glDrawElementsInstancedBaseVertexOES") == 0)
754         {
755             return reinterpret_cast<angle::GenericProc>(
756                 DrawElementsInstancedBaseVertexMinimizedProc);
757         }
758         if (strcmp(procName, "glDrawRangeElements") == 0)
759         {
760             return reinterpret_cast<angle::GenericProc>(DrawRangeElementsMinimizedProc);
761         }
762         if (strcmp(procName, "glDrawArrays") == 0)
763         {
764             return reinterpret_cast<angle::GenericProc>(DrawArraysMinimizedProc);
765         }
766         if (strcmp(procName, "glDrawArraysInstanced") == 0 ||
767             strcmp(procName, "glDrawArraysInstancedEXT") == 0)
768         {
769             return reinterpret_cast<angle::GenericProc>(DrawArraysInstancedMinimizedProc);
770         }
771         if (strcmp(procName, "glDrawArraysIndirect") == 0)
772         {
773             return reinterpret_cast<angle::GenericProc>(DrawArraysIndirectMinimizedProc);
774         }
775         if (strcmp(procName, "glDispatchCompute") == 0)
776         {
777             return reinterpret_cast<angle::GenericProc>(DispatchComputeMinimizedProc);
778         }
779         if (strcmp(procName, "glDispatchComputeIndirect") == 0)
780         {
781             return reinterpret_cast<angle::GenericProc>(DispatchComputeIndirectMinimizedProc);
782         }
783 
784         // Interpose the calls that generate data copying work
785         if (strcmp(procName, "glBufferData") == 0)
786         {
787             return reinterpret_cast<angle::GenericProc>(BufferDataMinimizedProc);
788         }
789         if (strcmp(procName, "glBufferSubData") == 0)
790         {
791             return reinterpret_cast<angle::GenericProc>(BufferSubDataMinimizedProc);
792         }
793         if (strcmp(procName, "glMapBufferRange") == 0 ||
794             strcmp(procName, "glMapBufferRangeEXT") == 0)
795         {
796             return reinterpret_cast<angle::GenericProc>(MapBufferRangeMinimizedProc);
797         }
798         if (strcmp(procName, "glTexImage2D") == 0)
799         {
800             return reinterpret_cast<angle::GenericProc>(TexImage2DMinimizedProc);
801         }
802         if (strcmp(procName, "glTexImage3D") == 0)
803         {
804             return reinterpret_cast<angle::GenericProc>(TexImage3DMinimizedProc);
805         }
806         if (strcmp(procName, "glTexSubImage2D") == 0)
807         {
808             return reinterpret_cast<angle::GenericProc>(TexSubImage2DMinimizedProc);
809         }
810         if (strcmp(procName, "glTexSubImage3D") == 0)
811         {
812             return reinterpret_cast<angle::GenericProc>(TexSubImage3DMinimizedProc);
813         }
814         if (strcmp(procName, "glGenerateMipmap") == 0 ||
815             strcmp(procName, "glGenerateMipmapOES") == 0)
816         {
817             return reinterpret_cast<angle::GenericProc>(GenerateMipmapMinimizedProc);
818         }
819         if (strcmp(procName, "glBlitFramebuffer") == 0)
820         {
821             return reinterpret_cast<angle::GenericProc>(BlitFramebufferMinimizedProc);
822         }
823         if (strcmp(procName, "glReadPixels") == 0)
824         {
825             return reinterpret_cast<angle::GenericProc>(ReadPixelsMinimizedProc);
826         }
827         if (strcmp(procName, "glBeginTransformFeedback") == 0)
828         {
829             return reinterpret_cast<angle::GenericProc>(BeginTransformFeedbackMinimizedProc);
830         }
831     }
832 
833     return gCurrentTracePerfTest->getGLWindow()->getProcAddress(procName);
834 }
835 
ValidateSerializedState(const char * serializedState,const char * fileName,uint32_t line)836 void ValidateSerializedState(const char *serializedState, const char *fileName, uint32_t line)
837 {
838     gCurrentTracePerfTest->validateSerializedState(serializedState, fileName, line);
839 }
840 
841 constexpr char kTraceTestFolder[] = "src/tests/restricted_traces";
842 
FindTraceTestDataPath(const char * traceName,char * testDataDirOut,size_t maxDataDirLen)843 bool FindTraceTestDataPath(const char *traceName, char *testDataDirOut, size_t maxDataDirLen)
844 {
845     char relativeTestDataDir[kMaxPath] = {};
846     snprintf(relativeTestDataDir, kMaxPath, "%s%c%s", kTraceTestFolder, GetPathSeparator(),
847              traceName);
848     return angle::FindTestDataPath(relativeTestDataDir, testDataDirOut, maxDataDirLen);
849 }
850 
FindRootTraceTestDataPath(char * testDataDirOut,size_t maxDataDirLen)851 bool FindRootTraceTestDataPath(char *testDataDirOut, size_t maxDataDirLen)
852 {
853     return angle::FindTestDataPath(kTraceTestFolder, testDataDirOut, maxDataDirLen);
854 }
855 
TracePerfTest(std::unique_ptr<const TracePerfParams> params)856 TracePerfTest::TracePerfTest(std::unique_ptr<const TracePerfParams> params)
857     : ANGLERenderTest("TracePerf", *params.get(), "ms"),
858       mParams(std::move(params)),
859       mStartFrame(0),
860       mEndFrame(0)
861 {
862     bool isAMD            = IsAMD() && !mParams->isSwiftshader();
863     bool isAMDLinux       = isAMD && IsLinux();
864     bool isAMDLinuxANGLE  = isAMDLinux && mParams->isANGLE();
865     bool isAMDLinuxNative = isAMDLinux && !mParams->isANGLE();
866     bool isAMDWin         = isAMD && IsWindows();
867     bool isAMDWinANGLE    = isAMDWin && mParams->isANGLE();
868     // bool isAMDWinNative   = isAMDWin && !mParams->isANGLE();
869 
870     bool isIntel            = IsIntel() && !mParams->isSwiftshader();
871     bool isIntelLinux       = isIntel && IsLinux();
872     bool isIntelLinuxANGLE  = isIntelLinux && mParams->isANGLE();
873     bool isIntelLinuxNative = isIntelLinux && !mParams->isANGLE();
874     bool isIntelWin         = IsWindows() && isIntel;
875     bool isIntelWinANGLE    = isIntelWin && mParams->isANGLE();
876     bool isIntelWinNative   = isIntelWin && !mParams->isANGLE();
877 
878     bool isNVIDIA            = IsNVIDIA() && !mParams->isSwiftshader();
879     bool isNVIDIALinux       = isNVIDIA && IsLinux();
880     bool isNVIDIALinuxANGLE  = isNVIDIALinux && mParams->isANGLE();
881     bool isNVIDIALinuxNative = isNVIDIALinux && !mParams->isANGLE();
882     bool isNVIDIAWin         = isNVIDIA && IsWindows();
883     bool isNVIDIAWinANGLE    = isNVIDIAWin && mParams->isANGLE();
884     bool isNVIDIAWinNative   = isNVIDIAWin && !mParams->isANGLE();
885 
886     if (!mParams->traceInfo.initialized)
887     {
888         failTest("Failed to load trace json.");
889         return;
890     }
891 
892     for (std::string extension : mParams->traceInfo.requiredExtensions)
893     {
894         addExtensionPrerequisite(extension);
895     }
896 
897     if (!mParams->traceInfo.keyFrames.empty())
898     {
899         // Only support one keyFrame for now
900         if (mParams->traceInfo.keyFrames.size() != 1)
901         {
902             WARN() << "Multiple keyframes detected, only using the first";
903         }
904 
905         // Only use keyFrame if the user didn't specify a value.
906         if (gScreenshotFrame == kDefaultScreenshotFrame)
907         {
908             mScreenshotFrame = mParams->traceInfo.keyFrames[0];
909             INFO() << "Trace contains keyframe, using frame " << mScreenshotFrame
910                    << " for screenshot";
911         }
912         else
913         {
914             WARN() << "Ignoring keyframe, user requested frame " << mScreenshotFrame
915                    << " for screenshot";
916             if (mScreenshotFrame == kAllFrames)
917             {
918                 WARN() << "Capturing screenshots of all frames since requested frame was "
919                        << kAllFrames;
920             }
921         }
922     }
923 
924     if (isIntelWinANGLE && traceNameIs("manhattan_10"))
925     {
926         skipTest(
927             "TODO: http://anglebug.com/40096690 This fails after the upgrade to the 26.20.100.7870 "
928             "driver");
929     }
930 
931     if (isIntelWinNative && traceNameIs("angry_birds_2_1500"))
932     {
933         skipTest(
934             "TODO: http://anglebug.com/40096702 Fails on older Intel drivers. Passes in newer");
935     }
936 
937     if (traceNameIs("cod_mobile"))
938     {
939         if (isIntelWin)
940         {
941             skipTest("http://anglebug.com/42265065 Flaky on Intel/windows");
942         }
943     }
944 
945     if (isIntelLinuxANGLE && traceNameIs("octopath_traveler"))
946     {
947         skipTest("TODO: http://anglebug.com/378666645 Non-deterministic image on Ubuntu 22.04");
948     }
949 
950     if (isIntelLinuxANGLE && traceNameIs("dead_by_daylight"))
951     {
952         skipTest("TODO: http://anglebug.com/378666645 Non-deterministic image on Ubuntu 22.04");
953     }
954 
955     if (isIntelWinANGLE && traceNameIs("black_desert_mobile"))
956     {
957         skipTest(
958             "TODO: http://anglebug.com/42266346 Non-deterministic image on 31.0.101.2111 driver");
959     }
960 
961     if (isIntelWinANGLE && traceNameIs("the_gardens_between"))
962     {
963         skipTest(
964             "TODO: http://anglebug.com/42266346 Non-deterministic image on 31.0.101.2111 driver");
965     }
966 
967     if (isIntelWinANGLE && traceNameIs("animal_crossing"))
968     {
969         skipTest(
970             "TODO: http://anglebug.com/353690308 Non-deterministic image on UHD770 31.0.101.5333");
971     }
972 
973     if (isIntelWinANGLE && traceNameIs("black_clover_m"))
974     {
975         skipTest(
976             "TODO: http://anglebug.com/353690308 Non-deterministic image on UHD770 31.0.101.5333");
977     }
978 
979     if (traceNameIs("brawl_stars"))
980     {
981         addExtensionPrerequisite("GL_EXT_shadow_samplers");
982     }
983 
984     if (traceNameIs("free_fire"))
985     {
986         addExtensionPrerequisite("GL_OES_EGL_image_external");
987     }
988 
989     if (traceNameIs("marvel_contest_of_champions"))
990     {
991         addExtensionPrerequisite("GL_EXT_color_buffer_half_float");
992     }
993 
994     if (traceNameIs("world_of_tanks_blitz"))
995     {
996         addExtensionPrerequisite("GL_EXT_disjoint_timer_query");
997     }
998 
999     if (traceNameIs("dragon_ball_legends"))
1000     {
1001         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1002     }
1003 
1004     if (traceNameIs("lego_legacy"))
1005     {
1006         addExtensionPrerequisite("GL_EXT_shadow_samplers");
1007     }
1008 
1009     if (traceNameIs("world_war_doh"))
1010     {
1011         // Linux+NVIDIA doesn't support GL_KHR_texture_compression_astc_ldr (possibly others also)
1012         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1013     }
1014 
1015     if (traceNameIs("saint_seiya_awakening"))
1016     {
1017         addExtensionPrerequisite("GL_EXT_shadow_samplers");
1018 
1019         if (isIntelLinuxANGLE)
1020         {
1021             skipTest(
1022                 "TODO: https://anglebug.com/42264055 Linux+Intel generates 'Framebuffer is "
1023                 "incomplete' "
1024                 "errors");
1025         }
1026     }
1027 
1028     if (traceNameIs("magic_tiles_3"))
1029     {
1030         // Linux+NVIDIA doesn't support GL_KHR_texture_compression_astc_ldr (possibly others also)
1031         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1032     }
1033 
1034     if (traceNameIs("real_gangster_crime"))
1035     {
1036         // Linux+NVIDIA doesn't support GL_KHR_texture_compression_astc_ldr (possibly others also)
1037         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1038 
1039         // Intel doesn't support external images.
1040         addExtensionPrerequisite("GL_OES_EGL_image_external");
1041 
1042         if (isIntelLinuxNative || isAMDLinuxNative)
1043         {
1044             skipTest(
1045                 "http://anglebug.com/42264358 Failing on Linux Intel and AMD due to invalid enum");
1046         }
1047     }
1048 
1049     if (traceNameIs("asphalt_8"))
1050     {
1051         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1052     }
1053 
1054     if (traceNameIs("hearthstone"))
1055     {
1056         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1057     }
1058 
1059     if (traceNameIs("efootball_pes_2021"))
1060     {
1061         if (isIntelLinuxANGLE)
1062         {
1063             skipTest(
1064                 "TODO: https://anglebug.com/42264055 Linux+Intel generate 'Framebuffer is "
1065                 "incomplete' errors with the Vulkan backend");
1066         }
1067     }
1068 
1069     if (traceNameIs("shadow_fight_2"))
1070     {
1071         addExtensionPrerequisite("GL_OES_EGL_image_external");
1072         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1073     }
1074 
1075     if (traceNameIs("rise_of_kingdoms"))
1076     {
1077         addExtensionPrerequisite("GL_OES_EGL_image_external");
1078     }
1079 
1080     if (traceNameIs("happy_color"))
1081     {
1082         if (isAMDWinANGLE)
1083         {
1084             skipTest(
1085                 "http://anglebug.com/42264158 Generates incorrect results on AMD Windows Vulkan");
1086         }
1087     }
1088 
1089     if (traceNameIs("bus_simulator_indonesia"))
1090     {
1091         if (isIntelLinuxNative || isAMDLinuxNative)
1092         {
1093             skipTest(
1094                 "TODO: https://anglebug.com/42264164 native GLES generates GL_INVALID_OPERATION");
1095         }
1096     }
1097 
1098     if (traceNameIs("messenger_lite"))
1099     {
1100         if (isNVIDIAWinANGLE)
1101         {
1102             skipTest(
1103                 "https://anglebug.com/42264199 Incorrect pixels on NVIDIA Windows for first frame");
1104         }
1105     }
1106 
1107     if (traceNameIs("among_us"))
1108     {
1109         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1110     }
1111 
1112     if (traceNameIs("car_parking_multiplayer"))
1113     {
1114         if (isNVIDIAWinNative || isNVIDIALinuxNative)
1115         {
1116             skipTest(
1117                 "TODO: https://anglebug.com/42264147 NVIDIA native driver spews undefined behavior "
1118                 "warnings");
1119         }
1120         if (isIntelWinANGLE)
1121         {
1122             skipTest("https://anglebug.com/42264261 Device lost on Win Intel");
1123         }
1124     }
1125 
1126     if (traceNameIs("fifa_mobile"))
1127     {
1128         if (isIntelWinANGLE)
1129         {
1130             skipTest(
1131                 "TODO: http://anglebug.com/42264415 Intel Windows Vulkan flakily renders entirely "
1132                 "black");
1133         }
1134     }
1135 
1136     if (traceNameIs("extreme_car_driving_simulator"))
1137     {
1138         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1139     }
1140 
1141     if (traceNameIs("plants_vs_zombies_2"))
1142     {
1143         if (isAMDWinANGLE)
1144         {
1145             skipTest("TODO: http://crbug.com/1187752 Corrupted image");
1146         }
1147     }
1148 
1149     if (traceNameIs("junes_journey"))
1150     {
1151         addExtensionPrerequisite("GL_OES_EGL_image_external");
1152     }
1153 
1154     if (traceNameIs("ragnarok_m_eternal_love"))
1155     {
1156         addExtensionPrerequisite("GL_OES_EGL_image_external");
1157         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1158     }
1159 
1160     if (traceNameIs("league_of_legends_wild_rift"))
1161     {
1162         addExtensionPrerequisite("GL_OES_EGL_image_external");
1163         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1164 
1165         if (isIntelLinuxANGLE)
1166         {
1167             skipTest("TODO: http://anglebug.com/42264351 Trace is crashing on Intel Linux");
1168         }
1169     }
1170 
1171     if (traceNameIs("aztec_ruins"))
1172     {
1173         if (isIntelWinANGLE)
1174         {
1175             skipTest(
1176                 "TODO: http://anglebug.com/353690308 Non-deterministic image on UHD770 "
1177                 "31.0.101.5333");
1178         }
1179 
1180         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1181     }
1182 
1183     if (traceNameIs("dragon_raja"))
1184     {
1185         addExtensionPrerequisite("GL_OES_EGL_image_external");
1186 
1187         if (isIntelLinuxANGLE)
1188         {
1189             skipTest(
1190                 "TODO: http://anglebug.com/42264343 Intel Linux errors with 'Framebuffer is "
1191                 "incomplete' on Vulkan");
1192         }
1193     }
1194 
1195     if (traceNameIs("hill_climb_racing") || traceNameIs("dead_trigger_2") ||
1196         traceNameIs("disney_mirrorverse") || traceNameIs("cut_the_rope") ||
1197         traceNameIs("geometry_dash") || traceNameIs("critical_ops"))
1198     {
1199         if (IsAndroid() && (IsPixel4() || IsPixel4XL()) && !mParams->isANGLE())
1200         {
1201             skipTest(
1202                 "http://anglebug.com/42264359 Adreno gives a driver error with empty/small draw "
1203                 "calls");
1204         }
1205     }
1206 
1207     if (traceNameIs("avakin_life"))
1208     {
1209         addExtensionPrerequisite("GL_OES_EGL_image_external");
1210     }
1211 
1212     if (traceNameIs("professional_baseball_spirits"))
1213     {
1214         if (isIntelLinuxANGLE || isAMDLinuxNative)
1215         {
1216             skipTest(
1217                 "TODO: https://anglebug.com/42264363 Linux+Mesa/RADV Vulkan generates "
1218                 "GL_INVALID_FRAMEBUFFER_OPERATION. Mesa versions below 20.3.5 produce the same "
1219                 "issue on Linux+Mesa/Intel Vulkan");
1220         }
1221     }
1222 
1223     if (traceNameIs("call_break_offline_card_game"))
1224     {
1225         if (isIntelLinuxANGLE)
1226         {
1227             skipTest(
1228                 "TODO: http://anglebug.com/42264374 Intel Linux Vulkan errors with 'Framebuffer is "
1229                 "incomplete'");
1230         }
1231     }
1232 
1233     if (traceNameIs("ludo_king"))
1234     {
1235         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1236     }
1237 
1238     if (traceNameIs("summoners_war"))
1239     {
1240         if (isIntelWinANGLE)
1241         {
1242             skipTest("TODO: http://anglebug.com/42264477 GL_INVALID_ENUM on Windows/Intel");
1243         }
1244 
1245         if (isIntelLinuxNative)
1246         {
1247             skipTest("https://anglebug.com/42267118 fails on newer OS/driver");
1248         }
1249     }
1250 
1251     if (traceNameIs("pokemon_go"))
1252     {
1253         addExtensionPrerequisite("GL_EXT_texture_cube_map_array");
1254         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1255 
1256         if (isIntelLinuxANGLE)
1257         {
1258             skipTest("TODO: http://anglebug.com/42264520 Intel Linux crashing on teardown");
1259         }
1260 
1261         if (isIntelWinANGLE)
1262         {
1263             skipTest("TODO: http://anglebug.com/42264526 Intel Windows timing out periodically");
1264         }
1265     }
1266 
1267     if (traceNameIs("cookie_run_kingdom"))
1268     {
1269         addExtensionPrerequisite("GL_EXT_texture_cube_map_array");
1270         addExtensionPrerequisite("GL_OES_EGL_image_external");
1271     }
1272 
1273     if (traceNameIs("genshin_impact"))
1274     {
1275         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1276 
1277         if (isNVIDIAWinANGLE || isNVIDIALinuxANGLE)
1278         {
1279             skipTest("http://anglebug.com/42265965 Nondeterministic noise between runs");
1280         }
1281 
1282         if (isIntelLinuxANGLE)
1283         {
1284             skipTest("TODO: http://anglebug.com/42264560 Crashes on Linux Intel Vulkan");
1285         }
1286 
1287         if (IsQualcomm() && mParams->isVulkan())
1288         {
1289             skipTest("TODO: http://anglebug.com/378464990 Crashes on Qualcomm (Pixel 4)");
1290         }
1291 
1292         if (!Is64Bit())
1293         {
1294             skipTest("Genshin is too large to handle in 32-bit mode");
1295         }
1296     }
1297 
1298     if (traceNameIs("mario_kart_tour"))
1299     {
1300         if (isIntelLinuxNative)
1301         {
1302             skipTest("http://anglebug.com/42265205 Fails on native Mesa");
1303         }
1304     }
1305 
1306     if (traceNameIs("pubg_mobile_skydive") || traceNameIs("pubg_mobile_battle_royale"))
1307     {
1308         addExtensionPrerequisite("GL_EXT_texture_buffer");
1309 
1310         if (isIntelWinNative || isNVIDIALinuxNative || isNVIDIAWinNative)
1311         {
1312             skipTest(
1313                 "TODO: http://anglebug.com/42264759 Internal errors on Windows/Intel and NVIDIA");
1314         }
1315     }
1316 
1317     if (traceNameIs("sakura_school_simulator"))
1318     {
1319         if (isIntelWin)
1320         {
1321             skipTest("http://anglebug.com/42264813 Flaky on Intel");
1322         }
1323     }
1324 
1325     if (traceNameIs("scrabble_go"))
1326     {
1327         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1328     }
1329 
1330     if (traceNameIs("world_of_kings"))
1331     {
1332         addExtensionPrerequisite("GL_OES_EGL_image_external");
1333         if (isIntelWin)
1334         {
1335             skipTest("http://anglebug.com/42264888 Flaky on Intel");
1336         }
1337     }
1338 
1339     if (traceNameIs("nier_reincarnation"))
1340     {
1341         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1342     }
1343 
1344     if (traceNameIs("mini_world"))
1345     {
1346         if ((IsPixel4() || IsPixel4XL()) && mParams->isVulkan())
1347         {
1348             skipTest(
1349                 "TODO: http://anglebug.com/42264956 Vulkan Test failure on Pixel4XL due to vulkan "
1350                 "validation error VUID-vkDestroyBuffer-buffer-00922");
1351         }
1352         if (isIntelWinNative)
1353         {
1354             skipTest("https://anglebug.com/42266865 Flaky on native Win Intel");
1355         }
1356     }
1357 
1358     if (traceNameIs("pokemon_unite"))
1359     {
1360         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1361 
1362         if (IsIntel())
1363         {
1364             skipTest(
1365                 "http://anglebug.com/42265045 nondeterministic on Intel+Windows. Crashes on Linux "
1366                 "Intel");
1367         }
1368     }
1369 
1370     if (traceNameIs("world_cricket_championship_2"))
1371     {
1372         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1373 
1374         if (isIntelLinuxNative)
1375         {
1376             skipTest("http://anglebug.com/42265152 Native test timing out on Intel Linux");
1377         }
1378     }
1379 
1380     if (traceNameIs("zillow"))
1381     {
1382         if (isNVIDIAWinANGLE || isNVIDIALinuxANGLE)
1383         {
1384             skipTest("http://anglebug.com/42265153 Crashing in Vulkan backend");
1385         }
1386 
1387         if (isIntelLinuxNative)
1388         {
1389             skipTest("https://anglebug.com/42267118 fails on newer OS/driver");
1390         }
1391     }
1392 
1393     if (traceNameIs("township"))
1394     {
1395         addExtensionPrerequisite("GL_OES_EGL_image_external");
1396     }
1397 
1398     if (traceNameIs("asphalt_9"))
1399     {
1400         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1401     }
1402 
1403     if (traceNameIs("pubg_mobile_launch"))
1404     {
1405         if (isNVIDIALinuxNative)
1406         {
1407             skipTest("http://anglebug.com/40644857 Crashing in Nvidia GLES driver");
1408         }
1409     }
1410 
1411     if (traceNameIs("star_wars_kotor"))
1412     {
1413         if (IsLinux() && mParams->isSwiftshader())
1414         {
1415             skipTest("TODO: http://anglebug.com/42266034 Flaky on Swiftshader");
1416         }
1417     }
1418 
1419     if (traceNameIs("dead_by_daylight"))
1420     {
1421         addExtensionPrerequisite("GL_EXT_shader_framebuffer_fetch");
1422     }
1423 
1424     if (traceNameIs("war_planet_online"))
1425     {
1426         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1427     }
1428 
1429     if (traceNameIs("lords_mobile"))
1430     {
1431         // http://anglebug.com/42265475 - glTexStorage2DEXT is not exposed on Pixel 4 native
1432         addExtensionPrerequisite("GL_EXT_texture_storage");
1433     }
1434 
1435     if (traceNameIs("marvel_strike_force"))
1436     {
1437         if ((IsAndroid() && IsQualcomm()) && !mParams->isANGLE())
1438         {
1439             skipTest(
1440                 "http://anglebug.com/42265489 Qualcomm native driver gets confused about the state "
1441                 "of "
1442                 "a buffer that was recreated during the trace");
1443         }
1444     }
1445 
1446     if (traceNameIs("real_racing3"))
1447     {
1448         addExtensionPrerequisite("GL_EXT_shader_framebuffer_fetch");
1449         if (isNVIDIAWinANGLE || isNVIDIALinuxANGLE)
1450         {
1451             skipTest(
1452                 "http://anglebug.com/377923479 SYNC-HAZARD-WRITE-AFTER-WRITE on Linux 535.183.01 "
1453                 "Windows 31.0.15.4601");
1454         }
1455     }
1456 
1457     if (traceNameIs("blade_and_soul_revolution"))
1458     {
1459         addExtensionPrerequisite("GL_EXT_texture_buffer");
1460         addExtensionPrerequisite("GL_EXT_shader_framebuffer_fetch");
1461         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1462     }
1463 
1464     if (traceNameIs("scary_teacher_3d"))
1465     {
1466         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1467     }
1468 
1469     if (traceNameIs("car_chase"))
1470     {
1471         if (isIntelWin)
1472         {
1473             skipTest("http://anglebug.com/42265648 Fails on Intel HD 630 Mobile");
1474         }
1475 
1476         if (isIntelLinux)
1477         {
1478             skipTest("http://anglebug.com/42265598#comment9 Flaky hang on UHD630 Mesa 20.0.8");
1479         }
1480 
1481         if (isNVIDIAWinANGLE || isNVIDIALinuxANGLE)
1482         {
1483             skipTest("http://anglebug.com/42265598 Renders incorrectly on NVIDIA");
1484         }
1485 
1486         addExtensionPrerequisite("GL_EXT_geometry_shader");
1487         addExtensionPrerequisite("GL_EXT_primitive_bounding_box");
1488         addExtensionPrerequisite("GL_EXT_tessellation_shader");
1489         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1490         addExtensionPrerequisite("GL_EXT_texture_cube_map_array");
1491     }
1492 
1493     if (traceNameIs("pokemon_masters_ex"))
1494     {
1495         if (isIntelLinux)
1496         {
1497             skipTest(
1498                 "https://issuetracker.google.com/u/2/issues/326199738#comment3 Renders incorrectly "
1499                 "on Intel Linux");
1500         }
1501     }
1502 
1503     if (traceNameIs("aztec_ruins_high"))
1504     {
1505         if (isIntelWinANGLE)
1506         {
1507             skipTest(
1508                 "TODO: http://anglebug.com/353690308 Non-deterministic image on UHD770 "
1509                 "31.0.101.5333");
1510         }
1511 
1512         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1513     }
1514 
1515     if (traceNameIs("special_forces_group_2"))
1516     {
1517         addExtensionPrerequisite("GL_EXT_texture_buffer");
1518     }
1519 
1520     if (traceNameIs("tessellation"))
1521     {
1522         if (isNVIDIAWinANGLE || isNVIDIALinuxANGLE)
1523         {
1524             skipTest("http://anglebug.com/42265714 Tessellation driver bugs on Nvidia");
1525         }
1526 
1527         addExtensionPrerequisite("GL_EXT_geometry_shader");
1528         addExtensionPrerequisite("GL_EXT_primitive_bounding_box");
1529         addExtensionPrerequisite("GL_EXT_tessellation_shader");
1530         addExtensionPrerequisite("GL_EXT_texture_cube_map_array");
1531     }
1532 
1533     if (traceNameIs("basemark_gpu"))
1534     {
1535         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1536     }
1537 
1538     if (traceNameIs("mortal_kombat"))
1539     {
1540         addExtensionPrerequisite("GL_EXT_texture_buffer");
1541     }
1542 
1543     if (traceNameIs("ni_no_kuni"))
1544     {
1545         addExtensionPrerequisite("GL_EXT_shader_framebuffer_fetch");
1546         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1547     }
1548 
1549     if (traceNameIs("octopath_traveler"))
1550     {
1551         addExtensionPrerequisite("GL_EXT_shader_framebuffer_fetch");
1552         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1553     }
1554 
1555     if (traceNameIs("antutu_refinery"))
1556     {
1557         if (isIntelLinuxANGLE || isAMDLinuxANGLE)
1558         {
1559             skipTest("https://anglebug.com/342545097 fails on Mesa 23.2.1");
1560         }
1561 
1562         if (isIntelWinANGLE)
1563         {
1564             skipTest("https://anglebug.com/379886383 times out on Windows Intel");
1565         }
1566 
1567         addExtensionPrerequisite("GL_ANDROID_extension_pack_es31a");
1568     }
1569 
1570     if (traceNameIs("botworld_adventure"))
1571     {
1572         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1573     }
1574 
1575     if (traceNameIs("eve_echoes"))
1576     {
1577         if (IsQualcomm() && mParams->isVulkan())
1578         {
1579             skipTest(
1580                 "TODO: http://anglebug.com/42266157 Test crashes in LLVM on Qualcomm (Pixel 4)");
1581         }
1582     }
1583 
1584     if (traceNameIs("life_is_strange"))
1585     {
1586         if (isNVIDIAWinANGLE)
1587         {
1588             skipTest("http://anglebug.com/42266193 Renders incorrectly on Nvidia Windows");
1589         }
1590 
1591         if (isNVIDIALinuxANGLE)
1592         {
1593             skipTest("https://anglebug.com/362728695 Renders incorrectly on Linux/NVIDIA");
1594         }
1595 
1596         addExtensionPrerequisite("GL_EXT_texture_buffer");
1597         addExtensionPrerequisite("GL_EXT_texture_cube_map_array");
1598     }
1599 
1600     if (traceNameIs("survivor_io"))
1601     {
1602         if (isNVIDIAWinANGLE)
1603         {
1604             skipTest("http://anglebug.com/42266203 Renders incorrectly on Nvidia Windows");
1605         }
1606 
1607         if (isIntelWinNative)
1608         {
1609             skipTest(
1610                 "http://anglebug.com/42266207 Programs fail to link on Intel Windows native "
1611                 "driver, "
1612                 "citing MAX_UNIFORM_LOCATIONS exceeded");
1613         }
1614     }
1615 
1616     if (traceNameIs("minetest"))
1617     {
1618         if (isIntelLinuxNative)
1619         {
1620             skipTest("https://anglebug.com/42267118 fails on newer OS/driver");
1621         }
1622         addExtensionPrerequisite("GL_EXT_texture_format_BGRA8888");
1623         addIntegerPrerequisite(GL_MAX_TEXTURE_UNITS, 4);
1624     }
1625 
1626     if (traceNameIs("diablo_immortal"))
1627     {
1628         if (IsQualcomm() && mParams->isVulkan())
1629         {
1630             skipTest("TODO: http://anglebug.com/378464990 Crashes on Qualcomm (Pixel 4)");
1631         }
1632 
1633         addExtensionPrerequisite("GL_EXT_shader_framebuffer_fetch");
1634     }
1635 
1636     if (traceNameIs("mu_origin_3"))
1637     {
1638         addExtensionPrerequisite("GL_EXT_texture_buffer");
1639         addExtensionPrerequisite("GL_EXT_shader_framebuffer_fetch");
1640         addExtensionPrerequisite("GL_OES_EGL_image_external");
1641     }
1642 
1643     if (traceNameIs("catalyst_black"))
1644     {
1645         addExtensionPrerequisite("GL_EXT_shader_framebuffer_fetch");
1646     }
1647 
1648     if (traceNameIs("five_nights_at_freddys"))
1649     {
1650         if (isIntelWinANGLE)
1651         {
1652             skipTest("http://anglebug.com/42266395 Too slow on Win Intel Vulkan");
1653         }
1654     }
1655 
1656     if (traceNameIs("pubg_mobile_launch"))
1657     {
1658         if (isIntelWinNative || isIntelWinANGLE)
1659         {
1660             skipTest("http://anglebug.com/42266395 Too slow on Win Intel native and Vulkan");
1661         }
1662     }
1663 
1664     if (traceNameIs("beach_buggy_racing"))
1665     {
1666         if (isIntelWinANGLE)
1667         {
1668             skipTest("http://anglebug.com/42266401 Flaky context lost on Win Intel Vulkan");
1669         }
1670     }
1671 
1672     if (traceNameIs("aliexpress"))
1673     {
1674         if (isIntelWinNative)
1675         {
1676             skipTest("http://anglebug.com/42266401 Flaky failure on Win Intel native");
1677         }
1678     }
1679 
1680     if (traceNameIs("final_fantasy"))
1681     {
1682         if (IsAndroid() && IsPixel6() && !mParams->isANGLE())
1683         {
1684             skipTest("http://anglebug.com/42266403 Crashes on Pixel 6 native");
1685         }
1686     }
1687 
1688     if (traceNameIs("limbo"))
1689     {
1690         addExtensionPrerequisite("GL_EXT_shader_framebuffer_fetch");
1691 
1692         // For LUMINANCE8_ALPHA8_EXT
1693         addExtensionPrerequisite("GL_EXT_texture_storage");
1694     }
1695 
1696     if (traceNameIs("into_the_dead_2"))
1697     {
1698         if (isNVIDIAWinANGLE)
1699         {
1700             skipTest("http://anglebug.com/42266499 Non-deterministic trace");
1701         }
1702     }
1703 
1704     if (traceNameIs("arknights"))
1705     {
1706         // Intel doesn't support external images.
1707         addExtensionPrerequisite("GL_OES_EGL_image_external");
1708     }
1709 
1710     if (traceNameIs("street_fighter_duel"))
1711     {
1712         if (isNVIDIAWinANGLE)
1713         {
1714             skipTest("https://anglebug.com/42266525 NVIDIA Windows flaky diffs");
1715         }
1716     }
1717 
1718     if (traceNameIs("honkai_star_rail"))
1719     {
1720         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1721         if (isIntelWin)
1722         {
1723             skipTest("https://anglebug.com/42266613 Consistently stuck on Intel/windows");
1724         }
1725     }
1726 
1727     if (traceNameIs("gangstar_vegas"))
1728     {
1729         if (mParams->isSwiftshader())
1730         {
1731             skipTest("TODO: http://anglebug.com/42266611 Missing shadows on Swiftshader");
1732         }
1733     }
1734 
1735     if (traceNameIs("respawnables"))
1736     {
1737         if (!mParams->isANGLE() && (IsWindows() || IsLinux()))
1738         {
1739             skipTest("TODO: https://anglebug.com/42266627 Undefined behavior on native");
1740         }
1741     }
1742 
1743     if (traceNameIs("street_fighter_iv_ce"))
1744     {
1745         if (isIntelLinuxNative)
1746         {
1747             skipTest("https://anglebug.com/42267118 fails on newer OS/driver");
1748         }
1749 
1750         if (mParams->isSwiftshader())
1751         {
1752             skipTest("https://anglebug.com/42266679 Too slow on Swiftshader (large keyframe)");
1753         }
1754     }
1755 
1756     if (traceNameIs("monster_hunter_stories"))
1757     {
1758         if (isIntelWinANGLE)
1759         {
1760             skipTest("http://anglebug.com/42266025 Flaky context lost on Win Intel Vulkan");
1761         }
1762     }
1763 
1764     if (traceNameIs("injustice_2"))
1765     {
1766         if (isNVIDIAWinANGLE)
1767         {
1768             skipTest("https://anglebug.com/42266746 NVIDIA Windows flaky diffs");
1769         }
1770     }
1771 
1772     if (traceNameIs("toca_life_world"))
1773     {
1774         addExtensionPrerequisite("GL_OES_EGL_image_external");
1775     }
1776 
1777     if (traceNameIs("poppy_playtime"))
1778     {
1779         addExtensionPrerequisite("GL_OES_EGL_image_external");
1780         addIntegerPrerequisite(GL_MAX_TEXTURE_SIZE, 16383);
1781     }
1782 
1783     if (traceNameIs("dr_driving"))
1784     {
1785         if (isIntelLinuxNative)
1786         {
1787             skipTest("https://anglebug.com/42267118 fails on newer OS/driver");
1788         }
1789     }
1790 
1791     if (traceNameIs("plague_inc"))
1792     {
1793         if (isIntelLinuxNative)
1794         {
1795             skipTest("https://anglebug.com/42267118 fails on newer OS/driver");
1796         }
1797     }
1798 
1799     if (traceNameIs("sonic_the_hedgehog"))
1800     {
1801         if (isIntelLinuxNative)
1802         {
1803             skipTest("https://anglebug.com/42267118 fails on newer OS/driver");
1804         }
1805     }
1806 
1807     if (traceNameIs("wayward_souls"))
1808     {
1809         if (isIntelLinuxNative)
1810         {
1811             skipTest("https://anglebug.com/42267118 fails on newer OS/driver");
1812         }
1813     }
1814 
1815     if (traceNameIs("wordscapes"))
1816     {
1817         if (isIntelLinuxNative)
1818         {
1819             skipTest("https://anglebug.com/42267118 fails on newer OS/driver");
1820         }
1821     }
1822 
1823     if (traceNameIs("zenonia_4"))
1824     {
1825         if (isIntelLinuxNative)
1826         {
1827             skipTest("https://anglebug.com/42267118 fails on newer OS/driver");
1828         }
1829     }
1830 
1831     if (traceNameIs("zombie_smasher"))
1832     {
1833         if (isIntelLinuxNative)
1834         {
1835             skipTest("https://anglebug.com/42267118 fails on newer OS/driver");
1836         }
1837     }
1838 
1839     if (traceNameIs("modern_combat_5"))
1840     {
1841         if (IsPixel6() && !IsAndroid14OrNewer())
1842         {
1843             skipTest(
1844                 "https://issuetracker.google.com/42267261 Causing thermal failures on Pixel 6 with "
1845                 "Android 13");
1846         }
1847     }
1848 
1849     if (traceNameIs("grand_mountain_adventure"))
1850     {
1851         addIntegerPrerequisite(GL_MAX_TEXTURE_SIZE, 11016);
1852     }
1853 
1854     if (traceNameIs("passmark_simple"))
1855     {
1856         if (isIntelLinuxNative)
1857         {
1858             skipTest("https://anglebug.com/42267118 fails on newer OS/driver");
1859         }
1860         addExtensionPrerequisite("GL_OES_framebuffer_object");
1861     }
1862 
1863     if (traceNameIs("passmark_complex"))
1864     {
1865         if (isIntelLinuxNative)
1866         {
1867             skipTest("b/362801312 eglCreateContext fails on Mesa 23.2.1");
1868         }
1869     }
1870 
1871     if (traceNameIs("critical_ops"))
1872     {
1873         if (isNVIDIALinuxANGLE || isNVIDIAWinANGLE)
1874         {
1875             skipTest("https://anglebug.com/365524876 Renders incorrectly on Nvidia");
1876         }
1877     }
1878 
1879     if (traceNameIs("dota_underlords"))
1880     {
1881         if (isNVIDIALinuxANGLE || isNVIDIAWinANGLE)
1882         {
1883             skipTest(
1884                 "https://anglebug.com/369533074 Flaky on Nvidia Linux 535.183.1.0 Windows "
1885                 "31.0.15.4601");
1886         }
1887     }
1888 
1889     if (traceNameIs("going_balls"))
1890     {
1891         if (isIntelWinANGLE)
1892         {
1893             skipTest("https://issuetracker.google.com/372513853 Nondeterministic on Windows Intel");
1894         }
1895     }
1896 
1897     if (traceNameIs("solar_smash"))
1898     {
1899         if (isIntelWinANGLE)
1900         {
1901             skipTest("https://issuetracker.google.com/378900717 Nondeterministic on Windows Intel");
1902         }
1903     }
1904 
1905     if (IsGalaxyS22())
1906     {
1907         if (traceNameIs("cod_mobile") || traceNameIs("dota_underlords") ||
1908             traceNameIs("marvel_snap") || traceNameIs("nier_reincarnation") ||
1909             traceNameIs("pokemon_unite") || traceNameIs("slingshot_test1") ||
1910             traceNameIs("slingshot_test2") || traceNameIs("supertuxkart") ||
1911             traceNameIs("the_witcher_monster_slayer") || traceNameIs("warcraft_rumble") ||
1912             traceNameIs("critical_ops"))
1913         {
1914             skipTest("https://issuetracker.google.com/267953710 Trace needs triage on Galaxy S22");
1915         }
1916     }
1917 
1918     // glDebugMessageControlKHR and glDebugMessageCallbackKHR crash on ARM GLES1.
1919     if (IsARM() && mParams->traceInfo.contextClientMajorVersion == 1)
1920     {
1921         mEnableDebugCallback = false;
1922     }
1923 
1924     // We already swap in TracePerfTest::drawBenchmark, no need to swap again in the harness.
1925     disableTestHarnessSwap();
1926 
1927     gCurrentTracePerfTest = this;
1928 
1929     if (gTraceTestValidation)
1930     {
1931         mStepsToRun = frameCount();
1932     }
1933 
1934     if (gRunToKeyFrame)
1935     {
1936         if (mParams->traceInfo.keyFrames.empty())
1937         {
1938             // If we don't have a keyFrame, run one step
1939             INFO() << "No keyframe available for trace, running to frame 1";
1940             mStepsToRun = 1;
1941         }
1942         else
1943         {
1944             int keyFrame = mParams->traceInfo.keyFrames[0];
1945             INFO() << "Running to keyframe: " << keyFrame;
1946             mStepsToRun = keyFrame;
1947         }
1948     }
1949 }
1950 
startTest()1951 void TracePerfTest::startTest()
1952 {
1953     // runTrial() must align to frameCount()
1954     ASSERT(mCurrentFrame == mStartFrame);
1955 
1956     ANGLERenderTest::startTest();
1957 }
1958 
FindTraceGzPath(const std::string & traceName)1959 std::string FindTraceGzPath(const std::string &traceName)
1960 {
1961     std::stringstream pathStream;
1962 
1963     char genDir[kMaxPath] = {};
1964     if (!angle::FindTestDataPath("gen", genDir, kMaxPath))
1965     {
1966         return "";
1967     }
1968     pathStream << genDir << angle::GetPathSeparator() << "tracegz_" << traceName << ".gz";
1969 
1970     return pathStream.str();
1971 }
1972 
initializeBenchmark()1973 void TracePerfTest::initializeBenchmark()
1974 {
1975     const TraceInfo &traceInfo = mParams->traceInfo;
1976 
1977     char testDataDir[kMaxPath] = {};
1978     if (!FindTraceTestDataPath(traceInfo.name, testDataDir, kMaxPath))
1979     {
1980         failTest("Could not find test data folder.");
1981         return;
1982     }
1983 
1984     std::string baseDir = "";
1985 #if defined(ANGLE_TRACE_EXTERNAL_BINARIES)
1986     baseDir += AndroidWindow::GetApplicationDirectory() + "/angle_traces/";
1987 #endif
1988 
1989     if (gTraceInterpreter)
1990     {
1991         mTraceReplay.reset(new TraceLibrary("angle_trace_interpreter", traceInfo, baseDir));
1992         if (strcmp(gTraceInterpreter, "gz") == 0)
1993         {
1994             std::string traceGzPath = FindTraceGzPath(traceInfo.name);
1995             if (traceGzPath.empty())
1996             {
1997                 failTest("Could not find trace gz.");
1998                 return;
1999             }
2000             mTraceReplay->setTraceGzPath(traceGzPath);
2001         }
2002     }
2003     else
2004     {
2005         std::stringstream traceNameStr;
2006         traceNameStr << "angle_restricted_traces_" << traceInfo.name;
2007         std::string traceName = traceNameStr.str();
2008         mTraceReplay.reset(new TraceLibrary(traceNameStr.str(), traceInfo, baseDir));
2009     }
2010 
2011     LoadTraceEGL(TraceLoadProc);
2012     LoadTraceGLES(TraceLoadProc);
2013 
2014     if (!mTraceReplay->valid())
2015     {
2016         failTest("Could not load trace.");
2017         return;
2018     }
2019 
2020     mStartFrame = traceInfo.frameStart;
2021     mEndFrame   = traceInfo.frameEnd;
2022     mTraceReplay->setValidateSerializedStateCallback(ValidateSerializedState);
2023     mTraceReplay->setBinaryDataDir(testDataDir);
2024     mTraceReplay->setReplayResourceMode(gIncludeInactiveResources);
2025     if (gScreenshotDir)
2026     {
2027         mTraceReplay->setDebugOutputDir(gScreenshotDir);
2028     }
2029 
2030     if (gMinimizeGPUWork)
2031     {
2032         // Shrink the offscreen window to 1x1.
2033         mWindowWidth  = 1;
2034         mWindowHeight = 1;
2035     }
2036     else
2037     {
2038         mWindowWidth  = mTestParams.windowWidth;
2039         mWindowHeight = mTestParams.windowHeight;
2040     }
2041     mCurrentFrame     = mStartFrame;
2042     mCurrentIteration = mStartFrame;
2043     mCurrentOffscreenGridIteration = 0;
2044 
2045     if (IsAndroid())
2046     {
2047         // On Android, set the orientation used by the app, based on width/height
2048         getWindow()->setOrientation(mTestParams.windowWidth, mTestParams.windowHeight);
2049     }
2050 
2051     // If we're rendering offscreen we set up a default back buffer.
2052     if (mParams->surfaceType == SurfaceType::Offscreen)
2053     {
2054         bool gles1 = mParams->traceInfo.contextClientMajorVersion == 1;
2055         if (gles1 &&
2056             !CheckExtensionExists(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
2057                                   "GL_OES_framebuffer_object"))
2058         {
2059             failTest("GLES1 --offscreen requires GL_OES_framebuffer_object");
2060             return;
2061         }
2062 
2063         auto genRenderbuffers     = gles1 ? glGenRenderbuffersOES : glGenRenderbuffers;
2064         auto bindRenderbuffer     = gles1 ? glBindRenderbufferOES : glBindRenderbuffer;
2065         auto renderbufferStorage  = gles1 ? glRenderbufferStorageOES : glRenderbufferStorage;
2066         auto genFramebuffers      = gles1 ? glGenFramebuffersOES : glGenFramebuffers;
2067         auto bindFramebuffer      = gles1 ? glBindFramebufferOES : glBindFramebuffer;
2068         auto framebufferTexture2D = gles1 ? glFramebufferTexture2DOES : glFramebufferTexture2D;
2069         auto framebufferRenderbuffer =
2070             gles1 ? glFramebufferRenderbufferOES : glFramebufferRenderbuffer;
2071 
2072         genRenderbuffers(1, &mOffscreenDepthStencil);
2073         bindRenderbuffer(GL_RENDERBUFFER, mOffscreenDepthStencil);
2074         renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mWindowWidth, mWindowHeight);
2075         bindRenderbuffer(GL_RENDERBUFFER, 0);
2076 
2077         genFramebuffers(mMaxOffscreenBufferCount, mOffscreenFramebuffers.data());
2078         glGenTextures(mMaxOffscreenBufferCount, mOffscreenTextures.data());
2079         for (int i = 0; i < mMaxOffscreenBufferCount; i++)
2080         {
2081             bindFramebuffer(GL_FRAMEBUFFER, mOffscreenFramebuffers[i]);
2082 
2083             // Hard-code RGBA8/D24S8. This should be specified in the trace info.
2084             glBindTexture(GL_TEXTURE_2D, mOffscreenTextures[i]);
2085             glTexImage2D(GL_TEXTURE_2D, 0,
2086                          mParams->colorSpace == EGL_GL_COLORSPACE_SRGB ? GL_SRGB8_ALPHA8 : GL_RGBA,
2087                          mWindowWidth, mWindowHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
2088 
2089             framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
2090                                  mOffscreenTextures[i], 0);
2091             framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
2092                                     mOffscreenDepthStencil);
2093             framebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
2094                                     mOffscreenDepthStencil);
2095             glBindTexture(GL_TEXTURE_2D, 0);
2096         }
2097     }
2098 
2099     // Potentially slow. Can load a lot of resources.
2100     mTraceReplay->setupReplay();
2101 
2102     glFinish();
2103 
2104     ASSERT_GE(mEndFrame, mStartFrame);
2105 
2106     getWindow()->ignoreSizeEvents();
2107     getWindow()->setVisible(true);
2108 
2109     // If we're re-tracing, trigger capture start after setup. This ensures the Setup function gets
2110     // recaptured into another Setup function and not merged with the first frame.
2111     if (gRetraceMode)
2112     {
2113         getGLWindow()->swap();
2114     }
2115 }
2116 
2117 #undef TRACE_TEST_CASE
2118 
destroyBenchmark()2119 void TracePerfTest::destroyBenchmark()
2120 {
2121     if (mParams->surfaceType == SurfaceType::Offscreen)
2122     {
2123         glDeleteTextures(mMaxOffscreenBufferCount, mOffscreenTextures.data());
2124         mOffscreenTextures.fill(0);
2125 
2126         bool gles1               = mParams->traceInfo.contextClientMajorVersion == 1;
2127         auto deleteRenderbuffers = gles1 ? glDeleteRenderbuffersOES : glDeleteRenderbuffers;
2128         auto deleteFramebuffers  = gles1 ? glDeleteFramebuffersOES : glDeleteFramebuffers;
2129 
2130         deleteRenderbuffers(1, &mOffscreenDepthStencil);
2131         mOffscreenDepthStencil = 0;
2132 
2133         deleteFramebuffers(mMaxOffscreenBufferCount, mOffscreenFramebuffers.data());
2134         mOffscreenFramebuffers.fill(0);
2135     }
2136 
2137     mTraceReplay->finishReplay();
2138     mTraceReplay.reset(nullptr);
2139 }
2140 
sampleTime()2141 void TracePerfTest::sampleTime()
2142 {
2143     if (mUseTimestampQueries)
2144     {
2145         GLint64 glTime;
2146         // glGetInteger64vEXT is exported by newer versions of the timer query extensions.
2147         // Unfortunately only the core EP is exposed by some desktop drivers (e.g. NVIDIA).
2148         if (glGetInteger64vEXT)
2149         {
2150             glGetInteger64vEXT(GL_TIMESTAMP_EXT, &glTime);
2151         }
2152         else
2153         {
2154             glGetInteger64v(GL_TIMESTAMP_EXT, &glTime);
2155         }
2156         mTimeline.push_back({glTime, angle::GetHostTimeSeconds()});
2157     }
2158 }
2159 
drawBenchmark()2160 void TracePerfTest::drawBenchmark()
2161 {
2162     constexpr uint32_t kFramesPerX  = 6;
2163     constexpr uint32_t kFramesPerY  = 4;
2164     constexpr uint32_t kFramesPerSwap = kFramesPerY * kFramesPerX;
2165 
2166     const uint32_t kOffscreenOffsetX = 0;
2167     const uint32_t kOffscreenOffsetY = 0;
2168     const uint32_t kOffscreenWidth   = mTestParams.windowWidth;
2169     const uint32_t kOffscreenHeight  = mTestParams.windowHeight;
2170 
2171     const uint32_t kOffscreenFrameWidth = static_cast<uint32_t>(
2172         static_cast<double>(kOffscreenWidth / static_cast<double>(kFramesPerX)));
2173     const uint32_t kOffscreenFrameHeight = static_cast<uint32_t>(
2174         static_cast<double>(kOffscreenHeight / static_cast<double>(kFramesPerY)));
2175 
2176     // Add a time sample from GL and the host.
2177     if (mCurrentFrame == mStartFrame)
2178     {
2179         sampleTime();
2180     }
2181 
2182     bool gles1           = mParams->traceInfo.contextClientMajorVersion == 1;
2183     auto bindFramebuffer = gles1 ? glBindFramebufferOES : glBindFramebuffer;
2184     int offscreenBufferIndex = mTotalFrameCount % mMaxOffscreenBufferCount;
2185 
2186     if (mParams->surfaceType == SurfaceType::Offscreen)
2187     {
2188         // Some driver (ARM and ANGLE) try to nop or defer the glFlush if it is called within the
2189         // renderpass to avoid breaking renderpass (performance reason). For app traces that does
2190         // not use any FBO, when we run in the offscreen mode, there is no frame boundary and
2191         // glFlush call we issued at end of frame will get skipped. To overcome this (and also
2192         // matches what onscreen double buffering behavior as well), we use two offscreen FBOs and
2193         // ping pong between them for each frame.
2194         GLuint buffer = mOffscreenFramebuffers[offscreenBufferIndex];
2195 
2196         if (gles1 && mOffscreenFrameCount == kFramesPerSwap - 1)
2197         {
2198             buffer = 0;  // gles1: a single frame is rendered to buffer 0
2199         }
2200         bindFramebuffer(GL_FRAMEBUFFER, buffer);
2201 
2202         GLsync sync = mOffscreenSyncs[offscreenBufferIndex];
2203         if (sync)
2204         {
2205             constexpr GLuint64 kTimeout = 2'000'000'000;  // 2 seconds
2206             GLenum result = glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, kTimeout);
2207             if (result != GL_CONDITION_SATISFIED && result != GL_ALREADY_SIGNALED)
2208             {
2209                 failTest(std::string("glClientWaitSync unexpected result: ") +
2210                          std::to_string(result));
2211             }
2212             glDeleteSync(sync);
2213         }
2214     }
2215 
2216     char frameName[32];
2217     snprintf(frameName, sizeof(frameName), "Frame %u", mCurrentFrame);
2218     beginInternalTraceEvent(frameName);
2219 
2220     startGpuTimer();
2221     atraceCounter("TraceFrameIndex", mCurrentFrame);
2222     mTraceReplay->replayFrame(mCurrentFrame);
2223     stopGpuTimer();
2224 
2225     updatePerfCounters();
2226 
2227     if (mParams->surfaceType == SurfaceType::Offscreen)
2228     {
2229         if (gMinimizeGPUWork)
2230         {
2231             // To keep GPU work minimum, we skip the blit.
2232             glFlush();
2233             mOffscreenFrameCount++;
2234         }
2235         else
2236         {
2237             GLuint offscreenBuffer = mOffscreenFramebuffers[offscreenBufferIndex];
2238             GLint currentDrawFBO, currentReadFBO;
2239             if (gles1)
2240             {
2241                 // OES_framebuffer_object doesn't define a separate "read" binding
2242                 glGetIntegerv(GL_FRAMEBUFFER_BINDING_OES, &currentDrawFBO);
2243                 bindFramebuffer(GL_FRAMEBUFFER, offscreenBuffer);
2244             }
2245             else
2246             {
2247                 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &currentDrawFBO);
2248                 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &currentReadFBO);
2249 
2250                 bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
2251                 bindFramebuffer(GL_READ_FRAMEBUFFER, offscreenBuffer);
2252             }
2253 
2254             uint32_t frameX  = (mOffscreenFrameCount % kFramesPerSwap) % kFramesPerX;
2255             uint32_t frameY  = (mOffscreenFrameCount % kFramesPerSwap) / kFramesPerX;
2256             uint32_t windowX = kOffscreenOffsetX + frameX * kOffscreenFrameWidth;
2257             uint32_t windowY = kOffscreenOffsetY + frameY * kOffscreenFrameHeight;
2258 
2259             GLboolean scissorTest = GL_FALSE;
2260             glGetBooleanv(GL_SCISSOR_TEST, &scissorTest);
2261 
2262             if (scissorTest)
2263             {
2264                 glDisable(GL_SCISSOR_TEST);
2265             }
2266 
2267             if (!gles1)  // gles1: no glBlitFramebuffer, a single frame is rendered to buffer 0
2268             {
2269                 mOffscreenSyncs[offscreenBufferIndex] =
2270                     glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
2271 
2272                 glBlitFramebuffer(0, 0, mWindowWidth, mWindowHeight, windowX, windowY,
2273                                   windowX + kOffscreenFrameWidth, windowY + kOffscreenFrameHeight,
2274                                   GL_COLOR_BUFFER_BIT, GL_NEAREST);
2275             }
2276 
2277             // GL_READ_FRAMEBUFFER is already set correctly for glReadPixels
2278             saveScreenshotIfEnabled(ScreenshotType::kFrame);
2279 
2280             if (frameX == kFramesPerX - 1 && frameY == kFramesPerY - 1)
2281             {
2282                 bindFramebuffer(GL_FRAMEBUFFER, 0);
2283                 if (!gles1)  // gles1: no grid, a single frame is rendered to buffer 0
2284                 {
2285                     mCurrentOffscreenGridIteration++;
2286                     saveScreenshotIfEnabled(ScreenshotType::kGrid);
2287                 }
2288                 getGLWindow()->swap();
2289                 glClear(GL_COLOR_BUFFER_BIT);
2290                 mOffscreenFrameCount = 0;
2291             }
2292             else
2293             {
2294                 glFlush();
2295                 mOffscreenFrameCount++;
2296             }
2297 
2298             if (scissorTest)
2299             {
2300                 glEnable(GL_SCISSOR_TEST);
2301             }
2302 
2303             if (gles1)
2304             {
2305                 bindFramebuffer(GL_FRAMEBUFFER, currentDrawFBO);
2306             }
2307             else
2308             {
2309                 bindFramebuffer(GL_DRAW_FRAMEBUFFER, currentDrawFBO);
2310                 bindFramebuffer(GL_READ_FRAMEBUFFER, currentReadFBO);
2311             }
2312         }
2313     }
2314     else
2315     {
2316         bindFramebuffer(GL_FRAMEBUFFER, 0);
2317         saveScreenshotIfEnabled(ScreenshotType::kFrame);
2318         getGLWindow()->swap();
2319     }
2320 
2321     endInternalTraceEvent(frameName);
2322 
2323     if (gFpsLimit)
2324     {
2325         // Interval and time delta over kFpsNumFrames frames to get closer to requested fps
2326         // (this allows a bit more jitter in individual frames due to the averaging effect)
2327         double requestedNthFrameInterval = static_cast<double>(kFpsNumFrames) / gFpsLimit;
2328         double nthFrameTimeDelta =
2329             angle::GetCurrentSystemTime() - mFpsStartTimes[mTotalFrameCount % kFpsNumFrames];
2330         if (nthFrameTimeDelta < requestedNthFrameInterval)
2331         {
2332             std::this_thread::sleep_for(
2333                 std::chrono::duration<double>(requestedNthFrameInterval - nthFrameTimeDelta));
2334         }
2335         mFpsStartTimes[mTotalFrameCount % kFpsNumFrames] = angle::GetCurrentSystemTime();
2336     }
2337 
2338     mTotalFrameCount++;
2339 
2340     if (mCurrentFrame == mEndFrame)
2341     {
2342         mTraceReplay->resetReplay();
2343         mCurrentFrame = mStartFrame;
2344     }
2345     else
2346     {
2347         mCurrentFrame++;
2348     }
2349 
2350     // Always iterated for saving screenshots after reset
2351     mCurrentIteration++;
2352 
2353     // Process any running queries once per iteration.
2354     for (size_t queryIndex = 0; queryIndex < mRunningQueries.size();)
2355     {
2356         const QueryInfo &query = mRunningQueries[queryIndex];
2357 
2358         GLuint endResultAvailable = 0;
2359         glGetQueryObjectuivEXT(query.endTimestampQuery, GL_QUERY_RESULT_AVAILABLE,
2360                                &endResultAvailable);
2361 
2362         if (endResultAvailable == GL_TRUE)
2363         {
2364             char fboName[32];
2365             snprintf(fboName, sizeof(fboName), "FBO %u", query.framebuffer);
2366 
2367             GLint64 beginTimestamp = 0;
2368             glGetQueryObjecti64vEXT(query.beginTimestampQuery, GL_QUERY_RESULT, &beginTimestamp);
2369             glDeleteQueriesEXT(1, &query.beginTimestampQuery);
2370             double beginHostTime = getHostTimeFromGLTime(beginTimestamp);
2371             beginGLTraceEvent(fboName, beginHostTime);
2372 
2373             GLint64 endTimestamp = 0;
2374             glGetQueryObjecti64vEXT(query.endTimestampQuery, GL_QUERY_RESULT, &endTimestamp);
2375             glDeleteQueriesEXT(1, &query.endTimestampQuery);
2376             double endHostTime = getHostTimeFromGLTime(endTimestamp);
2377             endGLTraceEvent(fboName, endHostTime);
2378 
2379             mRunningQueries.erase(mRunningQueries.begin() + queryIndex);
2380         }
2381         else
2382         {
2383             queryIndex++;
2384         }
2385     }
2386 }
2387 
2388 // Converts a GL timestamp into a host-side CPU time aligned with "GetHostTimeSeconds".
2389 // This check is necessary to line up sampled trace events in a consistent timeline.
2390 // Uses a linear interpolation from a series of samples. We do a blocking call to sample
2391 // both host and GL time once per swap. We then find the two closest GL timestamps and
2392 // interpolate the host times between them to compute our result. If we are past the last
2393 // GL timestamp we sample a new data point pair.
getHostTimeFromGLTime(GLint64 glTime)2394 double TracePerfTest::getHostTimeFromGLTime(GLint64 glTime)
2395 {
2396     // Find two samples to do a lerp.
2397     size_t firstSampleIndex = mTimeline.size() - 1;
2398     while (firstSampleIndex > 0)
2399     {
2400         if (mTimeline[firstSampleIndex].glTime < glTime)
2401         {
2402             break;
2403         }
2404         firstSampleIndex--;
2405     }
2406 
2407     // Add an extra sample if we're missing an ending sample.
2408     if (firstSampleIndex == mTimeline.size() - 1)
2409     {
2410         sampleTime();
2411     }
2412 
2413     const TimeSample &start = mTimeline[firstSampleIndex];
2414     const TimeSample &end   = mTimeline[firstSampleIndex + 1];
2415 
2416     // Note: we have observed in some odd cases later timestamps producing values that are
2417     // smaller than preceding timestamps. This bears further investigation.
2418 
2419     // Compute the scaling factor for the lerp.
2420     double glDelta = static_cast<double>(glTime - start.glTime);
2421     double glRange = static_cast<double>(end.glTime - start.glTime);
2422     double t       = glDelta / glRange;
2423 
2424     // Lerp(t1, t2, t)
2425     double hostRange = end.hostTime - start.hostTime;
2426     return mTimeline[firstSampleIndex].hostTime + hostRange * t;
2427 }
2428 
onEglCreateContext(EGLDisplay display,EGLConfig config,EGLContext share_context,EGLint const * attrib_list)2429 EGLContext TracePerfTest::onEglCreateContext(EGLDisplay display,
2430                                              EGLConfig config,
2431                                              EGLContext share_context,
2432                                              EGLint const *attrib_list)
2433 {
2434     GLWindowContext newContext =
2435         getGLWindow()->createContextGeneric(reinterpret_cast<GLWindowContext>(share_context));
2436     return reinterpret_cast<EGLContext>(newContext);
2437 }
2438 
onEglMakeCurrent(EGLDisplay display,EGLSurface draw,EGLSurface read,EGLContext context)2439 void TracePerfTest::onEglMakeCurrent(EGLDisplay display,
2440                                      EGLSurface draw,
2441                                      EGLSurface read,
2442                                      EGLContext context)
2443 {
2444     getGLWindow()->makeCurrentGeneric(reinterpret_cast<GLWindowContext>(context));
2445 }
2446 
onEglGetCurrentContext()2447 EGLContext TracePerfTest::onEglGetCurrentContext()
2448 {
2449     return getGLWindow()->getCurrentContextGeneric();
2450 }
2451 
onEglCreateImage(EGLDisplay display,EGLContext context,EGLenum target,EGLClientBuffer buffer,const EGLAttrib * attrib_list)2452 EGLImage TracePerfTest::onEglCreateImage(EGLDisplay display,
2453                                          EGLContext context,
2454                                          EGLenum target,
2455                                          EGLClientBuffer buffer,
2456                                          const EGLAttrib *attrib_list)
2457 {
2458     GLWindowBase::Image image = getGLWindow()->createImage(
2459         reinterpret_cast<GLWindowContext>(context), target, buffer, attrib_list);
2460     return reinterpret_cast<EGLImage>(image);
2461 }
2462 
onEglCreateImageKHR(EGLDisplay display,EGLContext context,EGLenum target,EGLClientBuffer buffer,const EGLint * attrib_list)2463 EGLImageKHR TracePerfTest::onEglCreateImageKHR(EGLDisplay display,
2464                                                EGLContext context,
2465                                                EGLenum target,
2466                                                EGLClientBuffer buffer,
2467                                                const EGLint *attrib_list)
2468 {
2469     GLWindowBase::Image image = getGLWindow()->createImageKHR(
2470         reinterpret_cast<GLWindowContext>(context), target, buffer, attrib_list);
2471     return reinterpret_cast<EGLImage>(image);
2472 }
2473 
onEglDestroyImage(EGLDisplay display,EGLImage image)2474 EGLBoolean TracePerfTest::onEglDestroyImage(EGLDisplay display, EGLImage image)
2475 {
2476     return getGLWindow()->destroyImage(image);
2477 }
2478 
onEglDestroyImageKHR(EGLDisplay display,EGLImage image)2479 EGLBoolean TracePerfTest::onEglDestroyImageKHR(EGLDisplay display, EGLImage image)
2480 {
2481     return getGLWindow()->destroyImageKHR(image);
2482 }
2483 
onEglCreateSync(EGLDisplay dpy,EGLenum type,const EGLAttrib * attrib_list)2484 EGLSync TracePerfTest::onEglCreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list)
2485 {
2486     return getGLWindow()->createSync(dpy, type, attrib_list);
2487 }
2488 
onEglCreateSyncKHR(EGLDisplay dpy,EGLenum type,const EGLint * attrib_list)2489 EGLSync TracePerfTest::onEglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list)
2490 {
2491     return getGLWindow()->createSyncKHR(dpy, type, attrib_list);
2492 }
2493 
onEglDestroySync(EGLDisplay dpy,EGLSync sync)2494 EGLBoolean TracePerfTest::onEglDestroySync(EGLDisplay dpy, EGLSync sync)
2495 {
2496     return getGLWindow()->destroySync(dpy, sync);
2497 }
2498 
onEglDestroySyncKHR(EGLDisplay dpy,EGLSync sync)2499 EGLBoolean TracePerfTest::onEglDestroySyncKHR(EGLDisplay dpy, EGLSync sync)
2500 {
2501     return getGLWindow()->destroySyncKHR(dpy, sync);
2502 }
2503 
onEglClientWaitSync(EGLDisplay dpy,EGLSync sync,EGLint flags,EGLTimeKHR timeout)2504 EGLint TracePerfTest::onEglClientWaitSync(EGLDisplay dpy,
2505                                           EGLSync sync,
2506                                           EGLint flags,
2507                                           EGLTimeKHR timeout)
2508 {
2509     return getGLWindow()->clientWaitSync(dpy, sync, flags, timeout);
2510 }
2511 
onEglClientWaitSyncKHR(EGLDisplay dpy,EGLSync sync,EGLint flags,EGLTimeKHR timeout)2512 EGLint TracePerfTest::onEglClientWaitSyncKHR(EGLDisplay dpy,
2513                                              EGLSync sync,
2514                                              EGLint flags,
2515                                              EGLTimeKHR timeout)
2516 {
2517     return getGLWindow()->clientWaitSyncKHR(dpy, sync, flags, timeout);
2518 }
2519 
onEglGetError()2520 EGLint TracePerfTest::onEglGetError()
2521 {
2522     return getGLWindow()->getEGLError();
2523 }
2524 
onEglGetCurrentDisplay()2525 EGLDisplay TracePerfTest::onEglGetCurrentDisplay()
2526 {
2527     return getGLWindow()->getCurrentDisplay();
2528 }
2529 
2530 // Triggered when the replay calls glBindFramebuffer.
onReplayFramebufferChange(GLenum target,GLuint framebuffer)2531 void TracePerfTest::onReplayFramebufferChange(GLenum target, GLuint framebuffer)
2532 {
2533     bool gles1           = mParams->traceInfo.contextClientMajorVersion == 1;
2534     auto bindFramebuffer = gles1 ? glBindFramebufferOES : glBindFramebuffer;
2535 
2536     if (framebuffer == 0 && mParams->surfaceType == SurfaceType::Offscreen)
2537     {
2538         bindFramebuffer(target,
2539                         mOffscreenFramebuffers[mTotalFrameCount % mMaxOffscreenBufferCount]);
2540     }
2541     else
2542     {
2543         bindFramebuffer(target, framebuffer);
2544     }
2545 
2546     switch (target)
2547     {
2548         case GL_FRAMEBUFFER:
2549             mDrawFramebufferBinding = framebuffer;
2550             mReadFramebufferBinding = framebuffer;
2551             break;
2552         case GL_DRAW_FRAMEBUFFER:
2553             mDrawFramebufferBinding = framebuffer;
2554             break;
2555         case GL_READ_FRAMEBUFFER:
2556             mReadFramebufferBinding = framebuffer;
2557             return;
2558 
2559         default:
2560             UNREACHABLE();
2561             break;
2562     }
2563 
2564     if (!mUseTimestampQueries)
2565         return;
2566 
2567     // We have at most one active timestamp query at a time. This code will end the current
2568     // query and immediately start a new one.
2569     if (mCurrentQuery.beginTimestampQuery != 0)
2570     {
2571         glGenQueriesEXT(1, &mCurrentQuery.endTimestampQuery);
2572         glQueryCounterEXT(mCurrentQuery.endTimestampQuery, GL_TIMESTAMP_EXT);
2573         mRunningQueries.push_back(mCurrentQuery);
2574         mCurrentQuery = {};
2575     }
2576 
2577     ASSERT(mCurrentQuery.beginTimestampQuery == 0);
2578 
2579     glGenQueriesEXT(1, &mCurrentQuery.beginTimestampQuery);
2580     glQueryCounterEXT(mCurrentQuery.beginTimestampQuery, GL_TIMESTAMP_EXT);
2581     mCurrentQuery.framebuffer = framebuffer;
2582 }
2583 
GetDiffPath()2584 std::string GetDiffPath()
2585 {
2586 #if defined(ANGLE_PLATFORM_WINDOWS)
2587     std::array<char, MAX_PATH> filenameBuffer = {};
2588     char *filenamePtr                         = nullptr;
2589     if (SearchPathA(NULL, "diff", ".exe", MAX_PATH, filenameBuffer.data(), &filenamePtr) == 0)
2590     {
2591         return "";
2592     }
2593     return std::string(filenameBuffer.data());
2594 #else
2595     return "/usr/bin/diff";
2596 #endif  // defined(ANGLE_PLATFORM_WINDOWS)
2597 }
2598 
PrintFileDiff(const char * aFilePath,const char * bFilePath)2599 void PrintFileDiff(const char *aFilePath, const char *bFilePath)
2600 {
2601     std::string pathToDiff = GetDiffPath();
2602     if (pathToDiff.empty())
2603     {
2604         printf("Could not find diff in the path.\n");
2605         return;
2606     }
2607 
2608     std::vector<const char *> args;
2609     args.push_back(pathToDiff.c_str());
2610     args.push_back(aFilePath);
2611     args.push_back(bFilePath);
2612     args.push_back("-u3");
2613 
2614     printf("Calling");
2615     for (const char *arg : args)
2616     {
2617         printf(" %s", arg);
2618     }
2619     printf("\n");
2620 
2621     ProcessHandle proc(LaunchProcess(args, ProcessOutputCapture::StdoutOnly));
2622     if (proc && proc->finish())
2623     {
2624         printf("\n%s\n", proc->getStdout().c_str());
2625     }
2626 }
2627 
validateSerializedState(const char * expectedCapturedSerializedState,const char * fileName,uint32_t line)2628 void TracePerfTest::validateSerializedState(const char *expectedCapturedSerializedState,
2629                                             const char *fileName,
2630                                             uint32_t line)
2631 {
2632     if (!gTraceTestValidation)
2633     {
2634         return;
2635     }
2636 
2637     printf("Serialization checkpoint %s:%u...\n", fileName, line);
2638 
2639     const GLubyte *bytes                      = glGetString(GL_SERIALIZED_CONTEXT_STRING_ANGLE);
2640     const char *actualReplayedSerializedState = reinterpret_cast<const char *>(bytes);
2641     if (strcmp(expectedCapturedSerializedState, actualReplayedSerializedState) == 0)
2642     {
2643         printf("Serialization match.\n");
2644         return;
2645     }
2646 
2647     GTEST_NONFATAL_FAILURE_("Serialization mismatch!");
2648 
2649     const Optional<std::string> aFilePath = CreateTemporaryFile();
2650     const char *aFilePathCStr             = aFilePath.value().c_str();
2651     if (aFilePath.valid())
2652     {
2653         printf("Saving \"expected\" capture serialization to \"%s\".\n", aFilePathCStr);
2654         FILE *fpA = fopen(aFilePathCStr, "wt");
2655         ASSERT(fpA);
2656         fprintf(fpA, "%s", expectedCapturedSerializedState);
2657         fclose(fpA);
2658     }
2659 
2660     const Optional<std::string> bFilePath = CreateTemporaryFile();
2661     const char *bFilePathCStr             = bFilePath.value().c_str();
2662     if (bFilePath.valid())
2663     {
2664         printf("Saving \"actual\" replay serialization to \"%s\".\n", bFilePathCStr);
2665         FILE *fpB = fopen(bFilePathCStr, "wt");
2666         ASSERT(fpB);
2667         fprintf(fpB, "%s", actualReplayedSerializedState);
2668         fclose(fpB);
2669     }
2670 
2671     PrintFileDiff(aFilePathCStr, bFilePathCStr);
2672 }
2673 
isDefaultFramebuffer(GLenum target) const2674 bool TracePerfTest::isDefaultFramebuffer(GLenum target) const
2675 {
2676     switch (target)
2677     {
2678         case GL_FRAMEBUFFER:
2679         case GL_DRAW_FRAMEBUFFER:
2680             return (mDrawFramebufferBinding == 0);
2681 
2682         case GL_READ_FRAMEBUFFER:
2683             return (mReadFramebufferBinding == 0);
2684 
2685         default:
2686             UNREACHABLE();
2687             return false;
2688     }
2689 }
2690 
ConvertDefaultFramebufferEnum(GLenum value)2691 GLenum ConvertDefaultFramebufferEnum(GLenum value)
2692 {
2693     switch (value)
2694     {
2695         case GL_NONE:
2696             return GL_NONE;
2697         case GL_BACK:
2698         case GL_COLOR:
2699             return GL_COLOR_ATTACHMENT0;
2700         case GL_DEPTH:
2701             return GL_DEPTH_ATTACHMENT;
2702         case GL_STENCIL:
2703             return GL_STENCIL_ATTACHMENT;
2704         case GL_DEPTH_STENCIL:
2705             return GL_DEPTH_STENCIL_ATTACHMENT;
2706         default:
2707             UNREACHABLE();
2708             return GL_NONE;
2709     }
2710 }
2711 
ConvertDefaultFramebufferEnums(GLsizei numAttachments,const GLenum * attachments)2712 std::vector<GLenum> ConvertDefaultFramebufferEnums(GLsizei numAttachments,
2713                                                    const GLenum *attachments)
2714 {
2715     std::vector<GLenum> translatedAttachments;
2716     for (GLsizei attachmentIndex = 0; attachmentIndex < numAttachments; ++attachmentIndex)
2717     {
2718         GLenum converted = ConvertDefaultFramebufferEnum(attachments[attachmentIndex]);
2719         translatedAttachments.push_back(converted);
2720     }
2721     return translatedAttachments;
2722 }
2723 
2724 // Needs special handling to treat the 0 framebuffer in offscreen mode.
onReplayInvalidateFramebuffer(GLenum target,GLsizei numAttachments,const GLenum * attachments)2725 void TracePerfTest::onReplayInvalidateFramebuffer(GLenum target,
2726                                                   GLsizei numAttachments,
2727                                                   const GLenum *attachments)
2728 {
2729     if (mParams->surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(target))
2730     {
2731         glInvalidateFramebuffer(target, numAttachments, attachments);
2732     }
2733     else
2734     {
2735         std::vector<GLenum> translatedAttachments =
2736             ConvertDefaultFramebufferEnums(numAttachments, attachments);
2737         glInvalidateFramebuffer(target, numAttachments, translatedAttachments.data());
2738     }
2739 }
2740 
onReplayInvalidateSubFramebuffer(GLenum target,GLsizei numAttachments,const GLenum * attachments,GLint x,GLint y,GLsizei width,GLsizei height)2741 void TracePerfTest::onReplayInvalidateSubFramebuffer(GLenum target,
2742                                                      GLsizei numAttachments,
2743                                                      const GLenum *attachments,
2744                                                      GLint x,
2745                                                      GLint y,
2746                                                      GLsizei width,
2747                                                      GLsizei height)
2748 {
2749     if (mParams->surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(target))
2750     {
2751         glInvalidateSubFramebuffer(target, numAttachments, attachments, x, y, width, height);
2752     }
2753     else
2754     {
2755         std::vector<GLenum> translatedAttachments =
2756             ConvertDefaultFramebufferEnums(numAttachments, attachments);
2757         glInvalidateSubFramebuffer(target, numAttachments, translatedAttachments.data(), x, y,
2758                                    width, height);
2759     }
2760 }
2761 
onReplayDrawBuffers(GLsizei n,const GLenum * bufs)2762 void TracePerfTest::onReplayDrawBuffers(GLsizei n, const GLenum *bufs)
2763 {
2764     if (mParams->surfaceType != SurfaceType::Offscreen ||
2765         !isDefaultFramebuffer(GL_DRAW_FRAMEBUFFER))
2766     {
2767         glDrawBuffers(n, bufs);
2768     }
2769     else
2770     {
2771         std::vector<GLenum> translatedBufs = ConvertDefaultFramebufferEnums(n, bufs);
2772         glDrawBuffers(n, translatedBufs.data());
2773     }
2774 }
2775 
onReplayReadBuffer(GLenum src)2776 void TracePerfTest::onReplayReadBuffer(GLenum src)
2777 {
2778     if (mParams->surfaceType != SurfaceType::Offscreen ||
2779         !isDefaultFramebuffer(GL_READ_FRAMEBUFFER))
2780     {
2781         glReadBuffer(src);
2782     }
2783     else
2784     {
2785         GLenum translated = ConvertDefaultFramebufferEnum(src);
2786         glReadBuffer(translated);
2787     }
2788 }
2789 
onReplayDiscardFramebufferEXT(GLenum target,GLsizei numAttachments,const GLenum * attachments)2790 void TracePerfTest::onReplayDiscardFramebufferEXT(GLenum target,
2791                                                   GLsizei numAttachments,
2792                                                   const GLenum *attachments)
2793 {
2794     if (mParams->surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(target))
2795     {
2796         glDiscardFramebufferEXT(target, numAttachments, attachments);
2797     }
2798     else
2799     {
2800         std::vector<GLenum> translatedAttachments =
2801             ConvertDefaultFramebufferEnums(numAttachments, attachments);
2802         glDiscardFramebufferEXT(target, numAttachments, translatedAttachments.data());
2803     }
2804 }
2805 
saveScreenshotIfEnabled(ScreenshotType screenshotType)2806 void TracePerfTest::saveScreenshotIfEnabled(ScreenshotType screenshotType)
2807 {
2808     if (gScreenshotDir != nullptr && gSaveScreenshots && !mScreenshotSaved &&
2809         (static_cast<uint32_t>(mScreenshotFrame) == mCurrentIteration ||
2810          mScreenshotFrame == kAllFrames))
2811     {
2812         std::stringstream screenshotNameStr;
2813         screenshotNameStr << gScreenshotDir << GetPathSeparator() << "angle" << mBackend << "_"
2814                           << mStory;
2815 
2816         // Add a marker to the name for any screenshot that isn't start frame
2817         if (mStartFrame != static_cast<uint32_t>(mScreenshotFrame))
2818         {
2819             if (screenshotType == ScreenshotType::kFrame)
2820             {
2821                 screenshotNameStr << "_frame" << mCurrentIteration;
2822             }
2823             else
2824             {
2825                 screenshotNameStr << "_grid" << mCurrentOffscreenGridIteration;
2826             }
2827         }
2828 
2829         screenshotNameStr << ".png";
2830 
2831         std::string screenshotName = screenshotNameStr.str();
2832         saveScreenshot(screenshotName);
2833 
2834         // Only set this value if we're capturing a single frame
2835         mScreenshotSaved = mScreenshotFrame != kAllFrames;
2836     }
2837 }
2838 
saveScreenshot(const std::string & screenshotName)2839 void TracePerfTest::saveScreenshot(const std::string &screenshotName)
2840 {
2841     // The frame is already rendered and is waiting in the default framebuffer.
2842 
2843     // RGBA 4-byte data.
2844     uint32_t pixelCount = mTestParams.windowWidth * mTestParams.windowHeight;
2845     std::vector<uint8_t> pixelData(pixelCount * 4);
2846 
2847     glFinish();
2848 
2849     glReadPixels(0, 0, mTestParams.windowWidth, mTestParams.windowHeight, GL_RGBA, GL_UNSIGNED_BYTE,
2850                  pixelData.data());
2851 
2852     // Convert to RGB and flip y.
2853     std::vector<uint8_t> rgbData(pixelCount * 3);
2854     for (EGLint y = 0; y < mTestParams.windowHeight; ++y)
2855     {
2856         for (EGLint x = 0; x < mTestParams.windowWidth; ++x)
2857         {
2858             EGLint srcPixel = x + y * mTestParams.windowWidth;
2859             EGLint dstPixel = x + (mTestParams.windowHeight - y - 1) * mTestParams.windowWidth;
2860             memcpy(&rgbData[dstPixel * 3], &pixelData[srcPixel * 4], 3);
2861         }
2862     }
2863 
2864     if (!angle::SavePNGRGB(screenshotName.c_str(), "ANGLE Screenshot", mTestParams.windowWidth,
2865                            mTestParams.windowHeight, rgbData))
2866     {
2867         failTest(std::string("Error saving screenshot: ") + screenshotName);
2868         return;
2869     }
2870     else
2871     {
2872         printf("Saved screenshot: '%s'\n", screenshotName.c_str());
2873     }
2874 }
2875 }  // anonymous namespace
2876 
2877 using namespace params;
2878 
RegisterTraceTests()2879 void RegisterTraceTests()
2880 {
2881     GLESDriverType driverType = GetDriverTypeFromString(gUseGL, GLESDriverType::AngleEGL);
2882     GLenum platformType       = EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE;
2883     GLenum deviceType         = EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE;
2884     if (IsANGLE(driverType))
2885     {
2886         platformType = GetPlatformANGLETypeFromArg(gUseANGLE, EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE);
2887         deviceType =
2888             GetANGLEDeviceTypeFromArg(gUseANGLE, EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE);
2889     }
2890 
2891     char rootTracePath[kMaxPath] = {};
2892     if (!FindRootTraceTestDataPath(rootTracePath, kMaxPath))
2893     {
2894         ERR() << "Unable to find trace folder " << rootTracePath;
2895         return;
2896     }
2897 
2898     // Load JSON data.
2899     std::vector<std::string> traces;
2900     {
2901         char traceListPath[kMaxPath] = {};
2902         if (!angle::FindTestDataPath("gen/trace_list.json", traceListPath, kMaxPath))
2903         {
2904             ERR() << "Cannot find gen/trace_list.json";
2905             return;
2906         }
2907 
2908         if (!LoadTraceNamesFromJSON(traceListPath, &traces))
2909         {
2910             ERR() << "Unable to load traces from JSON file: " << traceListPath;
2911             return;
2912         }
2913     }
2914 
2915     std::vector<TraceInfo> traceInfos;
2916     for (const std::string &trace : traces)
2917     {
2918         std::stringstream traceJsonStream;
2919         traceJsonStream << rootTracePath << GetPathSeparator() << trace << GetPathSeparator()
2920                         << trace << ".json";
2921         std::string traceJsonPath = traceJsonStream.str();
2922 
2923         TraceInfo traceInfo = {};
2924         strncpy(traceInfo.name, trace.c_str(), kTraceInfoMaxNameLen);
2925         traceInfo.initialized = LoadTraceInfoFromJSON(trace, traceJsonPath, &traceInfo);
2926 
2927         traceInfos.push_back(traceInfo);
2928     }
2929 
2930     for (const TraceInfo &traceInfo : traceInfos)
2931     {
2932         const TracePerfParams params(traceInfo, driverType, platformType, deviceType);
2933 
2934         if (!IsPlatformAvailable(params))
2935         {
2936             continue;
2937         }
2938 
2939         auto factory = [params]() {
2940             return new TracePerfTest(std::make_unique<TracePerfParams>(params));
2941         };
2942         testing::RegisterTest("TraceTest", traceInfo.name, nullptr, nullptr, __FILE__, __LINE__,
2943                               factory);
2944     }
2945 }
2946