• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2014 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 // ANGLEPerfTests:
7 //   Base class for google test performance tests
8 //
9 
10 #include "ANGLEPerfTest.h"
11 
12 #include "ANGLEPerfTestArgs.h"
13 #include "common/debug.h"
14 #include "common/platform.h"
15 #include "common/system_utils.h"
16 #include "common/utilities.h"
17 #include "third_party/perf/perf_test.h"
18 #include "third_party/trace_event/trace_event.h"
19 #include "util/shader_utils.h"
20 #include "util/test_utils.h"
21 
22 #include <cassert>
23 #include <cmath>
24 #include <fstream>
25 #include <iostream>
26 #include <sstream>
27 
28 #include <json/json.h>
29 
30 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
31 #    include "util/windows/WGLWindow.h"
32 #endif  // defined(ANGLE_USE_UTIL_LOADER) &&defined(ANGLE_PLATFORM_WINDOWS)
33 
34 using namespace angle;
35 
36 namespace
37 {
38 constexpr size_t kInitialTraceEventBufferSize = 50000;
39 constexpr double kMicroSecondsPerSecond       = 1e6;
40 constexpr double kNanoSecondsPerSecond        = 1e9;
41 constexpr double kCalibrationRunTimeSeconds   = 1.0;
42 constexpr double kMaximumRunTimeSeconds       = 10.0;
43 constexpr unsigned int kNumTrials             = 3;
44 
45 struct TraceCategory
46 {
47     unsigned char enabled;
48     const char *name;
49 };
50 
51 constexpr TraceCategory gTraceCategories[2] = {
52     {1, "gpu.angle"},
53     {1, "gpu.angle.gpu"},
54 };
55 
EmptyPlatformMethod(angle::PlatformMethods *,const char *)56 void EmptyPlatformMethod(angle::PlatformMethods *, const char *) {}
57 
OverrideWorkaroundsD3D(angle::PlatformMethods * platform,angle::FeaturesD3D * featuresD3D)58 void OverrideWorkaroundsD3D(angle::PlatformMethods *platform, angle::FeaturesD3D *featuresD3D)
59 {
60     auto *angleRenderTest = static_cast<ANGLERenderTest *>(platform->context);
61     angleRenderTest->overrideWorkaroundsD3D(featuresD3D);
62 }
63 
AddPerfTraceEvent(angle::PlatformMethods * platform,char phase,const unsigned char * categoryEnabledFlag,const char * name,unsigned long long id,double timestamp,int numArgs,const char ** argNames,const unsigned char * argTypes,const unsigned long long * argValues,unsigned char flags)64 angle::TraceEventHandle AddPerfTraceEvent(angle::PlatformMethods *platform,
65                                           char phase,
66                                           const unsigned char *categoryEnabledFlag,
67                                           const char *name,
68                                           unsigned long long id,
69                                           double timestamp,
70                                           int numArgs,
71                                           const char **argNames,
72                                           const unsigned char *argTypes,
73                                           const unsigned long long *argValues,
74                                           unsigned char flags)
75 {
76     if (!gEnableTrace)
77         return 0;
78 
79     // Discover the category name based on categoryEnabledFlag.  This flag comes from the first
80     // parameter of TraceCategory, and corresponds to one of the entries in gTraceCategories.
81     static_assert(offsetof(TraceCategory, enabled) == 0,
82                   "|enabled| must be the first field of the TraceCategory class.");
83     const TraceCategory *category = reinterpret_cast<const TraceCategory *>(categoryEnabledFlag);
84 
85     ANGLERenderTest *renderTest     = static_cast<ANGLERenderTest *>(platform->context);
86     std::vector<TraceEvent> &buffer = renderTest->getTraceEventBuffer();
87     buffer.emplace_back(phase, category->name, name, timestamp);
88     return buffer.size();
89 }
90 
GetPerfTraceCategoryEnabled(angle::PlatformMethods * platform,const char * categoryName)91 const unsigned char *GetPerfTraceCategoryEnabled(angle::PlatformMethods *platform,
92                                                  const char *categoryName)
93 {
94     if (gEnableTrace)
95     {
96         for (const TraceCategory &category : gTraceCategories)
97         {
98             if (strcmp(category.name, categoryName) == 0)
99             {
100                 return &category.enabled;
101             }
102         }
103     }
104 
105     constexpr static unsigned char kZero = 0;
106     return &kZero;
107 }
108 
UpdateTraceEventDuration(angle::PlatformMethods * platform,const unsigned char * categoryEnabledFlag,const char * name,angle::TraceEventHandle eventHandle)109 void UpdateTraceEventDuration(angle::PlatformMethods *platform,
110                               const unsigned char *categoryEnabledFlag,
111                               const char *name,
112                               angle::TraceEventHandle eventHandle)
113 {
114     // Not implemented.
115 }
116 
MonotonicallyIncreasingTime(angle::PlatformMethods * platform)117 double MonotonicallyIncreasingTime(angle::PlatformMethods *platform)
118 {
119     return GetHostTimeSeconds();
120 }
121 
DumpTraceEventsToJSONFile(const std::vector<TraceEvent> & traceEvents,const char * outputFileName)122 void DumpTraceEventsToJSONFile(const std::vector<TraceEvent> &traceEvents,
123                                const char *outputFileName)
124 {
125     Json::Value eventsValue(Json::arrayValue);
126 
127     for (const TraceEvent &traceEvent : traceEvents)
128     {
129         Json::Value value(Json::objectValue);
130 
131         std::stringstream phaseName;
132         phaseName << traceEvent.phase;
133 
134         const auto microseconds =
135             static_cast<Json::LargestInt>(traceEvent.timestamp * 1000.0 * 1000.0);
136 
137         value["name"] = traceEvent.name;
138         value["cat"]  = traceEvent.categoryName;
139         value["ph"]   = phaseName.str();
140         value["ts"]   = microseconds;
141         value["pid"]  = strcmp(traceEvent.categoryName, "gpu.angle.gpu") == 0 ? "GPU" : "ANGLE";
142         value["tid"]  = 1;
143 
144         eventsValue.append(value);
145     }
146 
147     Json::Value root(Json::objectValue);
148     root["traceEvents"] = eventsValue;
149 
150     std::ofstream outFile;
151     outFile.open(outputFileName);
152 
153     Json::StyledWriter styledWrite;
154     outFile << styledWrite.write(root);
155 
156     outFile.close();
157 }
158 
DebugMessageCallback(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar * message,const void * userParam)159 ANGLE_MAYBE_UNUSED void KHRONOS_APIENTRY DebugMessageCallback(GLenum source,
160                                                               GLenum type,
161                                                               GLuint id,
162                                                               GLenum severity,
163                                                               GLsizei length,
164                                                               const GLchar *message,
165                                                               const void *userParam)
166 {
167     std::string sourceText   = gl::GetDebugMessageSourceString(source);
168     std::string typeText     = gl::GetDebugMessageTypeString(type);
169     std::string severityText = gl::GetDebugMessageSeverityString(severity);
170     std::cerr << sourceText << ", " << typeText << ", " << severityText << ": " << message << "\n";
171 }
172 }  // anonymous namespace
173 
TraceEvent(char phaseIn,const char * categoryNameIn,const char * nameIn,double timestampIn)174 TraceEvent::TraceEvent(char phaseIn,
175                        const char *categoryNameIn,
176                        const char *nameIn,
177                        double timestampIn)
178     : phase(phaseIn), categoryName(categoryNameIn), name{}, timestamp(timestampIn), tid(1)
179 {
180     ASSERT(strlen(nameIn) < kMaxNameLen);
181     strcpy(name, nameIn);
182 }
183 
ANGLEPerfTest(const std::string & name,const std::string & backend,const std::string & story,unsigned int iterationsPerStep)184 ANGLEPerfTest::ANGLEPerfTest(const std::string &name,
185                              const std::string &backend,
186                              const std::string &story,
187                              unsigned int iterationsPerStep)
188     : mName(name),
189       mBackend(backend),
190       mStory(story),
191       mGPUTimeNs(0),
192       mSkipTest(false),
193       mStepsToRun(std::numeric_limits<unsigned int>::max()),
194       mNumStepsPerformed(0),
195       mIterationsPerStep(iterationsPerStep),
196       mRunning(true)
197 {
198     if (mStory == "")
199     {
200         mStory = "baseline_story";
201     }
202     if (mStory[0] == '_')
203     {
204         mStory = mStory.substr(1);
205     }
206     mReporter = std::make_unique<perf_test::PerfResultReporter>(mName + mBackend, mStory);
207     mReporter->RegisterImportantMetric(".wall_time", "ns");
208     mReporter->RegisterImportantMetric(".gpu_time", "ns");
209     mReporter->RegisterFyiMetric(".steps", "count");
210 }
211 
~ANGLEPerfTest()212 ANGLEPerfTest::~ANGLEPerfTest() {}
213 
run()214 void ANGLEPerfTest::run()
215 {
216     if (mSkipTest)
217     {
218         return;
219     }
220 
221     // Calibrate to a fixed number of steps during an initial set time.
222     if (gStepsToRunOverride <= 0)
223     {
224         doRunLoop(kCalibrationRunTimeSeconds);
225 
226         // Scale steps down according to the time that exeeded one second.
227         double scale = kCalibrationRunTimeSeconds / mTimer.getElapsedTime();
228         mStepsToRun  = static_cast<unsigned int>(static_cast<double>(mNumStepsPerformed) * scale);
229 
230         // Calibration allows the perf test runner script to save some time.
231         if (gCalibration)
232         {
233             mReporter->AddResult(".steps", static_cast<size_t>(mStepsToRun));
234             return;
235         }
236     }
237     else
238     {
239         mStepsToRun = gStepsToRunOverride;
240     }
241 
242     // Check again for early exit.
243     if (mSkipTest)
244     {
245         return;
246     }
247 
248     // Do another warmup run. Seems to consistently improve results.
249     doRunLoop(kMaximumRunTimeSeconds);
250 
251     double totalTime = 0.0;
252     for (unsigned int trial = 0; trial < kNumTrials; ++trial)
253     {
254         doRunLoop(kMaximumRunTimeSeconds);
255         totalTime += printResults();
256     }
257 }
258 
doRunLoop(double maxRunTime)259 void ANGLEPerfTest::doRunLoop(double maxRunTime)
260 {
261     mNumStepsPerformed = 0;
262     mRunning           = true;
263     mTimer.start();
264     startTest();
265 
266     while (mRunning)
267     {
268         step();
269         if (mRunning)
270         {
271             ++mNumStepsPerformed;
272             if (mTimer.getElapsedTime() > maxRunTime)
273             {
274                 mRunning = false;
275             }
276             else if (mNumStepsPerformed >= mStepsToRun)
277             {
278                 mRunning = false;
279             }
280         }
281     }
282     finishTest();
283     mTimer.stop();
284 }
285 
SetUp()286 void ANGLEPerfTest::SetUp() {}
287 
TearDown()288 void ANGLEPerfTest::TearDown() {}
289 
printResults()290 double ANGLEPerfTest::printResults()
291 {
292     double elapsedTimeSeconds[2] = {
293         mTimer.getElapsedTime(),
294         mGPUTimeNs * 1e-9,
295     };
296 
297     const char *clockNames[2] = {
298         ".wall_time",
299         ".gpu_time",
300     };
301 
302     // If measured gpu time is non-zero, print that too.
303     size_t clocksToOutput = mGPUTimeNs > 0 ? 2 : 1;
304 
305     double retValue = 0.0;
306     for (size_t i = 0; i < clocksToOutput; ++i)
307     {
308         double secondsPerStep = elapsedTimeSeconds[i] / static_cast<double>(mNumStepsPerformed);
309         double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
310 
311         perf_test::MetricInfo metricInfo;
312         std::string units;
313         // Lazily register the metric, re-using the existing units if it is
314         // already registered.
315         if (!mReporter->GetMetricInfo(clockNames[i], &metricInfo))
316         {
317             units = secondsPerIteration > 1e-3 ? "us" : "ns";
318             mReporter->RegisterImportantMetric(clockNames[i], units);
319         }
320         else
321         {
322             units = metricInfo.units;
323         }
324 
325         if (units == "us")
326         {
327             retValue = secondsPerIteration * kMicroSecondsPerSecond;
328         }
329         else
330         {
331             retValue = secondsPerIteration * kNanoSecondsPerSecond;
332         }
333         mReporter->AddResult(clockNames[i], retValue);
334     }
335     return retValue;
336 }
337 
normalizedTime(size_t value) const338 double ANGLEPerfTest::normalizedTime(size_t value) const
339 {
340     return static_cast<double>(value) / static_cast<double>(mNumStepsPerformed);
341 }
342 
backend() const343 std::string RenderTestParams::backend() const
344 {
345     std::stringstream strstr;
346 
347     switch (driver)
348     {
349         case angle::GLESDriverType::AngleEGL:
350             break;
351         case angle::GLESDriverType::SystemEGL:
352             strstr << "_native";
353             break;
354         case angle::GLESDriverType::SystemWGL:
355             strstr << "_wgl";
356             break;
357         default:
358             assert(0);
359             return "_unk";
360     }
361 
362     switch (getRenderer())
363     {
364         case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
365             break;
366         case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE:
367             strstr << "_d3d11";
368             break;
369         case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE:
370             strstr << "_d3d9";
371             break;
372         case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
373             strstr << "_gl";
374             break;
375         case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
376             strstr << "_gles";
377             break;
378         case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
379             strstr << "_vulkan";
380             break;
381         default:
382             assert(0);
383             return "_unk";
384     }
385 
386     if (eglParameters.deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE)
387     {
388         strstr << "_null";
389     }
390 
391     return strstr.str();
392 }
393 
story() const394 std::string RenderTestParams::story() const
395 {
396     return "";
397 }
398 
backendAndStory() const399 std::string RenderTestParams::backendAndStory() const
400 {
401     return backend() + story();
402 }
403 
ANGLERenderTest(const std::string & name,const RenderTestParams & testParams)404 ANGLERenderTest::ANGLERenderTest(const std::string &name, const RenderTestParams &testParams)
405     : ANGLEPerfTest(name,
406                     testParams.backend(),
407                     testParams.story(),
408                     OneFrame() ? 1 : testParams.iterationsPerStep),
409       mTestParams(testParams),
410       mIsTimestampQueryAvailable(false),
411       mGLWindow(nullptr),
412       mOSWindow(nullptr),
413       mSwapEnabled(true)
414 {
415     // Force fast tests to make sure our slowest bots don't time out.
416     if (OneFrame())
417     {
418         const_cast<RenderTestParams &>(testParams).iterationsPerStep = 1;
419     }
420 
421     // Try to ensure we don't trigger allocation during execution.
422     mTraceEventBuffer.reserve(kInitialTraceEventBufferSize);
423 
424     switch (testParams.driver)
425     {
426         case angle::GLESDriverType::AngleEGL:
427             mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
428             mEntryPointsLib.reset(angle::OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME,
429                                                            angle::SearchType::ApplicationDir));
430             break;
431         case angle::GLESDriverType::SystemEGL:
432 #if defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
433             mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
434             mEntryPointsLib.reset(
435                 angle::OpenSharedLibraryWithExtension(GetNativeEGLLibraryNameWithExtension()));
436 #else
437             std::cerr << "Not implemented." << std::endl;
438             mSkipTest = true;
439 #endif  // defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
440             break;
441         case angle::GLESDriverType::SystemWGL:
442 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
443             mGLWindow = WGLWindow::New(testParams.majorVersion, testParams.minorVersion);
444             mEntryPointsLib.reset(
445                 angle::OpenSharedLibrary("opengl32", angle::SearchType::SystemDir));
446 #else
447             std::cout << "WGL driver not available. Skipping test." << std::endl;
448             mSkipTest = true;
449 #endif  // defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
450             break;
451         default:
452             std::cerr << "Error in switch." << std::endl;
453             mSkipTest = true;
454             break;
455     }
456 }
457 
~ANGLERenderTest()458 ANGLERenderTest::~ANGLERenderTest()
459 {
460     OSWindow::Delete(&mOSWindow);
461     GLWindowBase::Delete(&mGLWindow);
462 }
463 
addExtensionPrerequisite(const char * extensionName)464 void ANGLERenderTest::addExtensionPrerequisite(const char *extensionName)
465 {
466     mExtensionPrerequisites.push_back(extensionName);
467 }
468 
SetUp()469 void ANGLERenderTest::SetUp()
470 {
471     if (mSkipTest)
472     {
473         return;
474     }
475 
476     ANGLEPerfTest::SetUp();
477 
478     // Set a consistent CPU core affinity and high priority.
479     angle::StabilizeCPUForBenchmarking();
480 
481     mOSWindow = OSWindow::New();
482 
483     if (!mGLWindow)
484     {
485         mSkipTest = true;
486         return;
487     }
488 
489     mPlatformMethods.overrideWorkaroundsD3D      = OverrideWorkaroundsD3D;
490     mPlatformMethods.logError                    = EmptyPlatformMethod;
491     mPlatformMethods.logWarning                  = EmptyPlatformMethod;
492     mPlatformMethods.logInfo                     = EmptyPlatformMethod;
493     mPlatformMethods.addTraceEvent               = AddPerfTraceEvent;
494     mPlatformMethods.getTraceCategoryEnabledFlag = GetPerfTraceCategoryEnabled;
495     mPlatformMethods.updateTraceEventDuration    = UpdateTraceEventDuration;
496     mPlatformMethods.monotonicallyIncreasingTime = MonotonicallyIncreasingTime;
497     mPlatformMethods.context                     = this;
498 
499     if (!mOSWindow->initialize(mName, mTestParams.windowWidth, mTestParams.windowHeight))
500     {
501         mSkipTest = true;
502         FAIL() << "Failed initializing OSWindow";
503         // FAIL returns.
504     }
505 
506     // Override platform method parameter.
507     EGLPlatformParameters withMethods = mTestParams.eglParameters;
508     withMethods.platformMethods       = &mPlatformMethods;
509 
510     // Request a common framebuffer config
511     mConfigParams.redBits     = 8;
512     mConfigParams.greenBits   = 8;
513     mConfigParams.blueBits    = 8;
514     mConfigParams.alphaBits   = 8;
515     mConfigParams.depthBits   = 24;
516     mConfigParams.stencilBits = 8;
517 
518     if (!mGLWindow->initializeGL(mOSWindow, mEntryPointsLib.get(), mTestParams.driver, withMethods,
519                                  mConfigParams))
520     {
521         mSkipTest = true;
522         FAIL() << "Failed initializing GL Window";
523         // FAIL returns.
524     }
525 
526     // Disable vsync.
527     if (!mGLWindow->setSwapInterval(0))
528     {
529         mSkipTest = true;
530         FAIL() << "Failed setting swap interval";
531         // FAIL returns.
532     }
533 
534     mIsTimestampQueryAvailable = IsGLExtensionEnabled("GL_EXT_disjoint_timer_query");
535 
536     if (!areExtensionPrerequisitesFulfilled())
537     {
538         mSkipTest = true;
539     }
540 
541     if (mSkipTest)
542     {
543         return;
544     }
545 
546 #if defined(ANGLE_ENABLE_ASSERTS)
547     if (IsGLExtensionEnabled("GL_KHR_debug"))
548     {
549         glEnable(GL_DEBUG_OUTPUT);
550         glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
551         // Enable medium and high priority messages.
552         glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr,
553                                  GL_TRUE);
554         glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr,
555                                  GL_TRUE);
556         // Disable low and notification priority messages.
557         glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, nullptr,
558                                  GL_FALSE);
559         glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0,
560                                  nullptr, GL_FALSE);
561         // Disable medium priority performance messages to reduce spam.
562         glDebugMessageControlKHR(GL_DONT_CARE, GL_DEBUG_TYPE_PERFORMANCE, GL_DEBUG_SEVERITY_MEDIUM,
563                                  0, nullptr, GL_FALSE);
564         glDebugMessageCallbackKHR(DebugMessageCallback, this);
565     }
566 #endif
567 
568     initializeBenchmark();
569 
570     if (mTestParams.iterationsPerStep == 0)
571     {
572         mSkipTest = true;
573         FAIL() << "Please initialize 'iterationsPerStep'.";
574         // FAIL returns.
575     }
576 
577     // Capture a screenshot if enabled.
578     if (gScreenShotDir != nullptr)
579     {
580         std::stringstream screenshotNameStr;
581         screenshotNameStr << gScreenShotDir << GetPathSeparator() << "angle" << mBackend << "_"
582                           << mStory << ".png";
583         std::string screenshotName = screenshotNameStr.str();
584         saveScreenshot(screenshotName);
585     }
586 }
587 
TearDown()588 void ANGLERenderTest::TearDown()
589 {
590     if (!mSkipTest)
591     {
592         destroyBenchmark();
593     }
594 
595     if (mGLWindow)
596     {
597         mGLWindow->destroyGL();
598         mGLWindow = nullptr;
599     }
600 
601     if (mOSWindow)
602     {
603         mOSWindow->destroy();
604         mOSWindow = nullptr;
605     }
606 
607     // Dump trace events to json file.
608     if (gEnableTrace)
609     {
610         DumpTraceEventsToJSONFile(mTraceEventBuffer, gTraceFile);
611     }
612 
613     ANGLEPerfTest::TearDown();
614 }
615 
beginInternalTraceEvent(const char * name)616 void ANGLERenderTest::beginInternalTraceEvent(const char *name)
617 {
618     if (gEnableTrace)
619     {
620         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[0].name, name,
621                                        MonotonicallyIncreasingTime(&mPlatformMethods));
622     }
623 }
624 
endInternalTraceEvent(const char * name)625 void ANGLERenderTest::endInternalTraceEvent(const char *name)
626 {
627     if (gEnableTrace)
628     {
629         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[0].name, name,
630                                        MonotonicallyIncreasingTime(&mPlatformMethods));
631     }
632 }
633 
beginGLTraceEvent(const char * name,double hostTimeSec)634 void ANGLERenderTest::beginGLTraceEvent(const char *name, double hostTimeSec)
635 {
636     if (gEnableTrace)
637     {
638         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[1].name, name,
639                                        hostTimeSec);
640     }
641 }
642 
endGLTraceEvent(const char * name,double hostTimeSec)643 void ANGLERenderTest::endGLTraceEvent(const char *name, double hostTimeSec)
644 {
645     if (gEnableTrace)
646     {
647         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[1].name, name,
648                                        hostTimeSec);
649     }
650 }
651 
step()652 void ANGLERenderTest::step()
653 {
654     beginInternalTraceEvent("step");
655 
656     // Clear events that the application did not process from this frame
657     Event event;
658     bool closed = false;
659     while (popEvent(&event))
660     {
661         // If the application did not catch a close event, close now
662         if (event.Type == Event::EVENT_CLOSED)
663         {
664             closed = true;
665         }
666     }
667 
668     if (closed)
669     {
670         abortTest();
671     }
672     else
673     {
674         drawBenchmark();
675 
676         // Swap is needed so that the GPU driver will occasionally flush its
677         // internal command queue to the GPU. This is enabled for null back-end
678         // devices because some back-ends (e.g. Vulkan) also accumulate internal
679         // command queues.
680         if (mSwapEnabled)
681         {
682             mGLWindow->swap();
683         }
684         mOSWindow->messageLoop();
685 
686 #if defined(ANGLE_ENABLE_ASSERTS)
687         EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
688 #endif  // defined(ANGLE_ENABLE_ASSERTS)
689     }
690 
691     endInternalTraceEvent("step");
692 }
693 
startGpuTimer()694 void ANGLERenderTest::startGpuTimer()
695 {
696     if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
697     {
698         glBeginQueryEXT(GL_TIME_ELAPSED_EXT, mTimestampQuery);
699     }
700 }
701 
stopGpuTimer()702 void ANGLERenderTest::stopGpuTimer()
703 {
704     if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
705     {
706         glEndQueryEXT(GL_TIME_ELAPSED_EXT);
707         uint64_t gpuTimeNs = 0;
708         glGetQueryObjectui64vEXT(mTimestampQuery, GL_QUERY_RESULT_EXT, &gpuTimeNs);
709 
710         mGPUTimeNs += gpuTimeNs;
711     }
712 }
713 
startTest()714 void ANGLERenderTest::startTest()
715 {
716     if (mTestParams.trackGpuTime)
717     {
718         glGenQueriesEXT(1, &mTimestampQuery);
719         mGPUTimeNs = 0;
720     }
721 }
722 
finishTest()723 void ANGLERenderTest::finishTest()
724 {
725     if (mTestParams.trackGpuTime)
726     {
727         glDeleteQueriesEXT(1, &mTimestampQuery);
728     }
729     if (mTestParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE)
730     {
731         glFinish();
732     }
733 }
734 
popEvent(Event * event)735 bool ANGLERenderTest::popEvent(Event *event)
736 {
737     return mOSWindow->popEvent(event);
738 }
739 
getWindow()740 OSWindow *ANGLERenderTest::getWindow()
741 {
742     return mOSWindow;
743 }
744 
getGLWindow()745 GLWindowBase *ANGLERenderTest::getGLWindow()
746 {
747     return mGLWindow;
748 }
749 
areExtensionPrerequisitesFulfilled() const750 bool ANGLERenderTest::areExtensionPrerequisitesFulfilled() const
751 {
752     for (const char *extension : mExtensionPrerequisites)
753     {
754         if (!CheckExtensionExists(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
755                                   extension))
756         {
757             std::cout << "Test skipped due to missing extension: " << extension << std::endl;
758             return false;
759         }
760     }
761     return true;
762 }
763 
setWebGLCompatibilityEnabled(bool webglCompatibility)764 void ANGLERenderTest::setWebGLCompatibilityEnabled(bool webglCompatibility)
765 {
766     mConfigParams.webGLCompatibility = webglCompatibility;
767 }
768 
setRobustResourceInit(bool enabled)769 void ANGLERenderTest::setRobustResourceInit(bool enabled)
770 {
771     mConfigParams.robustResourceInit = enabled;
772 }
773 
getTraceEventBuffer()774 std::vector<TraceEvent> &ANGLERenderTest::getTraceEventBuffer()
775 {
776     return mTraceEventBuffer;
777 }
778 
779 namespace angle
780 {
GetHostTimeSeconds()781 double GetHostTimeSeconds()
782 {
783     // Move the time origin to the first call to this function, to avoid generating unnecessarily
784     // large timestamps.
785     static double origin = angle::GetCurrentTime();
786     return angle::GetCurrentTime() - origin;
787 }
788 }  // namespace angle
789