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