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/system_utils.h"
13 #include "tests/perf_tests/ANGLEPerfTest.h"
14 #include "tests/perf_tests/ANGLEPerfTestArgs.h"
15 #include "tests/perf_tests/DrawCallPerfParams.h"
16 #include "util/egl_loader_autogen.h"
17 #include "util/frame_capture_test_utils.h"
18 #include "util/png_utils.h"
19
20 #include "restricted_traces/restricted_traces_autogen.h"
21
22 #include <cassert>
23 #include <functional>
24 #include <sstream>
25
26 using namespace angle;
27 using namespace egl_platform;
28
29 namespace
30 {
31 struct TracePerfParams final : public RenderTestParams
32 {
33 // Common default options
TracePerfParams__anon74a605680111::TracePerfParams34 TracePerfParams()
35 {
36 majorVersion = 3;
37 minorVersion = 1;
38
39 // Display the frame after every drawBenchmark invocation
40 iterationsPerStep = 1;
41 }
42
story__anon74a605680111::TracePerfParams43 std::string story() const override
44 {
45 std::stringstream strstr;
46 strstr << RenderTestParams::story() << "_" << GetTraceInfo(testID).name;
47 return strstr.str();
48 }
49
50 RestrictedTraceID testID;
51 };
52
operator <<(std::ostream & os,const TracePerfParams & params)53 std::ostream &operator<<(std::ostream &os, const TracePerfParams ¶ms)
54 {
55 os << params.backendAndStory().substr(1);
56 return os;
57 }
58
59 class TracePerfTest : public ANGLERenderTest, public ::testing::WithParamInterface<TracePerfParams>
60 {
61 public:
62 TracePerfTest();
63
64 void initializeBenchmark() override;
65 void destroyBenchmark() override;
66 void drawBenchmark() override;
67
68 void onReplayFramebufferChange(GLenum target, GLuint framebuffer);
69 void onReplayInvalidateFramebuffer(GLenum target,
70 GLsizei numAttachments,
71 const GLenum *attachments);
72 void onReplayInvalidateSubFramebuffer(GLenum target,
73 GLsizei numAttachments,
74 const GLenum *attachments,
75 GLint x,
76 GLint y,
77 GLsizei width,
78 GLsizei height);
79 void onReplayDrawBuffers(GLsizei n, const GLenum *bufs);
80 void onReplayReadBuffer(GLenum src);
81 void onReplayDiscardFramebufferEXT(GLenum target,
82 GLsizei numAttachments,
83 const GLenum *attachments);
84
85 bool isDefaultFramebuffer(GLenum target) const;
86
87 uint32_t mStartFrame;
88 uint32_t mEndFrame;
89
90 double getHostTimeFromGLTime(GLint64 glTime);
91
92 private:
93 struct QueryInfo
94 {
95 GLuint beginTimestampQuery;
96 GLuint endTimestampQuery;
97 GLuint framebuffer;
98 };
99
100 struct TimeSample
101 {
102 GLint64 glTime;
103 double hostTime;
104 };
105
106 void sampleTime();
107 void saveScreenshot(const std::string &screenshotName) override;
108
109 // For tracking RenderPass/FBO change timing.
110 QueryInfo mCurrentQuery = {};
111 std::vector<QueryInfo> mRunningQueries;
112 std::vector<TimeSample> mTimeline;
113
114 std::string mStartingDirectory;
115 bool mUseTimestampQueries = false;
116 GLuint mOffscreenFramebuffer = 0;
117 GLuint mOffscreenTexture = 0;
118 GLuint mOffscreenDepthStencil = 0;
119 int mWindowWidth = 0;
120 int mWindowHeight = 0;
121 GLuint mDrawFramebufferBinding = 0;
122 GLuint mReadFramebufferBinding = 0;
123 };
124
125 class TracePerfTest;
126 TracePerfTest *gCurrentTracePerfTest = nullptr;
127
128 // Don't forget to include KHRONOS_APIENTRY in override methods. Neccessary on Win/x86.
BindFramebufferProc(GLenum target,GLuint framebuffer)129 void KHRONOS_APIENTRY BindFramebufferProc(GLenum target, GLuint framebuffer)
130 {
131 gCurrentTracePerfTest->onReplayFramebufferChange(target, framebuffer);
132 }
133
InvalidateFramebufferProc(GLenum target,GLsizei numAttachments,const GLenum * attachments)134 void KHRONOS_APIENTRY InvalidateFramebufferProc(GLenum target,
135 GLsizei numAttachments,
136 const GLenum *attachments)
137 {
138 gCurrentTracePerfTest->onReplayInvalidateFramebuffer(target, numAttachments, attachments);
139 }
140
InvalidateSubFramebufferProc(GLenum target,GLsizei numAttachments,const GLenum * attachments,GLint x,GLint y,GLsizei width,GLsizei height)141 void KHRONOS_APIENTRY InvalidateSubFramebufferProc(GLenum target,
142 GLsizei numAttachments,
143 const GLenum *attachments,
144 GLint x,
145 GLint y,
146 GLsizei width,
147 GLsizei height)
148 {
149 gCurrentTracePerfTest->onReplayInvalidateSubFramebuffer(target, numAttachments, attachments, x,
150 y, width, height);
151 }
152
DrawBuffersProc(GLsizei n,const GLenum * bufs)153 void KHRONOS_APIENTRY DrawBuffersProc(GLsizei n, const GLenum *bufs)
154 {
155 gCurrentTracePerfTest->onReplayDrawBuffers(n, bufs);
156 }
157
ReadBufferProc(GLenum src)158 void KHRONOS_APIENTRY ReadBufferProc(GLenum src)
159 {
160 gCurrentTracePerfTest->onReplayReadBuffer(src);
161 }
162
DiscardFramebufferEXTProc(GLenum target,GLsizei numAttachments,const GLenum * attachments)163 void KHRONOS_APIENTRY DiscardFramebufferEXTProc(GLenum target,
164 GLsizei numAttachments,
165 const GLenum *attachments)
166 {
167 gCurrentTracePerfTest->onReplayDiscardFramebufferEXT(target, numAttachments, attachments);
168 }
169
TraceLoadProc(const char * procName)170 angle::GenericProc KHRONOS_APIENTRY TraceLoadProc(const char *procName)
171 {
172 if (strcmp(procName, "glBindFramebuffer") == 0)
173 {
174 return reinterpret_cast<angle::GenericProc>(BindFramebufferProc);
175 }
176 if (strcmp(procName, "glInvalidateFramebuffer") == 0)
177 {
178 return reinterpret_cast<angle::GenericProc>(InvalidateFramebufferProc);
179 }
180 if (strcmp(procName, "glInvalidateSubFramebuffer") == 0)
181 {
182 return reinterpret_cast<angle::GenericProc>(InvalidateSubFramebufferProc);
183 }
184 if (strcmp(procName, "glDrawBuffers") == 0)
185 {
186 return reinterpret_cast<angle::GenericProc>(DrawBuffersProc);
187 }
188 if (strcmp(procName, "glReadBuffer") == 0)
189 {
190 return reinterpret_cast<angle::GenericProc>(ReadBufferProc);
191 }
192 if (strcmp(procName, "glDiscardFramebufferEXT") == 0)
193 {
194 return reinterpret_cast<angle::GenericProc>(DiscardFramebufferEXTProc);
195 }
196 return gCurrentTracePerfTest->getGLWindow()->getProcAddress(procName);
197 }
198
TracePerfTest()199 TracePerfTest::TracePerfTest()
200 : ANGLERenderTest("TracePerf", GetParam(), "ms"), mStartFrame(0), mEndFrame(0)
201 {
202 const TracePerfParams ¶m = GetParam();
203
204 // TODO: http://anglebug.com/4533 This fails after the upgrade to the 26.20.100.7870 driver.
205 if (IsWindows() && IsIntel() && param.getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE &&
206 param.testID == RestrictedTraceID::manhattan_10)
207 {
208 mSkipTest = true;
209 }
210
211 // TODO: http://anglebug.com/4731 Fails on older Intel drivers. Passes in newer.
212 if (IsWindows() && IsIntel() && param.driver != GLESDriverType::AngleEGL &&
213 param.testID == RestrictedTraceID::angry_birds_2_1500)
214 {
215 mSkipTest = true;
216 }
217
218 if (param.surfaceType != SurfaceType::Window && !gEnableAllTraceTests)
219 {
220 printf("Test skipped. Use --enable-all-trace-tests to run.\n");
221 mSkipTest = true;
222 }
223
224 if (param.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE &&
225 !gEnableAllTraceTests)
226 {
227 printf("Test skipped. Use --enable-all-trace-tests to run.\n");
228 mSkipTest = true;
229 }
230
231 if (param.testID == RestrictedTraceID::cod_mobile)
232 {
233 // TODO: http://anglebug.com/4967 Vulkan: GL_EXT_color_buffer_float not supported on Pixel 2
234 // The COD:Mobile trace uses a framebuffer attachment with:
235 // format = GL_RGB
236 // type = GL_UNSIGNED_INT_10F_11F_11F_REV
237 // That combination is only renderable if GL_EXT_color_buffer_float is supported.
238 // It happens to not be supported on Pixel 2's Vulkan driver.
239 addExtensionPrerequisite("GL_EXT_color_buffer_float");
240
241 // TODO: http://anglebug.com/4731 This extension is missing on older Intel drivers.
242 addExtensionPrerequisite("GL_OES_EGL_image_external");
243 }
244
245 if (param.testID == RestrictedTraceID::brawl_stars)
246 {
247 addExtensionPrerequisite("GL_EXT_shadow_samplers");
248 }
249
250 if (param.testID == RestrictedTraceID::free_fire)
251 {
252 addExtensionPrerequisite("GL_OES_EGL_image_external");
253 }
254
255 if (param.testID == RestrictedTraceID::marvel_contest_of_champions)
256 {
257 addExtensionPrerequisite("GL_EXT_color_buffer_half_float");
258 }
259
260 if (param.testID == RestrictedTraceID::world_of_tanks_blitz)
261 {
262 addExtensionPrerequisite("GL_EXT_disjoint_timer_query");
263 }
264
265 if (param.testID == RestrictedTraceID::dragon_ball_legends)
266 {
267 addExtensionPrerequisite("GL_KHR_texture_compression_astc_ldr");
268 }
269
270 // We already swap in TracePerfTest::drawBenchmark, no need to swap again in the harness.
271 disableTestHarnessSwap();
272
273 gCurrentTracePerfTest = this;
274 }
275
initializeBenchmark()276 void TracePerfTest::initializeBenchmark()
277 {
278 const auto ¶ms = GetParam();
279
280 mStartingDirectory = angle::GetCWD().value();
281
282 // To load the trace data path correctly we set the CWD to the executable dir.
283 if (!IsAndroid())
284 {
285 std::string exeDir = angle::GetExecutableDirectory();
286 angle::SetCWD(exeDir.c_str());
287 }
288
289 trace_angle::LoadGLES(TraceLoadProc);
290
291 const TraceInfo &traceInfo = GetTraceInfo(params.testID);
292 mStartFrame = traceInfo.startFrame;
293 mEndFrame = traceInfo.endFrame;
294 SetBinaryDataDecompressCallback(params.testID, DecompressBinaryData);
295
296 setStepsPerRunLoopStep(mEndFrame - mStartFrame + 1);
297
298 std::stringstream testDataDirStr;
299 testDataDirStr << ANGLE_TRACE_DATA_DIR << "/" << traceInfo.name;
300 std::string testDataDir = testDataDirStr.str();
301 SetBinaryDataDir(params.testID, testDataDir.c_str());
302
303 mWindowWidth = mTestParams.windowWidth;
304 mWindowHeight = mTestParams.windowHeight;
305
306 if (IsAndroid())
307 {
308 // On Android, set the orientation used by the app, based on width/height
309 getWindow()->setOrientation(mTestParams.windowWidth, mTestParams.windowHeight);
310 }
311
312 // If we're rendering offscreen we set up a default backbuffer.
313 if (params.surfaceType == SurfaceType::Offscreen)
314 {
315 if (!IsAndroid())
316 {
317 mWindowWidth *= 4;
318 mWindowHeight *= 4;
319 }
320
321 glGenFramebuffers(1, &mOffscreenFramebuffer);
322 glBindFramebuffer(GL_FRAMEBUFFER, mOffscreenFramebuffer);
323
324 // Hard-code RGBA8/D24S8. This should be specified in the trace info.
325 glGenTextures(1, &mOffscreenTexture);
326 glBindTexture(GL_TEXTURE_2D, mOffscreenTexture);
327 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWindowWidth, mWindowHeight, 0, GL_RGBA,
328 GL_UNSIGNED_BYTE, nullptr);
329
330 glGenRenderbuffers(1, &mOffscreenDepthStencil);
331 glBindRenderbuffer(GL_RENDERBUFFER, mOffscreenDepthStencil);
332 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mWindowWidth, mWindowHeight);
333
334 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
335 mOffscreenTexture, 0);
336 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
337 mOffscreenDepthStencil);
338 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
339 mOffscreenDepthStencil);
340 glBindTexture(GL_TEXTURE_2D, 0);
341 glBindRenderbuffer(GL_RENDERBUFFER, 0);
342 }
343
344 // Potentially slow. Can load a lot of resources.
345 SetupReplay(params.testID);
346
347 glFinish();
348
349 ASSERT_TRUE(mEndFrame > mStartFrame);
350
351 getWindow()->ignoreSizeEvents();
352 getWindow()->setVisible(true);
353
354 // If we're re-tracing, trigger capture start after setup. This ensures the Setup function gets
355 // recaptured into another Setup function and not merged with the first frame.
356 if (angle::gStartTraceAfterSetup)
357 {
358 angle::SetEnvironmentVar("ANGLE_CAPTURE_TRIGGER", "0");
359 getGLWindow()->swap();
360 }
361 }
362
363 #undef TRACE_TEST_CASE
364
destroyBenchmark()365 void TracePerfTest::destroyBenchmark()
366 {
367 if (mOffscreenTexture != 0)
368 {
369 glDeleteTextures(1, &mOffscreenTexture);
370 mOffscreenTexture = 0;
371 }
372
373 if (mOffscreenDepthStencil != 0)
374 {
375 glDeleteRenderbuffers(1, &mOffscreenDepthStencil);
376 mOffscreenDepthStencil = 0;
377 }
378
379 if (mOffscreenFramebuffer != 0)
380 {
381 glDeleteFramebuffers(1, &mOffscreenFramebuffer);
382 mOffscreenFramebuffer = 0;
383 }
384
385 // In order for the next test to load, restore the working directory
386 angle::SetCWD(mStartingDirectory.c_str());
387 }
388
sampleTime()389 void TracePerfTest::sampleTime()
390 {
391 if (mUseTimestampQueries)
392 {
393 GLint64 glTime;
394 // glGetInteger64vEXT is exported by newer versions of the timer query extensions.
395 // Unfortunately only the core EP is exposed by some desktop drivers (e.g. NVIDIA).
396 if (glGetInteger64vEXT)
397 {
398 glGetInteger64vEXT(GL_TIMESTAMP_EXT, &glTime);
399 }
400 else
401 {
402 glGetInteger64v(GL_TIMESTAMP_EXT, &glTime);
403 }
404 mTimeline.push_back({glTime, angle::GetHostTimeSeconds()});
405 }
406 }
407
drawBenchmark()408 void TracePerfTest::drawBenchmark()
409 {
410 constexpr uint32_t kFramesPerX = 6;
411 constexpr uint32_t kFramesPerY = 4;
412 constexpr uint32_t kFramesPerXY = kFramesPerY * kFramesPerX;
413
414 const uint32_t kOffscreenOffsetX =
415 static_cast<uint32_t>(static_cast<double>(mTestParams.windowWidth) / 3.0f);
416 const uint32_t kOffscreenOffsetY =
417 static_cast<uint32_t>(static_cast<double>(mTestParams.windowHeight) / 3.0f);
418 const uint32_t kOffscreenWidth = kOffscreenOffsetX;
419 const uint32_t kOffscreenHeight = kOffscreenOffsetY;
420
421 const uint32_t kOffscreenFrameWidth = static_cast<uint32_t>(
422 static_cast<double>(kOffscreenWidth / static_cast<double>(kFramesPerX)));
423 const uint32_t kOffscreenFrameHeight = static_cast<uint32_t>(
424 static_cast<double>(kOffscreenHeight / static_cast<double>(kFramesPerY)));
425
426 const TracePerfParams ¶ms = GetParam();
427
428 // Add a time sample from GL and the host.
429 sampleTime();
430
431 uint32_t endFrame = mEndFrame;
432 if (gMaxStepsPerformed > 0)
433 {
434 endFrame =
435 std::min(endFrame, gMaxStepsPerformed - mTotalNumStepsPerformed - 1 + mStartFrame);
436 mStepsPerRunLoopStep = endFrame - mStartFrame + 1;
437 }
438
439 for (uint32_t frame = mStartFrame; frame <= mEndFrame; ++frame)
440 {
441 char frameName[32];
442 sprintf(frameName, "Frame %u", frame);
443 beginInternalTraceEvent(frameName);
444
445 startGpuTimer();
446 ReplayFrame(params.testID, frame);
447 stopGpuTimer();
448
449 if (params.surfaceType != SurfaceType::Offscreen)
450 {
451 getGLWindow()->swap();
452 }
453 else
454 {
455 GLint currentDrawFBO, currentReadFBO;
456 glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤tDrawFBO);
457 glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, ¤tReadFBO);
458
459 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
460 glBindFramebuffer(GL_READ_FRAMEBUFFER, mOffscreenFramebuffer);
461
462 uint32_t frames = getNumStepsPerformed() + (frame - mStartFrame);
463 uint32_t frameX = (frames % kFramesPerXY) % kFramesPerX;
464 uint32_t frameY = (frames % kFramesPerXY) / kFramesPerX;
465 uint32_t windowX = kOffscreenOffsetX + frameX * kOffscreenFrameWidth;
466 uint32_t windowY = kOffscreenOffsetY + frameY * kOffscreenFrameHeight;
467
468 if (angle::gVerboseLogging)
469 {
470 printf("Frame %d: x %d y %d (screen x %d, screen y %d)\n", frames, frameX, frameY,
471 windowX, windowY);
472 }
473
474 GLboolean scissorTest = GL_FALSE;
475 glGetBooleanv(GL_SCISSOR_TEST, &scissorTest);
476
477 if (scissorTest)
478 {
479 glDisable(GL_SCISSOR_TEST);
480 }
481
482 glBlitFramebuffer(0, 0, mWindowWidth, mWindowHeight, windowX, windowY,
483 windowX + kOffscreenFrameWidth, windowY + kOffscreenFrameHeight,
484 GL_COLOR_BUFFER_BIT, GL_NEAREST);
485
486 if (frameX == kFramesPerX - 1 && frameY == kFramesPerY - 1)
487 {
488 getGLWindow()->swap();
489 glBindFramebuffer(GL_FRAMEBUFFER, 0);
490 glClear(GL_COLOR_BUFFER_BIT);
491 }
492
493 if (scissorTest)
494 {
495 glEnable(GL_SCISSOR_TEST);
496 }
497 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, currentDrawFBO);
498 glBindFramebuffer(GL_READ_FRAMEBUFFER, currentReadFBO);
499 }
500
501 endInternalTraceEvent(frameName);
502
503 // Check for abnormal exit.
504 if (!mRunning)
505 {
506 return;
507 }
508 }
509
510 ResetReplay(params.testID);
511
512 // Process any running queries once per iteration.
513 for (size_t queryIndex = 0; queryIndex < mRunningQueries.size();)
514 {
515 const QueryInfo &query = mRunningQueries[queryIndex];
516
517 GLuint endResultAvailable = 0;
518 glGetQueryObjectuivEXT(query.endTimestampQuery, GL_QUERY_RESULT_AVAILABLE,
519 &endResultAvailable);
520
521 if (endResultAvailable == GL_TRUE)
522 {
523 char fboName[32];
524 sprintf(fboName, "FBO %u", query.framebuffer);
525
526 GLint64 beginTimestamp = 0;
527 glGetQueryObjecti64vEXT(query.beginTimestampQuery, GL_QUERY_RESULT, &beginTimestamp);
528 glDeleteQueriesEXT(1, &query.beginTimestampQuery);
529 double beginHostTime = getHostTimeFromGLTime(beginTimestamp);
530 beginGLTraceEvent(fboName, beginHostTime);
531
532 GLint64 endTimestamp = 0;
533 glGetQueryObjecti64vEXT(query.endTimestampQuery, GL_QUERY_RESULT, &endTimestamp);
534 glDeleteQueriesEXT(1, &query.endTimestampQuery);
535 double endHostTime = getHostTimeFromGLTime(endTimestamp);
536 endGLTraceEvent(fboName, endHostTime);
537
538 mRunningQueries.erase(mRunningQueries.begin() + queryIndex);
539 }
540 else
541 {
542 queryIndex++;
543 }
544 }
545 }
546
547 // Converts a GL timestamp into a host-side CPU time aligned with "GetHostTimeSeconds".
548 // This check is necessary to line up sampled trace events in a consistent timeline.
549 // Uses a linear interpolation from a series of samples. We do a blocking call to sample
550 // both host and GL time once per swap. We then find the two closest GL timestamps and
551 // interpolate the host times between them to compute our result. If we are past the last
552 // GL timestamp we sample a new data point pair.
getHostTimeFromGLTime(GLint64 glTime)553 double TracePerfTest::getHostTimeFromGLTime(GLint64 glTime)
554 {
555 // Find two samples to do a lerp.
556 size_t firstSampleIndex = mTimeline.size() - 1;
557 while (firstSampleIndex > 0)
558 {
559 if (mTimeline[firstSampleIndex].glTime < glTime)
560 {
561 break;
562 }
563 firstSampleIndex--;
564 }
565
566 // Add an extra sample if we're missing an ending sample.
567 if (firstSampleIndex == mTimeline.size() - 1)
568 {
569 sampleTime();
570 }
571
572 const TimeSample &start = mTimeline[firstSampleIndex];
573 const TimeSample &end = mTimeline[firstSampleIndex + 1];
574
575 // Note: we have observed in some odd cases later timestamps producing values that are
576 // smaller than preceding timestamps. This bears further investigation.
577
578 // Compute the scaling factor for the lerp.
579 double glDelta = static_cast<double>(glTime - start.glTime);
580 double glRange = static_cast<double>(end.glTime - start.glTime);
581 double t = glDelta / glRange;
582
583 // Lerp(t1, t2, t)
584 double hostRange = end.hostTime - start.hostTime;
585 return mTimeline[firstSampleIndex].hostTime + hostRange * t;
586 }
587
588 // Triggered when the replay calls glBindFramebuffer.
onReplayFramebufferChange(GLenum target,GLuint framebuffer)589 void TracePerfTest::onReplayFramebufferChange(GLenum target, GLuint framebuffer)
590 {
591 if (framebuffer == 0 && GetParam().surfaceType == SurfaceType::Offscreen)
592 {
593 glBindFramebuffer(target, mOffscreenFramebuffer);
594 }
595 else
596 {
597 glBindFramebuffer(target, framebuffer);
598 }
599
600 switch (target)
601 {
602 case GL_FRAMEBUFFER:
603 mDrawFramebufferBinding = framebuffer;
604 mReadFramebufferBinding = framebuffer;
605 break;
606 case GL_DRAW_FRAMEBUFFER:
607 mDrawFramebufferBinding = framebuffer;
608 break;
609 case GL_READ_FRAMEBUFFER:
610 mReadFramebufferBinding = framebuffer;
611 return;
612
613 default:
614 UNREACHABLE();
615 break;
616 }
617
618 if (!mUseTimestampQueries)
619 return;
620
621 // We have at most one active timestamp query at a time. This code will end the current
622 // query and immediately start a new one.
623 if (mCurrentQuery.beginTimestampQuery != 0)
624 {
625 glGenQueriesEXT(1, &mCurrentQuery.endTimestampQuery);
626 glQueryCounterEXT(mCurrentQuery.endTimestampQuery, GL_TIMESTAMP_EXT);
627 mRunningQueries.push_back(mCurrentQuery);
628 mCurrentQuery = {};
629 }
630
631 ASSERT(mCurrentQuery.beginTimestampQuery == 0);
632
633 glGenQueriesEXT(1, &mCurrentQuery.beginTimestampQuery);
634 glQueryCounterEXT(mCurrentQuery.beginTimestampQuery, GL_TIMESTAMP_EXT);
635 mCurrentQuery.framebuffer = framebuffer;
636 }
637
isDefaultFramebuffer(GLenum target) const638 bool TracePerfTest::isDefaultFramebuffer(GLenum target) const
639 {
640 switch (target)
641 {
642 case GL_FRAMEBUFFER:
643 case GL_DRAW_FRAMEBUFFER:
644 return (mDrawFramebufferBinding == 0);
645
646 case GL_READ_FRAMEBUFFER:
647 return (mReadFramebufferBinding == 0);
648
649 default:
650 UNREACHABLE();
651 return false;
652 }
653 }
654
ConvertDefaultFramebufferEnum(GLenum value)655 GLenum ConvertDefaultFramebufferEnum(GLenum value)
656 {
657 switch (value)
658 {
659 case GL_NONE:
660 return GL_NONE;
661 case GL_BACK:
662 case GL_COLOR:
663 return GL_COLOR_ATTACHMENT0;
664 case GL_DEPTH:
665 return GL_DEPTH_ATTACHMENT;
666 case GL_STENCIL:
667 return GL_STENCIL_ATTACHMENT;
668 case GL_DEPTH_STENCIL:
669 return GL_DEPTH_STENCIL_ATTACHMENT;
670 default:
671 UNREACHABLE();
672 return GL_NONE;
673 }
674 }
675
ConvertDefaultFramebufferEnums(GLsizei numAttachments,const GLenum * attachments)676 std::vector<GLenum> ConvertDefaultFramebufferEnums(GLsizei numAttachments,
677 const GLenum *attachments)
678 {
679 std::vector<GLenum> translatedAttachments;
680 for (GLsizei attachmentIndex = 0; attachmentIndex < numAttachments; ++attachmentIndex)
681 {
682 GLenum converted = ConvertDefaultFramebufferEnum(attachments[attachmentIndex]);
683 translatedAttachments.push_back(converted);
684 }
685 return translatedAttachments;
686 }
687
688 // Needs special handling to treat the 0 framebuffer in offscreen mode.
onReplayInvalidateFramebuffer(GLenum target,GLsizei numAttachments,const GLenum * attachments)689 void TracePerfTest::onReplayInvalidateFramebuffer(GLenum target,
690 GLsizei numAttachments,
691 const GLenum *attachments)
692 {
693 if (GetParam().surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(target))
694 {
695 glInvalidateFramebuffer(target, numAttachments, attachments);
696 }
697 else
698 {
699 std::vector<GLenum> translatedAttachments =
700 ConvertDefaultFramebufferEnums(numAttachments, attachments);
701 glInvalidateFramebuffer(target, numAttachments, translatedAttachments.data());
702 }
703 }
704
onReplayInvalidateSubFramebuffer(GLenum target,GLsizei numAttachments,const GLenum * attachments,GLint x,GLint y,GLsizei width,GLsizei height)705 void TracePerfTest::onReplayInvalidateSubFramebuffer(GLenum target,
706 GLsizei numAttachments,
707 const GLenum *attachments,
708 GLint x,
709 GLint y,
710 GLsizei width,
711 GLsizei height)
712 {
713 if (GetParam().surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(target))
714 {
715 glInvalidateSubFramebuffer(target, numAttachments, attachments, x, y, width, height);
716 }
717 else
718 {
719 std::vector<GLenum> translatedAttachments =
720 ConvertDefaultFramebufferEnums(numAttachments, attachments);
721 glInvalidateSubFramebuffer(target, numAttachments, translatedAttachments.data(), x, y,
722 width, height);
723 }
724 }
725
onReplayDrawBuffers(GLsizei n,const GLenum * bufs)726 void TracePerfTest::onReplayDrawBuffers(GLsizei n, const GLenum *bufs)
727 {
728 if (GetParam().surfaceType != SurfaceType::Offscreen ||
729 !isDefaultFramebuffer(GL_DRAW_FRAMEBUFFER))
730 {
731 glDrawBuffers(n, bufs);
732 }
733 else
734 {
735 std::vector<GLenum> translatedBufs = ConvertDefaultFramebufferEnums(n, bufs);
736 glDrawBuffers(n, translatedBufs.data());
737 }
738 }
739
onReplayReadBuffer(GLenum src)740 void TracePerfTest::onReplayReadBuffer(GLenum src)
741 {
742 if (GetParam().surfaceType != SurfaceType::Offscreen ||
743 !isDefaultFramebuffer(GL_READ_FRAMEBUFFER))
744 {
745 glReadBuffer(src);
746 }
747 else
748 {
749 GLenum translated = ConvertDefaultFramebufferEnum(src);
750 glReadBuffer(translated);
751 }
752 }
753
onReplayDiscardFramebufferEXT(GLenum target,GLsizei numAttachments,const GLenum * attachments)754 void TracePerfTest::onReplayDiscardFramebufferEXT(GLenum target,
755 GLsizei numAttachments,
756 const GLenum *attachments)
757 {
758 if (GetParam().surfaceType != SurfaceType::Offscreen || !isDefaultFramebuffer(target))
759 {
760 glDiscardFramebufferEXT(target, numAttachments, attachments);
761 }
762 else
763 {
764 std::vector<GLenum> translatedAttachments =
765 ConvertDefaultFramebufferEnums(numAttachments, attachments);
766 glDiscardFramebufferEXT(target, numAttachments, translatedAttachments.data());
767 }
768 }
769
saveScreenshot(const std::string & screenshotName)770 void TracePerfTest::saveScreenshot(const std::string &screenshotName)
771 {
772 // Render a single frame.
773 RestrictedTraceID testID = GetParam().testID;
774 const TraceInfo &traceInfo = GetTraceInfo(testID);
775 ReplayFrame(testID, traceInfo.startFrame);
776
777 // RGBA 4-byte data.
778 uint32_t pixelCount = mTestParams.windowWidth * mTestParams.windowHeight;
779 std::vector<uint8_t> pixelData(pixelCount * 4);
780
781 glBindFramebuffer(GL_FRAMEBUFFER, 0);
782 glReadPixels(0, 0, mTestParams.windowWidth, mTestParams.windowHeight, GL_RGBA, GL_UNSIGNED_BYTE,
783 pixelData.data());
784
785 // Convert to RGB and flip y.
786 std::vector<uint8_t> rgbData(pixelCount * 3);
787 for (EGLint y = 0; y < mTestParams.windowHeight; ++y)
788 {
789 for (EGLint x = 0; x < mTestParams.windowWidth; ++x)
790 {
791 EGLint srcPixel = x + y * mTestParams.windowWidth;
792 EGLint dstPixel = x + (mTestParams.windowHeight - y - 1) * mTestParams.windowWidth;
793 memcpy(&rgbData[dstPixel * 3], &pixelData[srcPixel * 4], 3);
794 }
795 }
796
797 if (!angle::SavePNGRGB(screenshotName.c_str(), "ANGLE Screenshot", mTestParams.windowWidth,
798 mTestParams.windowHeight, rgbData))
799 {
800 FAIL() << "Error saving screenshot: " << screenshotName;
801 }
802 else
803 {
804 printf("Saved screenshot: '%s'\n", screenshotName.c_str());
805 }
806
807 // Finish the frame loop.
808 for (uint32_t nextFrame = traceInfo.startFrame + 1; nextFrame <= traceInfo.endFrame;
809 ++nextFrame)
810 {
811 ReplayFrame(testID, nextFrame);
812 }
813 ResetReplay(testID);
814 getGLWindow()->swap();
815 glFinish();
816 }
817
TEST_P(TracePerfTest,Run)818 TEST_P(TracePerfTest, Run)
819 {
820 run();
821 }
822
CombineTestID(const TracePerfParams & in,RestrictedTraceID id)823 TracePerfParams CombineTestID(const TracePerfParams &in, RestrictedTraceID id)
824 {
825 const TraceInfo &traceInfo = GetTraceInfo(id);
826
827 TracePerfParams out = in;
828 out.testID = id;
829 out.windowWidth = traceInfo.drawSurfaceWidth;
830 out.windowHeight = traceInfo.drawSurfaceHeight;
831 return out;
832 }
833
NoAndroidMockICD(const TracePerfParams & in)834 bool NoAndroidMockICD(const TracePerfParams &in)
835 {
836 return in.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE || !IsAndroid();
837 }
838
CombineWithSurfaceType(const TracePerfParams & in,SurfaceType surfaceType)839 TracePerfParams CombineWithSurfaceType(const TracePerfParams &in, SurfaceType surfaceType)
840 {
841 TracePerfParams out = in;
842 out.surfaceType = surfaceType;
843
844 if (!IsAndroid() && surfaceType == SurfaceType::Offscreen)
845 {
846 out.windowWidth /= 4;
847 out.windowHeight /= 4;
848 }
849
850 // We track GPU time only in frame-rate-limited cases.
851 out.trackGpuTime = surfaceType == SurfaceType::WindowWithVSync;
852
853 return out;
854 }
855
856 using namespace params;
857 using P = TracePerfParams;
858
859 std::vector<P> gTestsWithID =
860 CombineWithValues({P()}, AllEnums<RestrictedTraceID>(), CombineTestID);
861 std::vector<P> gTestsWithSurfaceType =
862 CombineWithValues(gTestsWithID,
863 {SurfaceType::Offscreen, SurfaceType::Window, SurfaceType::WindowWithVSync},
864 CombineWithSurfaceType);
865 std::vector<P> gTestsWithRenderer =
866 CombineWithFuncs(gTestsWithSurfaceType,
867 {Vulkan<P>, VulkanMockICD<P>, VulkanSwiftShader<P>, Native<P>});
868 std::vector<P> gTestsWithoutMockICD = FilterWithFunc(gTestsWithRenderer, NoAndroidMockICD);
869 ANGLE_INSTANTIATE_TEST_ARRAY(TracePerfTest, gTestsWithoutMockICD);
870
871 } // anonymous namespace
872