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/egl_loader_autogen.h"
19 #include "util/png_utils.h"
20 #include "util/test_utils.h"
21 
22 #include "restricted_traces/restricted_traces_autogen.h"
23 
24 #include <rapidjson/document.h>
25 #include <rapidjson/istreamwrapper.h>
26 
27 #include <cassert>
28 #include <fstream>
29 #include <functional>
30 #include <sstream>
31 
32 // When --minimize-gpu-work is specified, we want to reduce GPU work to minimum and lift up the CPU
33 // overhead to surface so that we can see how much CPU overhead each driver has for each app trace.
34 // On some driver(s) the bufferSubData/texSubImage calls end up dominating the frame time when the
35 // actual GPU work is minimized. Even reducing the texSubImage calls to only update 1x1 area is not
36 // enough. The driver may be implementing copy on write by cloning the entire texture to another
37 // memory storage for texSubImage call. While this information is also important for performance,
38 // they should be evaluated separately in real app usage scenario, or write stand alone tests for
39 // these. For the purpose of CPU overhead and avoid data copy to dominate the trace, I am using this
40 // flag to noop the texSubImage and bufferSubData call when --minimize-gpu-work is specified. Feel
41 // free to disable this when you have other needs. Or it can be turned to another run time option
42 // when desired.
43 #define NOOP_SUBDATA_SUBIMAGE_FOR_MINIMIZE_GPU_WORK
44 
45 using namespace angle;
46 using namespace egl_platform;
47 
48 namespace
49 {
50 constexpr size_t kMaxPath = 1024;
51 
52 struct TracePerfParams final : public RenderTestParams
53 {
54     // Common default options
TracePerfParams__anon20281::final55     TracePerfParams()
56     {
57         // Display the frame after every drawBenchmark invocation
58         iterationsPerStep = 1;
59     }
60 
61     std::string story() const override
62     {
63         std::stringstream strstr;
64         strstr << RenderTestParams::story() << "_" << traceInfo.name;
65         return strstr.str();
66     }
67 
68     TraceInfo traceInfo = {};
69 };
70 
operator <<(std::ostream &os, const TracePerfParams &params)71 std::ostream &operator<<(std::ostream &os, const TracePerfParams &params)
72 {
73     os << params.backendAndStory().substr(1);
74     return os;
75 }
76 
77 class TracePerfTest : public ANGLERenderTest
78 {
79   public:
80     TracePerfTest(const TracePerfParams &params);
81 
82     void initializeBenchmark() override;
83     void destroyBenchmark() override;
84     void drawBenchmark() override;
85 
86     // TODO(http://www.anglebug.com/5878): Add support for creating EGLSurface:
87     // - eglCreatePbufferSurface()
88     // - eglCreateWindowSurface()
89     EGLContext onEglCreateContext(EGLDisplay display,
90                                   EGLConfig config,
91                                   EGLContext share_context,
92                                   EGLint const *attrib_list);
93     void onEglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context);
94     EGLContext onEglGetCurrentContext();
95     void onReplayFramebufferChange(GLenum target, GLuint framebuffer);
96     void onReplayInvalidateFramebuffer(GLenum target,
97                                        GLsizei numAttachments,
98                                        const GLenum *attachments);
99     void onReplayInvalidateSubFramebuffer(GLenum target,
100                                           GLsizei numAttachments,
101                                           const GLenum *attachments,
102                                           GLint x,
103                                           GLint y,
104                                           GLsizei width,
105                                           GLsizei height);
106     void onReplayDrawBuffers(GLsizei n, const GLenum *bufs);
107     void onReplayReadBuffer(GLenum src);
108     void onReplayDiscardFramebufferEXT(GLenum target,
109                                        GLsizei numAttachments,
110                                        const GLenum *attachments);
111 
112     void validateSerializedState(const char *serializedState, const char *fileName, uint32_t line);
113 
114     bool isDefaultFramebuffer(GLenum target) const;
115 
116     double getHostTimeFromGLTime(GLint64 glTime);
117 
frameCount() const118     uint32_t frameCount() const
119     {
120         const TraceInfo &traceInfo = mParams.traceInfo;
121         return traceInfo.frameEnd - traceInfo.frameStart + 1;
122     }
123 
124     int getStepAlignment() const override
125     {
126         // Align step counts to the number of frames in a trace.
127         return static_cast<int>(frameCount());
128     }
129 
130     void TestBody() override { run(); }
131 
traceNameIs(const char *name) const132     bool traceNameIs(const char *name) const
133     {
134         return strncmp(name, mParams.traceInfo.name, kTraceInfoMaxNameLen) == 0;
135     }
136 
137   private:
138     struct QueryInfo
139     {
140         GLuint beginTimestampQuery;
141         GLuint endTimestampQuery;
142         GLuint framebuffer;
143     };
144 
145     struct TimeSample
146     {
147         GLint64 glTime;
148         double hostTime;
149     };
150 
151     void sampleTime();
152     void saveScreenshot(const std::string &screenshotName) override;
153     void swap();
154 
155     const TracePerfParams mParams;
156 
157     uint32_t mStartFrame;
158     uint32_t mEndFrame;
159 
160     // For tracking RenderPass/FBO change timing.
161     QueryInfo mCurrentQuery = {};
162     std::vector<QueryInfo> mRunningQueries;
163     std::vector<TimeSample> mTimeline;
164 
165     std::string mStartingDirectory;
166     bool mUseTimestampQueries                                           = false;
167     static constexpr int mMaxOffscreenBufferCount                       = 2;
168     std::array<GLuint, mMaxOffscreenBufferCount> mOffscreenFramebuffers = {0, 0};
169     std::array<GLuint, mMaxOffscreenBufferCount> mOffscreenTextures     = {0, 0};
170     GLuint mOffscreenDepthStencil                                       = 0;
171     int mWindowWidth                                                    = 0;
172     int mWindowHeight                                                   = 0;
173     GLuint mDrawFramebufferBinding                                      = 0;
174     GLuint mReadFramebufferBinding                                      = 0;
175     uint32_t mCurrentFrame                                              = 0;
176     uint32_t mOffscreenFrameCount                                       = 0;
177     uint32_t mTotalFrameCount                                           = 0;
178     bool mScreenshotSaved                                               = false;
179     std::unique_ptr<TraceLibrary> mTraceLibrary;
180 };
181 
182 TracePerfTest *gCurrentTracePerfTest = nullptr;
183 
184 // 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)185 EGLContext KHRONOS_APIENTRY EglCreateContext(EGLDisplay display,
186                                              EGLConfig config,
187                                              EGLContext share_context,
188                                              EGLint const *attrib_list)
189 {
190     return gCurrentTracePerfTest->onEglCreateContext(display, config, share_context, attrib_list);
191 }
192 
EglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context)193 void KHRONOS_APIENTRY EglMakeCurrent(EGLDisplay display,
194                                      EGLSurface draw,
195                                      EGLSurface read,
196                                      EGLContext context)
197 {
198     gCurrentTracePerfTest->onEglMakeCurrent(display, draw, read, context);
199 }
200 
EglGetCurrentContext()201 EGLContext KHRONOS_APIENTRY EglGetCurrentContext()
202 {
203     return gCurrentTracePerfTest->onEglGetCurrentContext();
204 }
205 
BindFramebufferProc(GLenum target, GLuint framebuffer)206 void KHRONOS_APIENTRY BindFramebufferProc(GLenum target, GLuint framebuffer)
207 {
208     gCurrentTracePerfTest->onReplayFramebufferChange(target, framebuffer);
209 }
210 
InvalidateFramebufferProc(GLenum target, GLsizei numAttachments, const GLenum *attachments)211 void KHRONOS_APIENTRY InvalidateFramebufferProc(GLenum target,
212                                                 GLsizei numAttachments,
213                                                 const GLenum *attachments)
214 {
215     gCurrentTracePerfTest->onReplayInvalidateFramebuffer(target, numAttachments, attachments);
216 }
217 
InvalidateSubFramebufferProc(GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height)218 void KHRONOS_APIENTRY InvalidateSubFramebufferProc(GLenum target,
219                                                    GLsizei numAttachments,
220                                                    const GLenum *attachments,
221                                                    GLint x,
222                                                    GLint y,
223                                                    GLsizei width,
224                                                    GLsizei height)
225 {
226     gCurrentTracePerfTest->onReplayInvalidateSubFramebuffer(target, numAttachments, attachments, x,
227                                                             y, width, height);
228 }
229 
DrawBuffersProc(GLsizei n, const GLenum *bufs)230 void KHRONOS_APIENTRY DrawBuffersProc(GLsizei n, const GLenum *bufs)
231 {
232     gCurrentTracePerfTest->onReplayDrawBuffers(n, bufs);
233 }
234 
ReadBufferProc(GLenum src)235 void KHRONOS_APIENTRY ReadBufferProc(GLenum src)
236 {
237     gCurrentTracePerfTest->onReplayReadBuffer(src);
238 }
239 
DiscardFramebufferEXTProc(GLenum target, GLsizei numAttachments, const GLenum *attachments)240 void KHRONOS_APIENTRY DiscardFramebufferEXTProc(GLenum target,
241                                                 GLsizei numAttachments,
242                                                 const GLenum *attachments)
243 {
244     gCurrentTracePerfTest->onReplayDiscardFramebufferEXT(target, numAttachments, attachments);
245 }
246 
ViewportMinimizedProc(GLint x, GLint y, GLsizei width, GLsizei height)247 void KHRONOS_APIENTRY ViewportMinimizedProc(GLint x, GLint y, GLsizei width, GLsizei height)
248 {
249     glViewport(x, y, 1, 1);
250 }
251 
ScissorMinimizedProc(GLint x, GLint y, GLsizei width, GLsizei height)252 void KHRONOS_APIENTRY ScissorMinimizedProc(GLint x, GLint y, GLsizei width, GLsizei height)
253 {
254     glScissor(x, y, 1, 1);
255 }
256 
257 // Interpose the calls that generate actual GPU work
DrawElementsMinimizedProc(GLenum mode, GLsizei count, GLenum type, const void *indices)258 void KHRONOS_APIENTRY DrawElementsMinimizedProc(GLenum mode,
259                                                 GLsizei count,
260                                                 GLenum type,
261                                                 const void *indices)
262 {
263     glDrawElements(GL_POINTS, 1, type, indices);
264 }
265 
DrawElementsIndirectMinimizedProc(GLenum mode, GLenum type, const void *indirect)266 void KHRONOS_APIENTRY DrawElementsIndirectMinimizedProc(GLenum mode,
267                                                         GLenum type,
268                                                         const void *indirect)
269 {
270     glDrawElementsInstancedBaseVertex(GL_POINTS, 1, type, 0, 1, 0);
271 }
272 
DrawElementsInstancedMinimizedProc(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount)273 void KHRONOS_APIENTRY DrawElementsInstancedMinimizedProc(GLenum mode,
274                                                          GLsizei count,
275                                                          GLenum type,
276                                                          const void *indices,
277                                                          GLsizei instancecount)
278 {
279     glDrawElementsInstanced(GL_POINTS, 1, type, indices, 1);
280 }
281 
DrawElementsBaseVertexMinimizedProc(GLenum mode, GLsizei count, GLenum type, const void *indices, GLint basevertex)282 void KHRONOS_APIENTRY DrawElementsBaseVertexMinimizedProc(GLenum mode,
283                                                           GLsizei count,
284                                                           GLenum type,
285                                                           const void *indices,
286                                                           GLint basevertex)
287 {
288     glDrawElementsBaseVertex(GL_POINTS, 1, type, indices, basevertex);
289 }
290 
DrawElementsInstancedBaseVertexMinimizedProc(GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex)291 void KHRONOS_APIENTRY DrawElementsInstancedBaseVertexMinimizedProc(GLenum mode,
292                                                                    GLsizei count,
293                                                                    GLenum type,
294                                                                    const void *indices,
295                                                                    GLsizei instancecount,
296                                                                    GLint basevertex)
297 {
298     glDrawElementsInstancedBaseVertex(GL_POINTS, 1, type, indices, 1, basevertex);
299 }
300 
DrawRangeElementsMinimizedProc(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices)301 void KHRONOS_APIENTRY DrawRangeElementsMinimizedProc(GLenum mode,
302                                                      GLuint start,
303                                                      GLuint end,
304                                                      GLsizei count,
305                                                      GLenum type,
306                                                      const void *indices)
307 {
308     glDrawRangeElements(GL_POINTS, start, end, 1, type, indices);
309 }
310 
DrawArraysMinimizedProc(GLenum mode, GLint first, GLsizei count)311 void KHRONOS_APIENTRY DrawArraysMinimizedProc(GLenum mode, GLint first, GLsizei count)
312 {
313     glDrawArrays(GL_POINTS, first, 1);
314 }
315 
DrawArraysInstancedMinimizedProc(GLenum mode, GLint first, GLsizei count, GLsizei instancecount)316 void KHRONOS_APIENTRY DrawArraysInstancedMinimizedProc(GLenum mode,
317                                                        GLint first,
318                                                        GLsizei count,
319                                                        GLsizei instancecount)
320 {
321     glDrawArraysInstanced(GL_POINTS, first, 1, 1);
322 }
323 
DrawArraysIndirectMinimizedProc(GLenum mode, const void *indirect)324 void KHRONOS_APIENTRY DrawArraysIndirectMinimizedProc(GLenum mode, const void *indirect)
325 {
326     glDrawArraysInstanced(GL_POINTS, 0, 1, 1);
327 }
328 
DispatchComputeMinimizedProc(GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z)329 void KHRONOS_APIENTRY DispatchComputeMinimizedProc(GLuint num_groups_x,
330                                                    GLuint num_groups_y,
331                                                    GLuint num_groups_z)
332 {
333     glDispatchCompute(1, 1, 1);
334 }
335 
DispatchComputeIndirectMinimizedProc(GLintptr indirect)336 void KHRONOS_APIENTRY DispatchComputeIndirectMinimizedProc(GLintptr indirect)
337 {
338     glDispatchCompute(1, 1, 1);
339 }
340 
341 // Interpose the calls that generate data copying work
BufferDataMinimizedProc(GLenum target, GLsizeiptr size, const void *data, GLenum usage)342 void KHRONOS_APIENTRY BufferDataMinimizedProc(GLenum target,
343                                               GLsizeiptr size,
344                                               const void *data,
345                                               GLenum usage)
346 {
347     glBufferData(target, size, nullptr, usage);
348 }
349 
BufferSubDataMinimizedProc(GLenum target, GLintptr offset, GLsizeiptr size, const void *data)350 void KHRONOS_APIENTRY BufferSubDataMinimizedProc(GLenum target,
351                                                  GLintptr offset,
352                                                  GLsizeiptr size,
353                                                  const void *data)
354 {
355 #if !defined(NOOP_SUBDATA_SUBIMAGE_FOR_MINIMIZE_GPU_WORK)
356     glBufferSubData(target, offset, 1, data);
357 #endif
358 }
359 
MapBufferRangeMinimizedProc(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)360 void *KHRONOS_APIENTRY MapBufferRangeMinimizedProc(GLenum target,
361                                                    GLintptr offset,
362                                                    GLsizeiptr length,
363                                                    GLbitfield access)
364 {
365     access |= GL_MAP_UNSYNCHRONIZED_BIT;
366     return glMapBufferRange(target, offset, length, access);
367 }
368 
TexImage2DMinimizedProc(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void *pixels)369 void KHRONOS_APIENTRY TexImage2DMinimizedProc(GLenum target,
370                                               GLint level,
371                                               GLint internalformat,
372                                               GLsizei width,
373                                               GLsizei height,
374                                               GLint border,
375                                               GLenum format,
376                                               GLenum type,
377                                               const void *pixels)
378 {
379     GLint unpackBuffer = 0;
380     glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &unpackBuffer);
381     if (unpackBuffer)
382     {
383         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
384     }
385     glTexImage2D(target, level, internalformat, width, height, border, format, type, nullptr);
386     if (unpackBuffer)
387     {
388         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, unpackBuffer);
389     }
390 }
391 
TexSubImage2DMinimizedProc(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *pixels)392 void KHRONOS_APIENTRY TexSubImage2DMinimizedProc(GLenum target,
393                                                  GLint level,
394                                                  GLint xoffset,
395                                                  GLint yoffset,
396                                                  GLsizei width,
397                                                  GLsizei height,
398                                                  GLenum format,
399                                                  GLenum type,
400                                                  const void *pixels)
401 {
402 #if !defined(NOOP_SUBDATA_SUBIMAGE_FOR_MINIMIZE_GPU_WORK)
403     glTexSubImage2D(target, level, xoffset, yoffset, 1, 1, format, type, pixels);
404 #endif
405 }
406 
TexImage3DMinimizedProc(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void *pixels)407 void KHRONOS_APIENTRY TexImage3DMinimizedProc(GLenum target,
408                                               GLint level,
409                                               GLint internalformat,
410                                               GLsizei width,
411                                               GLsizei height,
412                                               GLsizei depth,
413                                               GLint border,
414                                               GLenum format,
415                                               GLenum type,
416                                               const void *pixels)
417 {
418     GLint unpackBuffer = 0;
419     glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &unpackBuffer);
420     if (unpackBuffer)
421     {
422         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
423     }
424     glTexImage3D(target, level, internalformat, width, height, depth, border, format, type,
425                  nullptr);
426     if (unpackBuffer)
427     {
428         glBindBuffer(GL_PIXEL_UNPACK_BUFFER, unpackBuffer);
429     }
430 }
431 
TexSubImage3DMinimizedProc(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void *pixels)432 void KHRONOS_APIENTRY TexSubImage3DMinimizedProc(GLenum target,
433                                                  GLint level,
434                                                  GLint xoffset,
435                                                  GLint yoffset,
436                                                  GLint zoffset,
437                                                  GLsizei width,
438                                                  GLsizei height,
439                                                  GLsizei depth,
440                                                  GLenum format,
441                                                  GLenum type,
442                                                  const void *pixels)
443 {
444 #if !defined(NOOP_SUBDATA_SUBIMAGE_FOR_MINIMIZE_GPU_WORK)
445     glTexSubImage3D(target, level, xoffset, yoffset, zoffset, 1, 1, 1, format, type, pixels);
446 #endif
447 }
448 
GenerateMipmapMinimizedProc(GLenum target)449 void KHRONOS_APIENTRY GenerateMipmapMinimizedProc(GLenum target)
450 {
451     // Noop it for now. There is a risk that this will leave an incomplete mipmap chain and cause
452     // other issues. If this turns out to be a real issue with app traces, we can turn this into a
453     // glTexImage2D call for each generated level.
454 }
455 
BlitFramebufferMinimizedProc(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)456 void KHRONOS_APIENTRY BlitFramebufferMinimizedProc(GLint srcX0,
457                                                    GLint srcY0,
458                                                    GLint srcX1,
459                                                    GLint srcY1,
460                                                    GLint dstX0,
461                                                    GLint dstY0,
462                                                    GLint dstX1,
463                                                    GLint dstY1,
464                                                    GLbitfield mask,
465                                                    GLenum filter)
466 {
467     glBlitFramebuffer(srcX0, srcY0, srcX0 + 1, srcY0 + 1, dstX0, dstY0, dstX0 + 1, dstY0 + 1, mask,
468                       filter);
469 }
470 
ReadPixelsMinimizedProc(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, void *pixels)471 void KHRONOS_APIENTRY ReadPixelsMinimizedProc(GLint x,
472                                               GLint y,
473                                               GLsizei width,
474                                               GLsizei height,
475                                               GLenum format,
476                                               GLenum type,
477                                               void *pixels)
478 {
479     glReadPixels(x, y, 1, 1, format, type, pixels);
480 }
481 
BeginTransformFeedbackMinimizedProc(GLenum primitiveMode)482 void KHRONOS_APIENTRY BeginTransformFeedbackMinimizedProc(GLenum primitiveMode)
483 {
484     glBeginTransformFeedback(GL_POINTS);
485 }
486 
TraceLoadProc(const char *procName)487 angle::GenericProc KHRONOS_APIENTRY TraceLoadProc(const char *procName)
488 {
489     // EGL
490     if (strcmp(procName, "eglCreateContext") == 0)
491     {
492         return reinterpret_cast<angle::GenericProc>(EglCreateContext);
493     }
494     if (strcmp(procName, "eglMakeCurrent") == 0)
495     {
496         return reinterpret_cast<angle::GenericProc>(EglMakeCurrent);
497     }
498     if (strcmp(procName, "eglGetCurrentContext") == 0)
499     {
500         return reinterpret_cast<angle::GenericProc>(EglGetCurrentContext);
501     }
502 
503     // GLES
504     if (strcmp(procName, "glBindFramebuffer") == 0)
505     {
506         return reinterpret_cast<angle::GenericProc>(BindFramebufferProc);
507     }
508     if (strcmp(procName, "glInvalidateFramebuffer") == 0)
509     {
510         return reinterpret_cast<angle::GenericProc>(InvalidateFramebufferProc);
511     }
512     if (strcmp(procName, "glInvalidateSubFramebuffer") == 0)
513     {
514         return reinterpret_cast<angle::GenericProc>(InvalidateSubFramebufferProc);
515     }
516     if (strcmp(procName, "glDrawBuffers") == 0)
517     {
518         return reinterpret_cast<angle::GenericProc>(DrawBuffersProc);
519     }
520     if (strcmp(procName, "glReadBuffer") == 0)
521     {
522         return reinterpret_cast<angle::GenericProc>(ReadBufferProc);
523     }
524     if (strcmp(procName, "glDiscardFramebufferEXT") == 0)
525     {
526         return reinterpret_cast<angle::GenericProc>(DiscardFramebufferEXTProc);
527     }
528 
529     if (gMinimizeGPUWork)
530     {
531         if (strcmp(procName, "glViewport") == 0)
532         {
533             return reinterpret_cast<angle::GenericProc>(ViewportMinimizedProc);
534         }
535 
536         if (strcmp(procName, "glScissor") == 0)
537         {
538             return reinterpret_cast<angle::GenericProc>(ScissorMinimizedProc);
539         }
540 
541         // Interpose the calls that generate actual GPU work
542         if (strcmp(procName, "glDrawElements") == 0)
543         {
544             return reinterpret_cast<angle::GenericProc>(DrawElementsMinimizedProc);
545         }
546         if (strcmp(procName, "glDrawElementsIndirect") == 0)
547         {
548             return reinterpret_cast<angle::GenericProc>(DrawElementsIndirectMinimizedProc);
549         }
550         if (strcmp(procName, "glDrawElementsInstanced") == 0 ||
551             strcmp(procName, "glDrawElementsInstancedEXT") == 0)
552         {
553             return reinterpret_cast<angle::GenericProc>(DrawElementsInstancedMinimizedProc);
554         }
555         if (strcmp(procName, "glDrawElementsBaseVertex") == 0 ||
556             strcmp(procName, "glDrawElementsBaseVertexEXT") == 0 ||
557             strcmp(procName, "glDrawElementsBaseVertexOES") == 0)
558         {
559             return reinterpret_cast<angle::GenericProc>(DrawElementsBaseVertexMinimizedProc);
560         }
561         if (strcmp(procName, "glDrawElementsInstancedBaseVertex") == 0 ||
562             strcmp(procName, "glDrawElementsInstancedBaseVertexEXT") == 0 ||
563             strcmp(procName, "glDrawElementsInstancedBaseVertexOES") == 0)
564         {
565             return reinterpret_cast<angle::GenericProc>(
566                 DrawElementsInstancedBaseVertexMinimizedProc);
567         }
568         if (strcmp(procName, "glDrawRangeElements") == 0)
569         {
570             return reinterpret_cast<angle::GenericProc>(DrawRangeElementsMinimizedProc);
571         }
572         if (strcmp(procName, "glDrawArrays") == 0)
573         {
574             return reinterpret_cast<angle::GenericProc>(DrawArraysMinimizedProc);
575         }
576         if (strcmp(procName, "glDrawArraysInstanced") == 0 ||
577             strcmp(procName, "glDrawArraysInstancedEXT") == 0)
578         {
579             return reinterpret_cast<angle::GenericProc>(DrawArraysInstancedMinimizedProc);
580         }
581         if (strcmp(procName, "glDrawArraysIndirect") == 0)
582         {
583             return reinterpret_cast<angle::GenericProc>(DrawArraysIndirectMinimizedProc);
584         }
585         if (strcmp(procName, "glDispatchCompute") == 0)
586         {
587             return reinterpret_cast<angle::GenericProc>(DispatchComputeMinimizedProc);
588         }
589         if (strcmp(procName, "glDispatchComputeIndirect") == 0)
590         {
591             return reinterpret_cast<angle::GenericProc>(DispatchComputeIndirectMinimizedProc);
592         }
593 
594         // Interpose the calls that generate data copying work
595         if (strcmp(procName, "glBufferData") == 0)
596         {
597             return reinterpret_cast<angle::GenericProc>(BufferDataMinimizedProc);
598         }
599         if (strcmp(procName, "glBufferSubData") == 0)
600         {
601             return reinterpret_cast<angle::GenericProc>(BufferSubDataMinimizedProc);
602         }
603         if (strcmp(procName, "glMapBufferRange") == 0 ||
604             strcmp(procName, "glMapBufferRangeEXT") == 0)
605         {
606             return reinterpret_cast<angle::GenericProc>(MapBufferRangeMinimizedProc);
607         }
608         if (strcmp(procName, "glTexImage2D") == 0)
609         {
610             return reinterpret_cast<angle::GenericProc>(TexImage2DMinimizedProc);
611         }
612         if (strcmp(procName, "glTexImage3D") == 0)
613         {
614             return reinterpret_cast<angle::GenericProc>(TexImage3DMinimizedProc);
615         }
616         if (strcmp(procName, "glTexSubImage2D") == 0)
617         {
618             return reinterpret_cast<angle::GenericProc>(TexSubImage2DMinimizedProc);
619         }
620         if (strcmp(procName, "glTexSubImage3D") == 0)
621         {
622             return reinterpret_cast<angle::GenericProc>(TexSubImage3DMinimizedProc);
623         }
624         if (strcmp(procName, "glGenerateMipmap") == 0 ||
625             strcmp(procName, "glGenerateMipmapOES") == 0)
626         {
627             return reinterpret_cast<angle::GenericProc>(GenerateMipmapMinimizedProc);
628         }
629         if (strcmp(procName, "glBlitFramebuffer") == 0)
630         {
631             return reinterpret_cast<angle::GenericProc>(BlitFramebufferMinimizedProc);
632         }
633         if (strcmp(procName, "glReadPixels") == 0)
634         {
635             return reinterpret_cast<angle::GenericProc>(ReadPixelsMinimizedProc);
636         }
637         if (strcmp(procName, "glBeginTransformFeedback") == 0)
638         {
639             return reinterpret_cast<angle::GenericProc>(BeginTransformFeedbackMinimizedProc);
640         }
641     }
642 
643     return gCurrentTracePerfTest->getGLWindow()->getProcAddress(procName);
644 }
645 
ValidateSerializedState(const char *serializedState, const char *fileName, uint32_t line)646 void ValidateSerializedState(const char *serializedState, const char *fileName, uint32_t line)
647 {
648     gCurrentTracePerfTest->validateSerializedState(serializedState, fileName, line);
649 }
650 
651 constexpr char kTraceTestFolder[] = "src/tests/restricted_traces";
652 
FindTraceTestDataPath(const char *traceName, char *testDataDirOut, size_t maxDataDirLen)653 bool FindTraceTestDataPath(const char *traceName, char *testDataDirOut, size_t maxDataDirLen)
654 {
655     char relativeTestDataDir[kMaxPath] = {};
656     snprintf(relativeTestDataDir, kMaxPath, "%s%c%s", kTraceTestFolder, GetPathSeparator(),
657              traceName);
658     return angle::FindTestDataPath(relativeTestDataDir, testDataDirOut, maxDataDirLen);
659 }
660 
FindRootTraceTestDataPath(char *testDataDirOut, size_t maxDataDirLen)661 bool FindRootTraceTestDataPath(char *testDataDirOut, size_t maxDataDirLen)
662 {
663     return angle::FindTestDataPath(kTraceTestFolder, testDataDirOut, maxDataDirLen);
664 }
665 
TracePerfTest(const TracePerfParams &params)666 TracePerfTest::TracePerfTest(const TracePerfParams &params)
667     : ANGLERenderTest("TracePerf", params, "ms"), mParams(params), mStartFrame(0), mEndFrame(0)
668 {
669     // TODO: http://anglebug.com/4533 This fails after the upgrade to the 26.20.100.7870 driver.
670     if (IsWindows() && IsIntel() && mParams.isVulkan() && traceNameIs("manhattan_10"))
671     {
672         mSkipTest = true;
673     }
674 
675     // TODO: http://anglebug.com/4731 Fails on older Intel drivers. Passes in newer.
676     if (IsWindows() && IsIntel() && !mParams.isANGLE() && traceNameIs("angry_birds_2_1500"))
677     {
678         mSkipTest = true;
679     }
680 
681     if (traceNameIs("cod_mobile"))
682     {
683         // TODO: http://anglebug.com/4967 Vulkan: GL_EXT_color_buffer_float not supported on Pixel 2
684         // The COD:Mobile trace uses a framebuffer attachment with:
685         //   format = GL_RGB
686         //   type = GL_UNSIGNED_INT_10F_11F_11F_REV
687         // That combination is only renderable if GL_EXT_color_buffer_float is supported.
688         // It happens to not be supported on Pixel 2's Vulkan driver.
689         addExtensionPrerequisite("GL_EXT_color_buffer_float");
690 
691         // TODO: http://anglebug.com/4731 This extension is missing on older Intel drivers.
692         addExtensionPrerequisite("GL_OES_EGL_image_external");
693 
694         // Flaky on Intel/windows http://anglebug.com/6568
695         if (IsWindows() && IsIntel())
696         {
697             mSkipTest = true;
698         }
699     }
700 
701     if (traceNameIs("brawl_stars"))
702     {
703         addExtensionPrerequisite("GL_EXT_shadow_samplers");
704     }
705 
706     if (traceNameIs("free_fire"))
707     {
708         addExtensionPrerequisite("GL_OES_EGL_image_external");
709     }
710 
711     if (traceNameIs("marvel_contest_of_champions"))
712     {
713         addExtensionPrerequisite("GL_EXT_color_buffer_half_float");
714     }
715 
716     if (traceNameIs("world_of_tanks_blitz"))
717     {
718         addExtensionPrerequisite("GL_EXT_disjoint_timer_query");
719     }
720 
721     if (traceNameIs("dragon_ball_legends"))
722     {
723         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
724     }
725 
726     if (traceNameIs("lego_legacy"))
727     {
728         addExtensionPrerequisite("GL_EXT_shadow_samplers");
729     }
730 
731     if (traceNameIs("world_war_doh"))
732     {
733         // Linux+NVIDIA doesn't support GL_KHR_texture_compression_astc_ldr (possibly others also)
734         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
735     }
736 
737     if (traceNameIs("saint_seiya_awakening"))
738     {
739         addExtensionPrerequisite("GL_EXT_shadow_samplers");
740 
741         // TODO(https://anglebug.com/5517) Linux+Intel generates "Framebuffer is incomplete" errors.
742         if (IsLinux() && IsIntel() && mParams.isVulkan())
743         {
744             mSkipTest = true;
745         }
746     }
747 
748     if (traceNameIs("magic_tiles_3"))
749     {
750         // Linux+NVIDIA doesn't support GL_KHR_texture_compression_astc_ldr (possibly others also)
751         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
752     }
753 
754     if (traceNameIs("real_gangster_crime"))
755     {
756         // Linux+NVIDIA doesn't support GL_KHR_texture_compression_astc_ldr (possibly others also)
757         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
758 
759         // Intel doesn't support external images.
760         addExtensionPrerequisite("GL_OES_EGL_image_external");
761 
762         // Failing on Linux Intel and AMD due to invalid enum. http://anglebug.com/5822
763         if (IsLinux() && (IsIntel() || IsAMD()) && mParams.driver != GLESDriverType::AngleEGL)
764         {
765             mSkipTest = true;
766         }
767     }
768 
769     if (traceNameIs("asphalt_8"))
770     {
771         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
772     }
773 
774     if (traceNameIs("hearthstone"))
775     {
776         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
777     }
778 
779     if (traceNameIs("efootball_pes_2021"))
780     {
781         // TODO(https://anglebug.com/5517) Linux+Intel and Pixel 2 generate "Framebuffer is
782         // incomplete" errors with the Vulkan backend.
783         if (mParams.isVulkan() && ((IsLinux() && IsIntel()) || IsPixel2()))
784         {
785             mSkipTest = true;
786         }
787     }
788 
789     if (traceNameIs("manhattan_31"))
790     {
791         // TODO: http://anglebug.com/5591 Trace crashes on Pixel 2 in vulkan driver
792         if (IsPixel2() && mParams.isVulkan())
793         {
794             mSkipTest = true;
795         }
796     }
797 
798     if (traceNameIs("idle_heroes"))
799     {
800         // TODO: http://anglebug.com/5591 Trace crashes on Pixel 2
801         if (IsPixel2())
802         {
803             mSkipTest = true;
804         }
805     }
806 
807     if (traceNameIs("shadow_fight_2"))
808     {
809         addExtensionPrerequisite("GL_OES_EGL_image_external");
810         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
811     }
812 
813     if (traceNameIs("rise_of_kingdoms"))
814     {
815         addExtensionPrerequisite("GL_OES_EGL_image_external");
816     }
817 
818     if (traceNameIs("happy_color"))
819     {
820         if (IsWindows() && IsAMD() && mParams.isVulkan())
821         {
822             mSkipTest = true;
823         }
824     }
825 
826     if (traceNameIs("bus_simulator_indonesia"))
827     {
828         // TODO(https://anglebug.com/5629) Linux+(Intel|AMD) native GLES generates
829         // GL_INVALID_OPERATION
830         if (IsLinux() && (IsIntel() || IsAMD()) && !mParams.isVulkan())
831         {
832             mSkipTest = true;
833         }
834     }
835 
836     if (traceNameIs("messenger_lite"))
837     {
838         // TODO: https://anglebug.com/5663 Incorrect pixels on NVIDIA Windows for first frame
839         if (IsWindows() && IsNVIDIA() && mParams.isVulkan() && !mParams.isSwiftshader())
840         {
841             mSkipTest = true;
842         }
843     }
844 
845     if (traceNameIs("among_us"))
846     {
847         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
848     }
849 
850     if (traceNameIs("car_parking_multiplayer"))
851     {
852         // TODO: https://anglebug.com/5613 NVIDIA native driver spews undefined behavior warnings
853         if (IsNVIDIA() && !mParams.isVulkan())
854         {
855             mSkipTest = true;
856         }
857         // TODO: https://anglebug.com/5724 Device lost on Win Intel
858         if (IsWindows() && IsIntel() && mParams.isVulkan())
859         {
860             mSkipTest = true;
861         }
862     }
863 
864     if (traceNameIs("fifa_mobile"))
865     {
866         // TODO: http://anglebug.com/5875 Intel Windows Vulkan flakily renders entirely black
867         if (IsWindows() && IsIntel() && mParams.isVulkan())
868         {
869             mSkipTest = true;
870         }
871     }
872 
873     if (traceNameIs("rope_hero_vice_town"))
874     {
875         // TODO: http://anglebug.com/5716 Trace crashes on Pixel 2 in vulkan driver
876         if (IsPixel2() && mParams.isVulkan())
877         {
878             mSkipTest = true;
879         }
880     }
881 
882     if (traceNameIs("extreme_car_driving_simulator"))
883     {
884         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
885     }
886 
887     if (traceNameIs("plants_vs_zombies_2"))
888     {
889         // TODO: http://crbug.com/1187752 Corrupted image
890         if (IsWindows() && IsAMD() && mParams.isVulkan())
891         {
892             mSkipTest = true;
893         }
894     }
895 
896     if (traceNameIs("junes_journey"))
897     {
898         addExtensionPrerequisite("GL_OES_EGL_image_external");
899     }
900 
901     if (traceNameIs("ragnarok_m_eternal_love"))
902     {
903         addExtensionPrerequisite("GL_OES_EGL_image_external");
904         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
905 
906         // TODO: http://anglebug.com/5772 Pixel 2 errors with "Framebuffer is incomplete" on Vulkan
907         if (IsPixel2() && mParams.isVulkan())
908         {
909             mSkipTest = true;
910         }
911     }
912 
913     if (traceNameIs("real_cricket_20"))
914     {
915         // TODO: http://anglebug.com/5777 ARM doesn't have enough VS storage blocks
916         if (IsAndroid() && IsARM())
917         {
918             mSkipTest = true;
919         }
920     }
921 
922     if (traceNameIs("league_of_legends_wild_rift"))
923     {
924         addExtensionPrerequisite("GL_OES_EGL_image_external");
925         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
926 
927         // TODO: http://anglebug.com/5815 Trace is crashing on Intel Linux
928         if (IsLinux() && IsIntel() && mParams.isVulkan())
929         {
930             mSkipTest = true;
931         }
932     }
933 
934     if (traceNameIs("aztec_ruins"))
935     {
936         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
937 
938         // TODO: http://anglebug.com/5553 Pixel 2 errors with "Framebuffer is incomplete" on Vulkan
939         if (IsPixel2() && mParams.isVulkan())
940         {
941             mSkipTest = true;
942         }
943     }
944 
945     if (traceNameIs("dragon_raja"))
946     {
947         addExtensionPrerequisite("GL_OES_EGL_image_external");
948 
949         // TODO: http://anglebug.com/5807 Intel Linux and Pixel 2 error with "Framebuffer is
950         // incomplete" on Vulkan
951         if (((IsLinux() && IsIntel()) || IsPixel2()) && mParams.isVulkan())
952         {
953             mSkipTest = true;
954         }
955     }
956 
957     // Adreno gives a driver error with empty/small draw calls. http://anglebug.com/5823
958     if (traceNameIs("hill_climb_racing"))
959     {
960         if (IsAndroid() && (IsPixel2() || IsPixel4()) &&
961             mParams.driver == GLESDriverType::SystemEGL)
962         {
963             mSkipTest = true;
964         }
965     }
966 
967     if (traceNameIs("avakin_life"))
968     {
969         addExtensionPrerequisite("GL_OES_EGL_image_external");
970     }
971 
972     if (traceNameIs("professional_baseball_spirits"))
973     {
974         // TODO(https://anglebug.com/5827) Linux+Mesa/RADV Vulkan generates
975         // GL_INVALID_FRAMEBUFFER_OPERATION.
976         // Mesa versions below 20.3.5 produce the same issue on Linux+Mesa/Intel Vulkan
977         if (IsLinux() && (IsAMD() || IsIntel()) && mParams.isVulkan() && !mParams.isSwiftshader())
978         {
979             mSkipTest = true;
980         }
981     }
982 
983     if (traceNameIs("call_break_offline_card_game"))
984     {
985         // TODO: http://anglebug.com/5837 Intel Linux Vulkan errors with "Framebuffer is incomplete"
986         if ((IsLinux() && IsIntel()) && mParams.isVulkan())
987         {
988             mSkipTest = true;
989         }
990     }
991 
992     if (traceNameIs("slingshot_test1") || traceNameIs("slingshot_test2"))
993     {
994         // TODO: http://anglebug.com/5877 Trace crashes on Pixel 2 in vulkan driver
995         if (IsPixel2() && mParams.isVulkan())
996         {
997             mSkipTest = true;
998         }
999     }
1000 
1001     if (traceNameIs("ludo_king"))
1002     {
1003         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1004     }
1005 
1006     // TODO: http://anglebug.com/5943 GL_INVALID_ENUM on Windows/Intel.
1007     if (traceNameIs("summoners_war"))
1008     {
1009         if (IsWindows() && IsIntel() && mParams.driver != GLESDriverType::AngleEGL)
1010         {
1011             mSkipTest = true;
1012         }
1013     }
1014 
1015     if (traceNameIs("pokemon_go"))
1016     {
1017         addExtensionPrerequisite("GL_EXT_texture_cube_map_array");
1018         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1019 
1020         // TODO: http://anglebug.com/5989 Intel Linux crashing on teardown
1021         // TODO: http://anglebug.com/5994 Intel Windows timing out periodically
1022         if ((IsLinux() || IsWindows()) && IsIntel() && mParams.isVulkan())
1023         {
1024             mSkipTest = true;
1025         }
1026     }
1027 
1028     if (traceNameIs("cookie_run_kingdom"))
1029     {
1030         addExtensionPrerequisite("GL_EXT_texture_cube_map_array");
1031         addExtensionPrerequisite("GL_OES_EGL_image_external");
1032 
1033         // TODO: http://anglebug.com/6017 ARM doesn't have enough VS storage blocks
1034         if (IsAndroid() && IsARM())
1035         {
1036             mSkipTest = true;
1037         }
1038     }
1039 
1040     if (traceNameIs("genshin_impact"))
1041     {
1042         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1043 
1044         // TODO: http://anglebug.com/6023 Crashes on Pixel 2 in vulkan driver
1045         // TODO: http://anglebug.com/6029 Crashes on Linux Intel Vulkan
1046         if (((IsLinux() && IsIntel()) || IsPixel2()) && mParams.isVulkan())
1047         {
1048             mSkipTest = true;
1049         }
1050 
1051         // Genshin is too large to handle in 32-bit mode.
1052         if (!Is64Bit())
1053         {
1054             mSkipTest = true;
1055         }
1056     }
1057 
1058     if (traceNameIs("mario_kart_tour"))
1059     {
1060         // Fails on native Mesa. http://anglebug.com/6711
1061         if (IsLinux() && IsIntel() && !mParams.isVulkan())
1062         {
1063             mSkipTest = true;
1064         }
1065     }
1066 
1067     if (traceNameIs("pubg_mobile_skydive") || traceNameIs("pubg_mobile_battle_royale"))
1068     {
1069         addExtensionPrerequisite("GL_EXT_texture_buffer");
1070 
1071         // TODO: http://anglebug.com/6240 Internal errors on Windows/Intel and NVIDIA
1072         if (((IsWindows() && IsIntel()) || IsNVIDIA()) && !mParams.isVulkan())
1073         {
1074             mSkipTest = true;
1075         }
1076     }
1077 
1078     if (traceNameIs("sakura_school_simulator"))
1079     {
1080         // Flaky on Intel. http://anglebug.com/6294
1081         if (IsWindows() && IsIntel())
1082         {
1083             mSkipTest = true;
1084         }
1085     }
1086 
1087     if (traceNameIs("scrabble_go"))
1088     {
1089         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1090     }
1091 
1092     if (traceNameIs("world_of_kings"))
1093     {
1094         addExtensionPrerequisite("GL_OES_EGL_image_external");
1095         // Flaky on Intel. http://anglebug.com/6372
1096         if (IsWindows() && IsIntel())
1097         {
1098             mSkipTest = true;
1099         }
1100     }
1101 
1102     if (traceNameIs("nier_reincarnation"))
1103     {
1104         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1105     }
1106 
1107     if (traceNameIs("mini_world"))
1108     {
1109         // TODO: http://anglebug.com/6443 Vulkan Test failure on Pixel4XL due to vulkan validation
1110         // error VUID-vkDestroyBuffer-buffer-00922
1111         if (IsQualcomm() && mParams.isVulkan())
1112         {
1113             mSkipTest = true;
1114         }
1115     }
1116 
1117     if (traceNameIs("pokemon_unite"))
1118     {
1119         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1120 
1121         // http://anglebug.com/6548 - nondeterministic on Intel+Windows
1122         // Crashes on Linux Intel
1123         if (IsIntel())
1124         {
1125             mSkipTest = true;
1126         }
1127     }
1128 
1129     if (traceNameIs("world_cricket_championship_2"))
1130     {
1131         addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
1132 
1133         // http://anglebug.com/6657 - Native test timing out on Intel Linux
1134         if (IsLinux() && IsIntel() && !mParams.isVulkan())
1135         {
1136             mSkipTest = true;
1137         }
1138     }
1139 
1140     if (traceNameIs("zillow"))
1141     {
1142         // http://anglebug.com/6658 - Crashing in Vulkan backend
1143         if ((IsLinux() || IsWindows()) && IsNVIDIA() && mParams.driver == GLESDriverType::AngleEGL)
1144         {
1145             mSkipTest = true;
1146         }
1147     }
1148 
1149     if (traceNameIs("township"))
1150     {
1151         addExtensionPrerequisite("GL_OES_EGL_image_external");
1152     }
1153 
1154     ASSERT(mParams.surfaceType == SurfaceType::Window || gEnableAllTraceTests);
1155     ASSERT(mParams.eglParameters.deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE ||
1156            gEnableAllTraceTests);
1157 
1158     // We already swap in TracePerfTest::drawBenchmark, no need to swap again in the harness.
1159     disableTestHarnessSwap();
1160 
1161     gCurrentTracePerfTest = this;
1162 
1163     if (gTraceTestValidation)
1164     {
1165         mStepsToRun = frameCount();
1166     }
1167 }
1168 
initializeBenchmark()1169 void TracePerfTest::initializeBenchmark()
1170 {
1171     const TraceInfo &traceInfo = mParams.traceInfo;
1172 
1173     mStartingDirectory = angle::GetCWD().value();
1174 
1175     std::stringstream traceNameStr;
1176     traceNameStr << "angle_restricted_trace_" << traceInfo.name;
1177     std::string traceName = traceNameStr.str();
1178     mTraceLibrary.reset(new TraceLibrary(traceName.c_str()));
1179 
1180     // To load the trace data path correctly we set the CWD to the executable dir.
1181     if (!IsAndroid())
1182     {
1183         std::string exeDir = angle::GetExecutableDirectory();
1184         angle::SetCWD(exeDir.c_str());
1185     }
1186 
1187     trace_angle::LoadEGL(TraceLoadProc);
1188     trace_angle::LoadGLES(TraceLoadProc);
1189 
1190     if (!mTraceLibrary->valid())
1191     {
1192         mSkipTest = true;
1193         FAIL() << "Could not load trace library.";
1194     }
1195 
1196     mStartFrame = traceInfo.frameStart;
1197     mEndFrame   = traceInfo.frameEnd;
1198     mTraceLibrary->setBinaryDataDecompressCallback(DecompressBinaryData);
1199 
1200     mTraceLibrary->setValidateSerializedStateCallback(ValidateSerializedState);
1201 
1202     char testDataDir[kMaxPath] = {};
1203     if (!FindTraceTestDataPath(traceInfo.name, testDataDir, kMaxPath))
1204     {
1205         mSkipTest = true;
1206         FAIL() << "Could not find test data folder.";
1207     }
1208 
1209     mTraceLibrary->setBinaryDataDir(testDataDir);
1210 
1211     if (gMinimizeGPUWork)
1212     {
1213         // Shrink the offscreen window to 1x1.
1214         mWindowWidth  = 1;
1215         mWindowHeight = 1;
1216     }
1217     else
1218     {
1219         mWindowWidth  = mTestParams.windowWidth;
1220         mWindowHeight = mTestParams.windowHeight;
1221     }
1222     mCurrentFrame = mStartFrame;
1223 
1224     if (IsAndroid())
1225     {
1226         // On Android, set the orientation used by the app, based on width/height
1227         getWindow()->setOrientation(mTestParams.windowWidth, mTestParams.windowHeight);
1228     }
1229 
1230     // If we're rendering offscreen we set up a default back buffer.
1231     if (mParams.surfaceType == SurfaceType::Offscreen)
1232     {
1233         if (!IsAndroid())
1234         {
1235             mWindowWidth *= 4;
1236             mWindowHeight *= 4;
1237         }
1238 
1239         glGenRenderbuffers(1, &mOffscreenDepthStencil);
1240         glBindRenderbuffer(GL_RENDERBUFFER, mOffscreenDepthStencil);
1241         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mWindowWidth, mWindowHeight);
1242         glBindRenderbuffer(GL_RENDERBUFFER, 0);
1243 
1244         glGenFramebuffers(mMaxOffscreenBufferCount, mOffscreenFramebuffers.data());
1245         glGenTextures(mMaxOffscreenBufferCount, mOffscreenTextures.data());
1246         for (int i = 0; i < mMaxOffscreenBufferCount; i++)
1247         {
1248             glBindFramebuffer(GL_FRAMEBUFFER, mOffscreenFramebuffers[i]);
1249 
1250             // Hard-code RGBA8/D24S8. This should be specified in the trace info.
1251             glBindTexture(GL_TEXTURE_2D, mOffscreenTextures[i]);
1252             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWindowWidth, mWindowHeight, 0, GL_RGBA,
1253                          GL_UNSIGNED_BYTE, nullptr);
1254 
1255             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
1256                                    mOffscreenTextures[i], 0);
1257             glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
1258                                       mOffscreenDepthStencil);
1259             glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
1260                                       mOffscreenDepthStencil);
1261             glBindTexture(GL_TEXTURE_2D, 0);
1262         }
1263     }
1264 
1265     // Potentially slow. Can load a lot of resources.
1266     mTraceLibrary->setupReplay();
1267 
1268     glFinish();
1269 
1270     ASSERT_GE(mEndFrame, mStartFrame);
1271 
1272     getWindow()->ignoreSizeEvents();
1273     getWindow()->setVisible(true);
1274 
1275     // If we're re-tracing, trigger capture start after setup. This ensures the Setup function gets
1276     // recaptured into another Setup function and not merged with the first frame.
1277     if (gRetraceMode)
1278     {
1279         getGLWindow()->swap();
1280     }
1281 }
1282 
1283 #undef TRACE_TEST_CASE
1284 
destroyBenchmark()1285 void TracePerfTest::destroyBenchmark()
1286 {
1287     if (mParams.surfaceType == SurfaceType::Offscreen)
1288     {
1289         glDeleteTextures(mMaxOffscreenBufferCount, mOffscreenTextures.data());
1290         mOffscreenTextures.fill(0);
1291 
1292         glDeleteRenderbuffers(1, &mOffscreenDepthStencil);
1293         mOffscreenDepthStencil = 0;
1294 
1295         glDeleteFramebuffers(mMaxOffscreenBufferCount, mOffscreenFramebuffers.data());
1296         mOffscreenFramebuffers.fill(0);
1297     }
1298 
1299     mTraceLibrary->finishReplay();
1300     mTraceLibrary.reset(nullptr);
1301 
1302     // In order for the next test to load, restore the working directory
1303     angle::SetCWD(mStartingDirectory.c_str());
1304 }
1305 
sampleTime()1306 void TracePerfTest::sampleTime()
1307 {
1308     if (mUseTimestampQueries)
1309     {
1310         GLint64 glTime;
1311         // glGetInteger64vEXT is exported by newer versions of the timer query extensions.
1312         // Unfortunately only the core EP is exposed by some desktop drivers (e.g. NVIDIA).
1313         if (glGetInteger64vEXT)
1314         {
1315             glGetInteger64vEXT(GL_TIMESTAMP_EXT, &glTime);
1316         }
1317         else
1318         {
1319             glGetInteger64v(GL_TIMESTAMP_EXT, &glTime);
1320         }
1321         mTimeline.push_back({glTime, angle::GetHostTimeSeconds()});
1322     }
1323 }
1324 
drawBenchmark()1325 void TracePerfTest::drawBenchmark()
1326 {
1327     constexpr uint32_t kFramesPerX  = 6;
1328     constexpr uint32_t kFramesPerY  = 4;
1329     constexpr uint32_t kFramesPerXY = kFramesPerY * kFramesPerX;
1330 
1331     const uint32_t kOffscreenOffsetX =
1332         static_cast<uint32_t>(static_cast<double>(mTestParams.windowWidth) / 3.0f);
1333     const uint32_t kOffscreenOffsetY =
1334         static_cast<uint32_t>(static_cast<double>(mTestParams.windowHeight) / 3.0f);
1335     const uint32_t kOffscreenWidth  = kOffscreenOffsetX;
1336     const uint32_t kOffscreenHeight = kOffscreenOffsetY;
1337 
1338     const uint32_t kOffscreenFrameWidth = static_cast<uint32_t>(
1339         static_cast<double>(kOffscreenWidth / static_cast<double>(kFramesPerX)));
1340     const uint32_t kOffscreenFrameHeight = static_cast<uint32_t>(
1341         static_cast<double>(kOffscreenHeight / static_cast<double>(kFramesPerY)));
1342 
1343     // Add a time sample from GL and the host.
1344     if (mCurrentFrame == mStartFrame)
1345     {
1346         sampleTime();
1347     }
1348 
1349     if (mParams.surfaceType == SurfaceType::Offscreen)
1350     {
1351         // Some driver (ARM and ANGLE) try to nop or defer the glFlush if it is called within the
1352         // renderpass to avoid breaking renderpass (performance reason). For app traces that does
1353         // not use any FBO, when we run in the offscreen mode, there is no frame boundary and
1354         // glFlush call we issued at end of frame will get skipped. To overcome this (and also
1355         // matches what onscreen double buffering behavior as well), we use two offscreen FBOs and
1356         // ping pong between them for each frame.
1357         glBindFramebuffer(GL_FRAMEBUFFER,
1358                           mOffscreenFramebuffers[mTotalFrameCount % mMaxOffscreenBufferCount]);
1359     }
1360 
1361     char frameName[32];
1362     sprintf(frameName, "Frame %u", mCurrentFrame);
1363     beginInternalTraceEvent(frameName);
1364 
1365     startGpuTimer();
1366     mTraceLibrary->replayFrame(mCurrentFrame);
1367     stopGpuTimer();
1368 
1369     if (mParams.surfaceType == SurfaceType::Offscreen)
1370     {
1371         if (gMinimizeGPUWork)
1372         {
1373             // To keep GPU work minimum, we skip the blit.
1374             glFlush();
1375             mOffscreenFrameCount++;
1376         }
1377         else
1378         {
1379             GLint currentDrawFBO, currentReadFBO;
1380             glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &currentDrawFBO);
1381             glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &currentReadFBO);
1382 
1383             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
1384             glBindFramebuffer(
1385                 GL_READ_FRAMEBUFFER,
1386                 mOffscreenFramebuffers[mOffscreenFrameCount % mMaxOffscreenBufferCount]);
1387 
1388             uint32_t frameX  = (mOffscreenFrameCount % kFramesPerXY) % kFramesPerX;
1389             uint32_t frameY  = (mOffscreenFrameCount % kFramesPerXY) / kFramesPerX;
1390             uint32_t windowX = kOffscreenOffsetX + frameX * kOffscreenFrameWidth;
1391             uint32_t windowY = kOffscreenOffsetY + frameY * kOffscreenFrameHeight;
1392 
1393             GLboolean scissorTest = GL_FALSE;
1394             glGetBooleanv(GL_SCISSOR_TEST, &scissorTest);
1395 
1396             if (scissorTest)
1397             {
1398                 glDisable(GL_SCISSOR_TEST);
1399             }
1400 
1401             glBlitFramebuffer(0, 0, mWindowWidth, mWindowHeight, windowX, windowY,
1402                               windowX + kOffscreenFrameWidth, windowY + kOffscreenFrameHeight,
1403                               GL_COLOR_BUFFER_BIT, GL_NEAREST);
1404 
1405             if (frameX == kFramesPerX - 1 && frameY == kFramesPerY - 1)
1406             {
1407                 swap();
1408                 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1409                 glClear(GL_COLOR_BUFFER_BIT);
1410                 mOffscreenFrameCount = 0;
1411             }
1412             else
1413             {
1414                 glFlush();
1415                 mOffscreenFrameCount++;
1416             }
1417 
1418             if (scissorTest)
1419             {
1420                 glEnable(GL_SCISSOR_TEST);
1421             }
1422             glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentDrawFBO);
1423             glBindFramebuffer(GL_READ_FRAMEBUFFER, currentReadFBO);
1424         }
1425 
1426         mTotalFrameCount++;
1427     }
1428     else
1429     {
1430         swap();
1431     }
1432 
1433     endInternalTraceEvent(frameName);
1434 
1435     if (mCurrentFrame == mEndFrame)
1436     {
1437         mTraceLibrary->resetReplay();
1438         mCurrentFrame = mStartFrame;
1439     }
1440     else
1441     {
1442         mCurrentFrame++;
1443     }
1444 
1445     // Process any running queries once per iteration.
1446     for (size_t queryIndex = 0; queryIndex < mRunningQueries.size();)
1447     {
1448         const QueryInfo &query = mRunningQueries[queryIndex];
1449 
1450         GLuint endResultAvailable = 0;
1451         glGetQueryObjectuivEXT(query.endTimestampQuery, GL_QUERY_RESULT_AVAILABLE,
1452                                &endResultAvailable);
1453 
1454         if (endResultAvailable == GL_TRUE)
1455         {
1456             char fboName[32];
1457             sprintf(fboName, "FBO %u", query.framebuffer);
1458 
1459             GLint64 beginTimestamp = 0;
1460             glGetQueryObjecti64vEXT(query.beginTimestampQuery, GL_QUERY_RESULT, &beginTimestamp);
1461             glDeleteQueriesEXT(1, &query.beginTimestampQuery);
1462             double beginHostTime = getHostTimeFromGLTime(beginTimestamp);
1463             beginGLTraceEvent(fboName, beginHostTime);
1464 
1465             GLint64 endTimestamp = 0;
1466             glGetQueryObjecti64vEXT(query.endTimestampQuery, GL_QUERY_RESULT, &endTimestamp);
1467             glDeleteQueriesEXT(1, &query.endTimestampQuery);
1468             double endHostTime = getHostTimeFromGLTime(endTimestamp);
1469             endGLTraceEvent(fboName, endHostTime);
1470 
1471             mRunningQueries.erase(mRunningQueries.begin() + queryIndex);
1472         }
1473         else
1474         {
1475             queryIndex++;
1476         }
1477     }
1478 }
1479 
1480 // Converts a GL timestamp into a host-side CPU time aligned with "GetHostTimeSeconds".
1481 // This check is necessary to line up sampled trace events in a consistent timeline.
1482 // Uses a linear interpolation from a series of samples. We do a blocking call to sample
1483 // both host and GL time once per swap. We then find the two closest GL timestamps and
1484 // interpolate the host times between them to compute our result. If we are past the last
1485 // GL timestamp we sample a new data point pair.
getHostTimeFromGLTime(GLint64 glTime)1486 double TracePerfTest::getHostTimeFromGLTime(GLint64 glTime)
1487 {
1488     // Find two samples to do a lerp.
1489     size_t firstSampleIndex = mTimeline.size() - 1;
1490     while (firstSampleIndex > 0)
1491     {
1492         if (mTimeline[firstSampleIndex].glTime < glTime)
1493         {
1494             break;
1495         }
1496         firstSampleIndex--;
1497     }
1498 
1499     // Add an extra sample if we're missing an ending sample.
1500     if (firstSampleIndex == mTimeline.size() - 1)
1501     {
1502         sampleTime();
1503     }
1504 
1505     const TimeSample &start = mTimeline[firstSampleIndex];
1506     const TimeSample &end   = mTimeline[firstSampleIndex + 1];
1507 
1508     // Note: we have observed in some odd cases later timestamps producing values that are
1509     // smaller than preceding timestamps. This bears further investigation.
1510 
1511     // Compute the scaling factor for the lerp.
1512     double glDelta = static_cast<double>(glTime - start.glTime);
1513     double glRange = static_cast<double>(end.glTime - start.glTime);
1514     double t       = glDelta / glRange;
1515 
1516     // Lerp(t1, t2, t)
1517     double hostRange = end.hostTime - start.hostTime;
1518     return mTimeline[firstSampleIndex].hostTime + hostRange * t;
1519 }
1520 
onEglCreateContext(EGLDisplay display, EGLConfig config, EGLContext share_context, EGLint const *attrib_list)1521 EGLContext TracePerfTest::onEglCreateContext(EGLDisplay display,
1522                                              EGLConfig config,
1523                                              EGLContext share_context,
1524                                              EGLint const *attrib_list)
1525 {
1526     GLWindowContext newContext =
1527         getGLWindow()->createContextGeneric(reinterpret_cast<GLWindowContext>(share_context));
1528     return reinterpret_cast<EGLContext>(newContext);
1529 }
1530 
onEglMakeCurrent(EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context)1531 void TracePerfTest::onEglMakeCurrent(EGLDisplay display,
1532                                      EGLSurface draw,
1533                                      EGLSurface read,
1534                                      EGLContext context)
1535 {
1536     getGLWindow()->makeCurrentGeneric(reinterpret_cast<GLWindowContext>(context));
1537 }
1538 
onEglGetCurrentContext()1539 EGLContext TracePerfTest::onEglGetCurrentContext()
1540 {
1541     return getGLWindow()->getCurrentContextGeneric();
1542 }
1543 
1544 // Triggered when the replay calls glBindFramebuffer.
onReplayFramebufferChange(GLenum target, GLuint framebuffer)1545 void TracePerfTest::onReplayFramebufferChange(GLenum target, GLuint framebuffer)
1546 {
1547     if (framebuffer == 0 && mParams.surfaceType == SurfaceType::Offscreen)
1548     {
1549         glBindFramebuffer(target,
1550                           mOffscreenFramebuffers[mTotalFrameCount % mMaxOffscreenBufferCount]);
1551     }
1552     else
1553     {
1554         glBindFramebuffer(target, framebuffer);
1555     }
1556 
1557     switch (target)
1558     {
1559         case GL_FRAMEBUFFER:
1560             mDrawFramebufferBinding = framebuffer;
1561             mReadFramebufferBinding = framebuffer;
1562             break;
1563         case GL_DRAW_FRAMEBUFFER:
1564             mDrawFramebufferBinding = framebuffer;
1565             break;
1566         case GL_READ_FRAMEBUFFER:
1567             mReadFramebufferBinding = framebuffer;
1568             return;
1569 
1570         default:
1571             UNREACHABLE();
1572             break;
1573     }
1574 
1575     if (!mUseTimestampQueries)
1576         return;
1577 
1578     // We have at most one active timestamp query at a time. This code will end the current
1579     // query and immediately start a new one.
1580     if (mCurrentQuery.beginTimestampQuery != 0)
1581     {
1582         glGenQueriesEXT(1, &mCurrentQuery.endTimestampQuery);
1583         glQueryCounterEXT(mCurrentQuery.endTimestampQuery, GL_TIMESTAMP_EXT);
1584         mRunningQueries.push_back(mCurrentQuery);
1585         mCurrentQuery = {};
1586     }
1587 
1588     ASSERT(mCurrentQuery.beginTimestampQuery == 0);
1589 
1590     glGenQueriesEXT(1, &mCurrentQuery.beginTimestampQuery);
1591     glQueryCounterEXT(mCurrentQuery.beginTimestampQuery, GL_TIMESTAMP_EXT);
1592     mCurrentQuery.framebuffer = framebuffer;
1593 }
1594 
GetDiffPath()1595 std::string GetDiffPath()
1596 {
1597 #if defined(ANGLE_PLATFORM_WINDOWS)
1598     std::array<char, MAX_PATH> filenameBuffer = {};
1599     char *filenamePtr                         = nullptr;
1600     if (SearchPathA(NULL, "diff", ".exe", MAX_PATH, filenameBuffer.data(), &filenamePtr) == 0)
1601     {
1602         return "";
1603     }
1604     return std::string(filenameBuffer.data());
1605 #else
1606     return "/usr/bin/diff";
1607 #endif  // defined(ANGLE_PLATFORM_WINDOWS)
1608 }
1609 
PrintFileDiff(const char *aFilePath, const char *bFilePath)1610 void PrintFileDiff(const char *aFilePath, const char *bFilePath)
1611 {
1612     std::string pathToDiff = GetDiffPath();
1613     if (pathToDiff.empty())
1614     {
1615         printf("Could not find diff in the path.\n");
1616         return;
1617     }
1618 
1619     std::vector<const char *> args;
1620     args.push_back(pathToDiff.c_str());
1621     args.push_back(aFilePath);
1622     args.push_back(bFilePath);
1623     args.push_back("-u3");
1624 
1625     printf("Calling");
1626     for (const char *arg : args)
1627     {
1628         printf(" %s", arg);
1629     }
1630     printf("\n");
1631 
1632     ProcessHandle proc(LaunchProcess(args, ProcessOutputCapture::StdoutOnly));
1633     if (proc && proc->finish())
1634     {
1635         printf("\n%s\n", proc->getStdout().c_str());
1636     }
1637 }
1638 
validateSerializedState(const char *expectedCapturedSerializedState, const char *fileName, uint32_t line)1639 void TracePerfTest::validateSerializedState(const char *expectedCapturedSerializedState,
1640                                             const char *fileName,
1641                                             uint32_t line)
1642 {
1643     if (!gTraceTestValidation)
1644     {
1645         return;
1646     }
1647 
1648     printf("Serialization checkpoint %s:%u...\n", fileName, line);
1649 
1650     const GLubyte *bytes                      = glGetString(GL_SERIALIZED_CONTEXT_STRING_ANGLE);
1651     const char *actualReplayedSerializedState = reinterpret_cast<const char *>(bytes);
1652     if (strcmp(expectedCapturedSerializedState, actualReplayedSerializedState) == 0)
1653     {
1654         printf("Serialization match.\n");
1655         return;
1656     }
1657 
1658     printf("Serialization mismatch!\n");
1659 
1660     char aFilePath[kMaxPath] = {};
1661     if (CreateTemporaryFile(aFilePath, kMaxPath))
1662     {
1663         printf("Saving \"expected\" capture serialization to \"%s\".\n", aFilePath);
1664         FILE *fpA = fopen(aFilePath, "wt");
1665         ASSERT(fpA);
1666         fprintf(fpA, "%s", expectedCapturedSerializedState);
1667         fclose(fpA);
1668     }
1669 
1670     char bFilePath[kMaxPath] = {};
1671     if (CreateTemporaryFile(bFilePath, kMaxPath))
1672     {
1673         printf("Saving \"actual\" replay serialization to \"%s\".\n", bFilePath);
1674         FILE *fpB = fopen(bFilePath, "wt");
1675         ASSERT(fpB);
1676         fprintf(fpB, "%s", actualReplayedSerializedState);
1677         fclose(fpB);
1678     }
1679 
1680     PrintFileDiff(aFilePath, bFilePath);
1681 }
1682 
isDefaultFramebuffer(GLenum target) const1683 bool TracePerfTest::isDefaultFramebuffer(GLenum target) const
1684 {
1685     switch (target)
1686     {
1687         case GL_FRAMEBUFFER:
1688         case GL_DRAW_FRAMEBUFFER:
1689             return (mDrawFramebufferBinding == 0);
1690 
1691         case GL_READ_FRAMEBUFFER:
1692             return (mReadFramebufferBinding == 0);
1693 
1694         default:
1695             UNREACHABLE();
1696             return false;
1697     }
1698 }
1699 
ConvertDefaultFramebufferEnum(GLenum value)1700 GLenum ConvertDefaultFramebufferEnum(GLenum value)
1701 {
1702     switch (value)
1703     {
1704         case GL_NONE:
1705             return GL_NONE;
1706         case GL_BACK:
1707         case GL_COLOR:
1708             return GL_COLOR_ATTACHMENT0;
1709         case GL_DEPTH:
1710             return GL_DEPTH_ATTACHMENT;
1711         case GL_STENCIL:
1712             return GL_STENCIL_ATTACHMENT;
1713         case GL_DEPTH_STENCIL:
1714             return GL_DEPTH_STENCIL_ATTACHMENT;
1715         default:
1716             UNREACHABLE();
1717             return GL_NONE;
1718     }
1719 }
1720 
ConvertDefaultFramebufferEnums(GLsizei numAttachments, const GLenum *attachments)1721 std::vector<GLenum> ConvertDefaultFramebufferEnums(GLsizei numAttachments,
1722                                                    const GLenum *attachments)
1723 {
1724     std::vector<GLenum> translatedAttachments;
1725     for (GLsizei attachmentIndex = 0; attachmentIndex < numAttachments; ++attachmentIndex)
1726     {
1727         GLenum converted = ConvertDefaultFramebufferEnum(attachments[attachmentIndex]);
1728         translatedAttachments.push_back(converted);
1729     }
1730     return translatedAttachments;
1731 }
1732 
1733 // Needs special handling to treat the 0 framebuffer in offscreen mode.
onReplayInvalidateFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments)1734 void TracePerfTest::onReplayInvalidateFramebuffer(GLenum target,
1735                                                   GLsizei numAttachments,
1736                                                   const GLenum *attachments)
1737 {
1738     if (mParams.surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(target))
1739     {
1740         glInvalidateFramebuffer(target, numAttachments, attachments);
1741     }
1742     else
1743     {
1744         std::vector<GLenum> translatedAttachments =
1745             ConvertDefaultFramebufferEnums(numAttachments, attachments);
1746         glInvalidateFramebuffer(target, numAttachments, translatedAttachments.data());
1747     }
1748 }
1749 
onReplayInvalidateSubFramebuffer(GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height)1750 void TracePerfTest::onReplayInvalidateSubFramebuffer(GLenum target,
1751                                                      GLsizei numAttachments,
1752                                                      const GLenum *attachments,
1753                                                      GLint x,
1754                                                      GLint y,
1755                                                      GLsizei width,
1756                                                      GLsizei height)
1757 {
1758     if (mParams.surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(target))
1759     {
1760         glInvalidateSubFramebuffer(target, numAttachments, attachments, x, y, width, height);
1761     }
1762     else
1763     {
1764         std::vector<GLenum> translatedAttachments =
1765             ConvertDefaultFramebufferEnums(numAttachments, attachments);
1766         glInvalidateSubFramebuffer(target, numAttachments, translatedAttachments.data(), x, y,
1767                                    width, height);
1768     }
1769 }
1770 
onReplayDrawBuffers(GLsizei n, const GLenum *bufs)1771 void TracePerfTest::onReplayDrawBuffers(GLsizei n, const GLenum *bufs)
1772 {
1773     if (mParams.surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(GL_DRAW_FRAMEBUFFER))
1774     {
1775         glDrawBuffers(n, bufs);
1776     }
1777     else
1778     {
1779         std::vector<GLenum> translatedBufs = ConvertDefaultFramebufferEnums(n, bufs);
1780         glDrawBuffers(n, translatedBufs.data());
1781     }
1782 }
1783 
onReplayReadBuffer(GLenum src)1784 void TracePerfTest::onReplayReadBuffer(GLenum src)
1785 {
1786     if (mParams.surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(GL_READ_FRAMEBUFFER))
1787     {
1788         glReadBuffer(src);
1789     }
1790     else
1791     {
1792         GLenum translated = ConvertDefaultFramebufferEnum(src);
1793         glReadBuffer(translated);
1794     }
1795 }
1796 
onReplayDiscardFramebufferEXT(GLenum target, GLsizei numAttachments, const GLenum *attachments)1797 void TracePerfTest::onReplayDiscardFramebufferEXT(GLenum target,
1798                                                   GLsizei numAttachments,
1799                                                   const GLenum *attachments)
1800 {
1801     if (mParams.surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(target))
1802     {
1803         glDiscardFramebufferEXT(target, numAttachments, attachments);
1804     }
1805     else
1806     {
1807         std::vector<GLenum> translatedAttachments =
1808             ConvertDefaultFramebufferEnums(numAttachments, attachments);
1809         glDiscardFramebufferEXT(target, numAttachments, translatedAttachments.data());
1810     }
1811 }
1812 
swap()1813 void TracePerfTest::swap()
1814 {
1815     // Capture a screenshot if enabled.
1816     if (gScreenShotDir != nullptr && !mScreenshotSaved &&
1817         static_cast<uint32_t>(gScreenShotFrame) == mCurrentFrame)
1818     {
1819         std::stringstream screenshotNameStr;
1820         screenshotNameStr << gScreenShotDir << GetPathSeparator() << "angle" << mBackend << "_"
1821                           << mStory;
1822 
1823         // Add a marker to the name for any screenshot that isn't start frame
1824         if (mStartFrame != static_cast<uint32_t>(gScreenShotFrame))
1825         {
1826             screenshotNameStr << "_frame" << gScreenShotFrame;
1827         }
1828 
1829         screenshotNameStr << ".png";
1830 
1831         std::string screenshotName = screenshotNameStr.str();
1832         saveScreenshot(screenshotName);
1833         mScreenshotSaved = true;
1834     }
1835 
1836     getGLWindow()->swap();
1837 }
1838 
saveScreenshot(const std::string &screenshotName)1839 void TracePerfTest::saveScreenshot(const std::string &screenshotName)
1840 {
1841     // The frame is already rendered and is waiting in the default framebuffer.
1842 
1843     // RGBA 4-byte data.
1844     uint32_t pixelCount = mTestParams.windowWidth * mTestParams.windowHeight;
1845     std::vector<uint8_t> pixelData(pixelCount * 4);
1846 
1847     // Only unbind the framebuffer on context versions where it's available.
1848     if (mParams.traceInfo.contextClientMajorVersion > 1)
1849     {
1850         glBindFramebuffer(GL_FRAMEBUFFER, 0);
1851     }
1852 
1853     glReadPixels(0, 0, mTestParams.windowWidth, mTestParams.windowHeight, GL_RGBA, GL_UNSIGNED_BYTE,
1854                  pixelData.data());
1855 
1856     // Convert to RGB and flip y.
1857     std::vector<uint8_t> rgbData(pixelCount * 3);
1858     for (EGLint y = 0; y < mTestParams.windowHeight; ++y)
1859     {
1860         for (EGLint x = 0; x < mTestParams.windowWidth; ++x)
1861         {
1862             EGLint srcPixel = x + y * mTestParams.windowWidth;
1863             EGLint dstPixel = x + (mTestParams.windowHeight - y - 1) * mTestParams.windowWidth;
1864             memcpy(&rgbData[dstPixel * 3], &pixelData[srcPixel * 4], 3);
1865         }
1866     }
1867 
1868     if (!angle::SavePNGRGB(screenshotName.c_str(), "ANGLE Screenshot", mTestParams.windowWidth,
1869                            mTestParams.windowHeight, rgbData))
1870     {
1871         FAIL() << "Error saving screenshot: " << screenshotName;
1872     }
1873     else
1874     {
1875         printf("Saved screenshot: '%s'\n", screenshotName.c_str());
1876     }
1877 }
1878 
CombineWithTraceInfo(const TracePerfParams &in, const TraceInfo &traceInfo)1879 TracePerfParams CombineWithTraceInfo(const TracePerfParams &in, const TraceInfo &traceInfo)
1880 {
1881     TracePerfParams out = in;
1882     out.traceInfo       = traceInfo;
1883     out.majorVersion    = traceInfo.contextClientMajorVersion;
1884     out.minorVersion    = traceInfo.contextClientMinorVersion;
1885     out.windowWidth     = traceInfo.drawSurfaceWidth;
1886     out.windowHeight    = traceInfo.drawSurfaceHeight;
1887     out.colorSpace      = traceInfo.drawSurfaceColorSpace;
1888     return out;
1889 }
1890 
CombineWithSurfaceType(const TracePerfParams &in, SurfaceType surfaceType)1891 TracePerfParams CombineWithSurfaceType(const TracePerfParams &in, SurfaceType surfaceType)
1892 {
1893     TracePerfParams out = in;
1894     out.surfaceType     = surfaceType;
1895 
1896     if (!IsAndroid() && surfaceType == SurfaceType::Offscreen)
1897     {
1898         out.windowWidth /= 4;
1899         out.windowHeight /= 4;
1900     }
1901 
1902     // We track GPU time only in frame-rate-limited cases.
1903     out.trackGpuTime = surfaceType == SurfaceType::WindowWithVSync;
1904 
1905     return out;
1906 }
1907 
1908 }  // anonymous namespace
1909 
1910 using namespace params;
1911 using P  = TracePerfParams;
1912 using PV = std::vector<P>;
1913 
RegisterTraceTests()1914 void RegisterTraceTests()
1915 {
1916     // To load the trace data path correctly we set the CWD to the executable dir.
1917     std::string previousCWD;
1918     if (!IsAndroid())
1919     {
1920         previousCWD        = GetCWD().value();
1921         std::string exeDir = GetExecutableDirectory();
1922         SetCWD(exeDir.c_str());
1923     }
1924 
1925     char rootTracePath[kMaxPath] = {};
1926     if (!FindRootTraceTestDataPath(rootTracePath, kMaxPath))
1927     {
1928         ERR() << "Unable to find trace folder.";
1929         return;
1930     }
1931 
1932     // Load JSON data.
1933     std::stringstream tracesJsonStream;
1934     tracesJsonStream << rootTracePath << GetPathSeparator() << "restricted_traces.json";
1935     std::string tracesJsonPath = tracesJsonStream.str();
1936 
1937     std::vector<std::string> traces;
1938     if (!LoadTraceNamesFromJSON(tracesJsonPath, &traces))
1939     {
1940         ERR() << "Unable to load traces from JSON file: " << tracesJsonPath;
1941         return;
1942     }
1943 
1944     std::vector<TraceInfo> traceInfos;
1945     for (const std::string &trace : traces)
1946     {
1947         std::stringstream traceJsonStream;
1948         traceJsonStream << rootTracePath << GetPathSeparator() << trace << GetPathSeparator()
1949                         << trace << ".json";
1950         std::string traceJsonPath = traceJsonStream.str();
1951 
1952         TraceInfo traceInfo = {};
1953         if (!LoadTraceInfoFromJSON(trace, traceJsonPath, &traceInfo))
1954         {
1955             static_assert(sizeof(TraceInfo) == sizeof(trace_angle::TraceInfo), "Size mismatch");
1956             trace_angle::TraceInfo autogenFormatInfo = trace_angle::GetTraceInfo(trace.c_str());
1957             memcpy(&traceInfo, &autogenFormatInfo, sizeof(TraceInfo));
1958         }
1959 
1960         traceInfos.push_back(traceInfo);
1961     }
1962 
1963     std::vector<SurfaceType> surfaceTypes = {SurfaceType::Window};
1964     if (gEnableAllTraceTests)
1965     {
1966         surfaceTypes.push_back(SurfaceType::Offscreen);
1967         surfaceTypes.push_back(SurfaceType::WindowWithVSync);
1968     }
1969 
1970     std::vector<ModifierFunc<P>> renderers = {Vulkan<P>, Native<P>};
1971     if (gEnableAllTraceTests)
1972     {
1973         if (!IsAndroid())
1974         {
1975             renderers.push_back(VulkanMockICD<P>);
1976         }
1977         renderers.push_back(VulkanSwiftShader<P>);
1978     }
1979 
1980     PV withTraceInfo   = CombineWithValues({P()}, traceInfos, CombineWithTraceInfo);
1981     PV withSurfaceType = CombineWithValues(withTraceInfo, surfaceTypes, CombineWithSurfaceType);
1982     PV withRenderer    = CombineWithFuncs(withSurfaceType, renderers);
1983 
1984     for (const TracePerfParams &params : withRenderer)
1985     {
1986         if (!IsPlatformAvailable(params))
1987         {
1988             continue;
1989         }
1990 
1991         // Force on features if we're validating serialization.
1992         TracePerfParams overrideParams = params;
1993         if (gTraceTestValidation)
1994         {
1995             // Enable limits when validating traces because we usually turn off capture.
1996             overrideParams.eglParameters.captureLimits = EGL_TRUE;
1997 
1998             // This feature should also be enabled in capture to mirror the replay.
1999             overrideParams.eglParameters.forceInitShaderVariables = EGL_TRUE;
2000         }
2001 
2002         auto factory          = [overrideParams]() { return new TracePerfTest(overrideParams); };
2003         std::string paramName = testing::PrintToString(params);
2004         std::stringstream testNameStr;
2005         testNameStr << "Run/" << paramName;
2006         std::string testName = testNameStr.str();
2007         testing::RegisterTest("TracePerfTest", testName.c_str(), nullptr, paramName.c_str(),
2008                               __FILE__, __LINE__, factory);
2009     }
2010 
2011     if (!previousCWD.empty())
2012     {
2013         SetCWD(previousCWD.c_str());
2014     }
2015 }
2016