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 ¶ms)71 std::ostream &operator<<(std::ostream &os, const TracePerfParams ¶ms)
72 {
73 os << params.backendAndStory().substr(1);
74 return os;
75 }
76
77 class TracePerfTest : public ANGLERenderTest
78 {
79 public:
80 TracePerfTest(const TracePerfParams ¶ms);
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 ¶ms)666 TracePerfTest::TracePerfTest(const TracePerfParams ¶ms)
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, ¤tDrawFBO);
1381 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, ¤tReadFBO);
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 ¶ms : 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