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 "test_utils/runner/TestSuite.h"
18 #include "third_party/perf/perf_test.h"
19 #include "third_party/trace_event/trace_event.h"
20 #include "util/shader_utils.h"
21 #include "util/test_utils.h"
22 
23 #include <cassert>
24 #include <cmath>
25 #include <fstream>
26 #include <iostream>
27 #include <sstream>
28 
29 #include <rapidjson/document.h>
30 #include <rapidjson/filewritestream.h>
31 #include <rapidjson/istreamwrapper.h>
32 #include <rapidjson/prettywriter.h>
33 
34 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
35 #    include "util/windows/WGLWindow.h"
36 #endif  // defined(ANGLE_USE_UTIL_LOADER) &&defined(ANGLE_PLATFORM_WINDOWS)
37 
38 using namespace angle;
39 namespace js = rapidjson;
40 
41 namespace
42 {
43 constexpr size_t kInitialTraceEventBufferSize = 50000;
44 constexpr double kMilliSecondsPerSecond       = 1e3;
45 constexpr double kMicroSecondsPerSecond       = 1e6;
46 constexpr double kNanoSecondsPerSecond        = 1e9;
47 
48 struct TraceCategory
49 {
50     unsigned char enabled;
51     const char *name;
52 };
53 
54 constexpr TraceCategory gTraceCategories[2] = {
55     {1, "gpu.angle"},
56     {1, "gpu.angle.gpu"},
57 };
58 
EmptyPlatformMethod(angle::PlatformMethods *,const char *)59 void EmptyPlatformMethod(angle::PlatformMethods *, const char *) {}
60 
CustomLogError(angle::PlatformMethods * platform,const char * errorMessage)61 void CustomLogError(angle::PlatformMethods *platform, const char *errorMessage)
62 {
63     auto *angleRenderTest = static_cast<ANGLERenderTest *>(platform->context);
64     angleRenderTest->onErrorMessage(errorMessage);
65 }
66 
OverrideWorkaroundsD3D(angle::PlatformMethods * platform,angle::FeaturesD3D * featuresD3D)67 void OverrideWorkaroundsD3D(angle::PlatformMethods *platform, angle::FeaturesD3D *featuresD3D)
68 {
69     auto *angleRenderTest = static_cast<ANGLERenderTest *>(platform->context);
70     angleRenderTest->overrideWorkaroundsD3D(featuresD3D);
71 }
72 
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)73 angle::TraceEventHandle AddPerfTraceEvent(angle::PlatformMethods *platform,
74                                           char phase,
75                                           const unsigned char *categoryEnabledFlag,
76                                           const char *name,
77                                           unsigned long long id,
78                                           double timestamp,
79                                           int numArgs,
80                                           const char **argNames,
81                                           const unsigned char *argTypes,
82                                           const unsigned long long *argValues,
83                                           unsigned char flags)
84 {
85     if (!gEnableTrace)
86         return 0;
87 
88     // Discover the category name based on categoryEnabledFlag.  This flag comes from the first
89     // parameter of TraceCategory, and corresponds to one of the entries in gTraceCategories.
90     static_assert(offsetof(TraceCategory, enabled) == 0,
91                   "|enabled| must be the first field of the TraceCategory class.");
92     const TraceCategory *category = reinterpret_cast<const TraceCategory *>(categoryEnabledFlag);
93 
94     ANGLERenderTest *renderTest = static_cast<ANGLERenderTest *>(platform->context);
95 
96     uint32_t tid = renderTest->getCurrentThreadSerial();
97 
98     std::vector<TraceEvent> &buffer = renderTest->getTraceEventBuffer();
99     buffer.emplace_back(phase, category->name, name, timestamp, tid);
100     return buffer.size();
101 }
102 
GetPerfTraceCategoryEnabled(angle::PlatformMethods * platform,const char * categoryName)103 const unsigned char *GetPerfTraceCategoryEnabled(angle::PlatformMethods *platform,
104                                                  const char *categoryName)
105 {
106     if (gEnableTrace)
107     {
108         for (const TraceCategory &category : gTraceCategories)
109         {
110             if (strcmp(category.name, categoryName) == 0)
111             {
112                 return &category.enabled;
113             }
114         }
115     }
116 
117     constexpr static unsigned char kZero = 0;
118     return &kZero;
119 }
120 
UpdateTraceEventDuration(angle::PlatformMethods * platform,const unsigned char * categoryEnabledFlag,const char * name,angle::TraceEventHandle eventHandle)121 void UpdateTraceEventDuration(angle::PlatformMethods *platform,
122                               const unsigned char *categoryEnabledFlag,
123                               const char *name,
124                               angle::TraceEventHandle eventHandle)
125 {
126     // Not implemented.
127 }
128 
MonotonicallyIncreasingTime(angle::PlatformMethods * platform)129 double MonotonicallyIncreasingTime(angle::PlatformMethods *platform)
130 {
131     return GetHostTimeSeconds();
132 }
133 
WriteJsonFile(const std::string & outputFile,js::Document * doc)134 bool WriteJsonFile(const std::string &outputFile, js::Document *doc)
135 {
136     FILE *fp = fopen(outputFile.c_str(), "w");
137     if (!fp)
138     {
139         return false;
140     }
141 
142     constexpr size_t kBufferSize = 0xFFFF;
143     std::vector<char> writeBuffer(kBufferSize);
144     js::FileWriteStream os(fp, writeBuffer.data(), kBufferSize);
145     js::PrettyWriter<js::FileWriteStream> writer(os);
146     if (!doc->Accept(writer))
147     {
148         fclose(fp);
149         return false;
150     }
151     fclose(fp);
152     return true;
153 }
154 
DumpTraceEventsToJSONFile(const std::vector<TraceEvent> & traceEvents,const char * outputFileName)155 void DumpTraceEventsToJSONFile(const std::vector<TraceEvent> &traceEvents,
156                                const char *outputFileName)
157 {
158     js::Document doc(js::kObjectType);
159     js::Document::AllocatorType &allocator = doc.GetAllocator();
160 
161     js::Value events(js::kArrayType);
162 
163     for (const TraceEvent &traceEvent : traceEvents)
164     {
165         js::Value value(js::kObjectType);
166 
167         const uint64_t microseconds = static_cast<uint64_t>(traceEvent.timestamp * 1000.0 * 1000.0);
168 
169         js::Document::StringRefType eventName(traceEvent.name);
170         js::Document::StringRefType categoryName(traceEvent.categoryName);
171         js::Document::StringRefType pidName(
172             strcmp(traceEvent.categoryName, "gpu.angle.gpu") == 0 ? "GPU" : "ANGLE");
173 
174         value.AddMember("name", eventName, allocator);
175         value.AddMember("cat", categoryName, allocator);
176         value.AddMember("ph", std::string(1, traceEvent.phase), allocator);
177         value.AddMember("ts", microseconds, allocator);
178         value.AddMember("pid", pidName, allocator);
179         value.AddMember("tid", traceEvent.tid, allocator);
180 
181         events.PushBack(value, allocator);
182     }
183 
184     doc.AddMember("traceEvents", events, allocator);
185 
186     if (WriteJsonFile(outputFileName, &doc))
187     {
188         printf("Wrote trace file to %s\n", outputFileName);
189     }
190     else
191     {
192         printf("Error writing trace file to %s\n", outputFileName);
193     }
194 }
195 }  // anonymous namespace
196 
TraceEvent(char phaseIn,const char * categoryNameIn,const char * nameIn,double timestampIn,uint32_t tidIn)197 TraceEvent::TraceEvent(char phaseIn,
198                        const char *categoryNameIn,
199                        const char *nameIn,
200                        double timestampIn,
201                        uint32_t tidIn)
202     : phase(phaseIn), categoryName(categoryNameIn), name{}, timestamp(timestampIn), tid(tidIn)
203 {
204     ASSERT(strlen(nameIn) < kMaxNameLen);
205     strcpy(name, nameIn);
206 }
207 
ANGLEPerfTest(const std::string & name,const std::string & backend,const std::string & story,unsigned int iterationsPerStep,const char * units)208 ANGLEPerfTest::ANGLEPerfTest(const std::string &name,
209                              const std::string &backend,
210                              const std::string &story,
211                              unsigned int iterationsPerStep,
212                              const char *units)
213     : mName(name),
214       mBackend(backend),
215       mStory(story),
216       mGPUTimeNs(0),
217       mSkipTest(false),
218       mStepsToRun(std::max(gStepsPerTrial, gMaxStepsPerformed)),
219       mTrialNumStepsPerformed(0),
220       mTotalNumStepsPerformed(0),
221       mStepsPerRunLoopStep(1),
222       mIterationsPerStep(iterationsPerStep),
223       mRunning(true)
224 {
225     if (mStory == "")
226     {
227         mStory = "baseline_story";
228     }
229     if (mStory[0] == '_')
230     {
231         mStory = mStory.substr(1);
232     }
233     mReporter = std::make_unique<perf_test::PerfResultReporter>(mName + mBackend, mStory);
234     mReporter->RegisterImportantMetric(".wall_time", units);
235     mReporter->RegisterImportantMetric(".gpu_time", units);
236     mReporter->RegisterFyiMetric(".trial_steps", "count");
237     mReporter->RegisterFyiMetric(".total_steps", "count");
238 }
239 
~ANGLEPerfTest()240 ANGLEPerfTest::~ANGLEPerfTest() {}
241 
run()242 void ANGLEPerfTest::run()
243 {
244     if (mSkipTest)
245     {
246         return;
247     }
248 
249     uint32_t numTrials = OneFrame() ? 1 : gTestTrials;
250     if (gVerboseLogging)
251     {
252         printf("Test Trials: %d\n", static_cast<int>(numTrials));
253     }
254 
255     for (uint32_t trial = 0; trial < numTrials; ++trial)
256     {
257         doRunLoop(gTestTimeSeconds, mStepsToRun, RunLoopPolicy::RunContinuously);
258         printResults();
259         if (gVerboseLogging)
260         {
261             double trialTime = mTimer.getElapsedTime();
262             printf("Trial %d time: %.2lf seconds.\n", trial + 1, trialTime);
263 
264             double secondsPerStep      = trialTime / static_cast<double>(mTrialNumStepsPerformed);
265             double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
266             mTestTrialResults.push_back(secondsPerIteration * 1000.0);
267         }
268     }
269 
270     if (gVerboseLogging)
271     {
272         double numResults = static_cast<double>(mTestTrialResults.size());
273         double mean       = 0;
274         for (double trialResult : mTestTrialResults)
275         {
276             mean += trialResult;
277         }
278         mean /= numResults;
279 
280         double variance = 0;
281         for (double trialResult : mTestTrialResults)
282         {
283             double difference = trialResult - mean;
284             variance += difference * difference;
285         }
286         variance /= numResults;
287 
288         double standardDeviation      = std::sqrt(variance);
289         double coefficientOfVariation = standardDeviation / mean;
290 
291         printf("Mean result time: %.4lf ms.\n", mean);
292         printf("Coefficient of variation: %.2lf%%\n", coefficientOfVariation * 100.0);
293     }
294 }
295 
setStepsPerRunLoopStep(int stepsPerRunLoop)296 void ANGLEPerfTest::setStepsPerRunLoopStep(int stepsPerRunLoop)
297 {
298     ASSERT(stepsPerRunLoop >= 1);
299     mStepsPerRunLoopStep = stepsPerRunLoop;
300 }
301 
doRunLoop(double maxRunTime,int maxStepsToRun,RunLoopPolicy runPolicy)302 void ANGLEPerfTest::doRunLoop(double maxRunTime, int maxStepsToRun, RunLoopPolicy runPolicy)
303 {
304     mTrialNumStepsPerformed = 0;
305     mRunning                = true;
306     mGPUTimeNs              = 0;
307     mTimer.start();
308     startTest();
309 
310     while (mRunning)
311     {
312         step();
313 
314         if (runPolicy == RunLoopPolicy::FinishEveryStep)
315         {
316             glFinish();
317         }
318 
319         if (mRunning)
320         {
321             mTrialNumStepsPerformed += mStepsPerRunLoopStep;
322             mTotalNumStepsPerformed += mStepsPerRunLoopStep;
323             if (mTimer.getElapsedTime() > maxRunTime)
324             {
325                 mRunning = false;
326             }
327             else if (mTrialNumStepsPerformed >= maxStepsToRun)
328             {
329                 mRunning = false;
330             }
331             else if (gMaxStepsPerformed > 0 && mTotalNumStepsPerformed >= gMaxStepsPerformed)
332             {
333                 mRunning = false;
334             }
335         }
336     }
337     finishTest();
338     mTimer.stop();
339     computeGPUTime();
340 }
341 
SetUp()342 void ANGLEPerfTest::SetUp() {}
343 
TearDown()344 void ANGLEPerfTest::TearDown() {}
345 
printResults()346 double ANGLEPerfTest::printResults()
347 {
348     double elapsedTimeSeconds[2] = {
349         mTimer.getElapsedTime(),
350         mGPUTimeNs * 1e-9,
351     };
352 
353     const char *clockNames[2] = {
354         ".wall_time",
355         ".gpu_time",
356     };
357 
358     // If measured gpu time is non-zero, print that too.
359     size_t clocksToOutput = mGPUTimeNs > 0 ? 2 : 1;
360 
361     double retValue = 0.0;
362     for (size_t i = 0; i < clocksToOutput; ++i)
363     {
364         double secondsPerStep =
365             elapsedTimeSeconds[i] / static_cast<double>(mTrialNumStepsPerformed);
366         double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
367 
368         perf_test::MetricInfo metricInfo;
369         std::string units;
370         // Lazily register the metric, re-using the existing units if it is
371         // already registered.
372         if (!mReporter->GetMetricInfo(clockNames[i], &metricInfo))
373         {
374             printf("Seconds per iteration: %lf\n", secondsPerIteration);
375             units = secondsPerIteration > 1e-3 ? "us" : "ns";
376             mReporter->RegisterImportantMetric(clockNames[i], units);
377         }
378         else
379         {
380             units = metricInfo.units;
381         }
382 
383         if (units == "ms")
384         {
385             retValue = secondsPerIteration * kMilliSecondsPerSecond;
386         }
387         else if (units == "us")
388         {
389             retValue = secondsPerIteration * kMicroSecondsPerSecond;
390         }
391         else
392         {
393             retValue = secondsPerIteration * kNanoSecondsPerSecond;
394         }
395         mReporter->AddResult(clockNames[i], retValue);
396     }
397 
398     if (gVerboseLogging)
399     {
400         double fps = static_cast<double>(mTrialNumStepsPerformed * mIterationsPerStep) /
401                      elapsedTimeSeconds[0];
402         printf("Ran %0.2lf iterations per second\n", fps);
403     }
404 
405     mReporter->AddResult(".trial_steps", static_cast<size_t>(mTrialNumStepsPerformed));
406     mReporter->AddResult(".total_steps", static_cast<size_t>(mTotalNumStepsPerformed));
407 
408     // Output histogram JSON set format if enabled.
409     double secondsPerStep = elapsedTimeSeconds[0] / static_cast<double>(mTrialNumStepsPerformed);
410     double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
411     TestSuite::GetInstance()->addHistogramSample(
412         mName + mBackend, mStory, secondsPerIteration * kMilliSecondsPerSecond, "msBestFitFormat");
413     return retValue;
414 }
415 
normalizedTime(size_t value) const416 double ANGLEPerfTest::normalizedTime(size_t value) const
417 {
418     return static_cast<double>(value) / static_cast<double>(mTrialNumStepsPerformed);
419 }
420 
calibrateStepsToRun()421 void ANGLEPerfTest::calibrateStepsToRun()
422 {
423     doRunLoop(gCalibrationTimeSeconds, std::numeric_limits<int>::max(),
424               RunLoopPolicy::FinishEveryStep);
425 
426     double elapsedTime = mTimer.getElapsedTime();
427 
428     // Scale steps down according to the time that exeeded one second.
429     double scale = gCalibrationTimeSeconds / elapsedTime;
430     mStepsToRun  = static_cast<unsigned int>(static_cast<double>(mTrialNumStepsPerformed) * scale);
431     mStepsToRun  = std::max(1, mStepsToRun);
432 
433     if (gVerboseLogging)
434     {
435         printf(
436             "Running %d steps (calibration took %.2lf seconds). Expecting trial time of %.2lf "
437             "seconds.\n",
438             mStepsToRun, elapsedTime,
439             mStepsToRun * (elapsedTime / static_cast<double>(mTrialNumStepsPerformed)));
440     }
441 
442     // Calibration allows the perf test runner script to save some time.
443     if (gCalibration)
444     {
445         mReporter->AddResult(".steps", static_cast<size_t>(mStepsToRun));
446         return;
447     }
448 }
449 
backend() const450 std::string RenderTestParams::backend() const
451 {
452     std::stringstream strstr;
453 
454     switch (driver)
455     {
456         case angle::GLESDriverType::AngleEGL:
457             break;
458         case angle::GLESDriverType::SystemWGL:
459         case angle::GLESDriverType::SystemEGL:
460             strstr << "_native";
461             break;
462         default:
463             assert(0);
464             return "_unk";
465     }
466 
467     switch (getRenderer())
468     {
469         case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
470             break;
471         case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE:
472             strstr << "_d3d11";
473             break;
474         case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE:
475             strstr << "_d3d9";
476             break;
477         case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
478             strstr << "_gl";
479             break;
480         case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
481             strstr << "_gles";
482             break;
483         case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
484             strstr << "_vulkan";
485             break;
486         default:
487             assert(0);
488             return "_unk";
489     }
490 
491     switch (eglParameters.deviceType)
492     {
493         case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE:
494             strstr << "_null";
495             break;
496         case EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE:
497             strstr << "_swiftshader";
498             break;
499         default:
500             break;
501     }
502 
503     return strstr.str();
504 }
505 
story() const506 std::string RenderTestParams::story() const
507 {
508     switch (surfaceType)
509     {
510         case SurfaceType::Window:
511             return "";
512         case SurfaceType::WindowWithVSync:
513             return "_vsync";
514         case SurfaceType::Offscreen:
515             return "_offscreen";
516         default:
517             UNREACHABLE();
518             return "";
519     }
520 }
521 
backendAndStory() const522 std::string RenderTestParams::backendAndStory() const
523 {
524     return backend() + story();
525 }
526 
ANGLERenderTest(const std::string & name,const RenderTestParams & testParams,const char * units)527 ANGLERenderTest::ANGLERenderTest(const std::string &name,
528                                  const RenderTestParams &testParams,
529                                  const char *units)
530     : ANGLEPerfTest(name,
531                     testParams.backend(),
532                     testParams.story(),
533                     OneFrame() ? 1 : testParams.iterationsPerStep,
534                     units),
535       mTestParams(testParams),
536       mIsTimestampQueryAvailable(false),
537       mGLWindow(nullptr),
538       mOSWindow(nullptr),
539       mSwapEnabled(true)
540 {
541     // Force fast tests to make sure our slowest bots don't time out.
542     if (OneFrame())
543     {
544         const_cast<RenderTestParams &>(testParams).iterationsPerStep = 1;
545     }
546 
547     // Try to ensure we don't trigger allocation during execution.
548     mTraceEventBuffer.reserve(kInitialTraceEventBufferSize);
549 
550     switch (testParams.driver)
551     {
552         case angle::GLESDriverType::AngleEGL:
553             mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
554             mEntryPointsLib.reset(angle::OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME,
555                                                            angle::SearchType::ApplicationDir));
556             break;
557         case angle::GLESDriverType::SystemEGL:
558 #if defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
559             mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
560             mEntryPointsLib.reset(
561                 angle::OpenSharedLibraryWithExtension(GetNativeEGLLibraryNameWithExtension()));
562 #else
563             std::cerr << "Not implemented." << std::endl;
564             mSkipTest = true;
565 #endif  // defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
566             break;
567         case angle::GLESDriverType::SystemWGL:
568 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
569             mGLWindow = WGLWindow::New(testParams.majorVersion, testParams.minorVersion);
570             mEntryPointsLib.reset(
571                 angle::OpenSharedLibrary("opengl32", angle::SearchType::SystemDir));
572 #else
573             std::cout << "WGL driver not available. Skipping test." << std::endl;
574             mSkipTest = true;
575 #endif  // defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
576             break;
577         default:
578             std::cerr << "Error in switch." << std::endl;
579             mSkipTest = true;
580             break;
581     }
582 }
583 
~ANGLERenderTest()584 ANGLERenderTest::~ANGLERenderTest()
585 {
586     OSWindow::Delete(&mOSWindow);
587     GLWindowBase::Delete(&mGLWindow);
588 }
589 
addExtensionPrerequisite(const char * extensionName)590 void ANGLERenderTest::addExtensionPrerequisite(const char *extensionName)
591 {
592     mExtensionPrerequisites.push_back(extensionName);
593 }
594 
SetUp()595 void ANGLERenderTest::SetUp()
596 {
597     if (mSkipTest)
598     {
599         return;
600     }
601 
602     ANGLEPerfTest::SetUp();
603 
604     // Set a consistent CPU core affinity and high priority.
605     angle::StabilizeCPUForBenchmarking();
606 
607     mOSWindow = OSWindow::New();
608 
609     if (!mGLWindow)
610     {
611         mSkipTest = true;
612         return;
613     }
614 
615     mPlatformMethods.overrideWorkaroundsD3D      = OverrideWorkaroundsD3D;
616     mPlatformMethods.logError                    = CustomLogError;
617     mPlatformMethods.logWarning                  = EmptyPlatformMethod;
618     mPlatformMethods.logInfo                     = EmptyPlatformMethod;
619     mPlatformMethods.addTraceEvent               = AddPerfTraceEvent;
620     mPlatformMethods.getTraceCategoryEnabledFlag = GetPerfTraceCategoryEnabled;
621     mPlatformMethods.updateTraceEventDuration    = UpdateTraceEventDuration;
622     mPlatformMethods.monotonicallyIncreasingTime = MonotonicallyIncreasingTime;
623     mPlatformMethods.context                     = this;
624 
625     if (!mOSWindow->initialize(mName, mTestParams.windowWidth, mTestParams.windowHeight))
626     {
627         mSkipTest = true;
628         FAIL() << "Failed initializing OSWindow";
629         // FAIL returns.
630     }
631 
632     // Override platform method parameter.
633     EGLPlatformParameters withMethods = mTestParams.eglParameters;
634     withMethods.platformMethods       = &mPlatformMethods;
635 
636     // Request a common framebuffer config
637     mConfigParams.redBits     = 8;
638     mConfigParams.greenBits   = 8;
639     mConfigParams.blueBits    = 8;
640     mConfigParams.alphaBits   = 8;
641     mConfigParams.depthBits   = 24;
642     mConfigParams.stencilBits = 8;
643 
644     if (!mGLWindow->initializeGL(mOSWindow, mEntryPointsLib.get(), mTestParams.driver, withMethods,
645                                  mConfigParams))
646     {
647         mSkipTest = true;
648         FAIL() << "Failed initializing GL Window";
649         // FAIL returns.
650     }
651 
652     // Disable vsync.
653     if (mTestParams.surfaceType != SurfaceType::WindowWithVSync)
654     {
655         if (!mGLWindow->setSwapInterval(0))
656         {
657             mSkipTest = true;
658             FAIL() << "Failed setting swap interval";
659             // FAIL returns.
660         }
661     }
662 
663     mIsTimestampQueryAvailable = IsGLExtensionEnabled("GL_EXT_disjoint_timer_query");
664 
665     if (!areExtensionPrerequisitesFulfilled())
666     {
667         mSkipTest = true;
668     }
669 
670     if (mSkipTest)
671     {
672         return;
673     }
674 
675 #if defined(ANGLE_ENABLE_ASSERTS)
676     if (IsGLExtensionEnabled("GL_KHR_debug"))
677     {
678         EnableDebugCallback(this);
679     }
680 #endif
681 
682     initializeBenchmark();
683 
684     if (mTestParams.iterationsPerStep == 0)
685     {
686         mSkipTest = true;
687         FAIL() << "Please initialize 'iterationsPerStep'.";
688         // FAIL returns.
689     }
690 
691     mTestTrialResults.reserve(gTestTrials);
692 
693     // Capture a screenshot if enabled.
694     if (gScreenShotDir != nullptr)
695     {
696         std::stringstream screenshotNameStr;
697         screenshotNameStr << gScreenShotDir << GetPathSeparator() << "angle" << mBackend << "_"
698                           << mStory << ".png";
699         std::string screenshotName = screenshotNameStr.str();
700         saveScreenshot(screenshotName);
701     }
702 
703     for (int loopIndex = 0; loopIndex < gWarmupLoops; ++loopIndex)
704     {
705         doRunLoop(gCalibrationTimeSeconds, std::numeric_limits<int>::max(),
706                   RunLoopPolicy::FinishEveryStep);
707         if (gVerboseLogging)
708         {
709             printf("Warm-up loop took %.2lf seconds.\n", mTimer.getElapsedTime());
710         }
711     }
712 
713     if (mStepsToRun <= 0)
714     {
715         calibrateStepsToRun();
716     }
717 }
718 
TearDown()719 void ANGLERenderTest::TearDown()
720 {
721     if (!mSkipTest)
722     {
723         destroyBenchmark();
724     }
725 
726     if (mGLWindow)
727     {
728         mGLWindow->destroyGL();
729         mGLWindow = nullptr;
730     }
731 
732     if (mOSWindow)
733     {
734         mOSWindow->destroy();
735         mOSWindow = nullptr;
736     }
737 
738     // Dump trace events to json file.
739     if (gEnableTrace)
740     {
741         DumpTraceEventsToJSONFile(mTraceEventBuffer, gTraceFile);
742     }
743 
744     ANGLEPerfTest::TearDown();
745 }
746 
beginInternalTraceEvent(const char * name)747 void ANGLERenderTest::beginInternalTraceEvent(const char *name)
748 {
749     if (gEnableTrace)
750     {
751         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[0].name, name,
752                                        MonotonicallyIncreasingTime(&mPlatformMethods),
753                                        getCurrentThreadSerial());
754     }
755 }
756 
endInternalTraceEvent(const char * name)757 void ANGLERenderTest::endInternalTraceEvent(const char *name)
758 {
759     if (gEnableTrace)
760     {
761         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[0].name, name,
762                                        MonotonicallyIncreasingTime(&mPlatformMethods),
763                                        getCurrentThreadSerial());
764     }
765 }
766 
beginGLTraceEvent(const char * name,double hostTimeSec)767 void ANGLERenderTest::beginGLTraceEvent(const char *name, double hostTimeSec)
768 {
769     if (gEnableTrace)
770     {
771         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[1].name, name,
772                                        hostTimeSec, getCurrentThreadSerial());
773     }
774 }
775 
endGLTraceEvent(const char * name,double hostTimeSec)776 void ANGLERenderTest::endGLTraceEvent(const char *name, double hostTimeSec)
777 {
778     if (gEnableTrace)
779     {
780         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[1].name, name,
781                                        hostTimeSec, getCurrentThreadSerial());
782     }
783 }
784 
step()785 void ANGLERenderTest::step()
786 {
787     beginInternalTraceEvent("step");
788 
789     // Clear events that the application did not process from this frame
790     Event event;
791     bool closed = false;
792     while (popEvent(&event))
793     {
794         // If the application did not catch a close event, close now
795         if (event.Type == Event::EVENT_CLOSED)
796         {
797             closed = true;
798         }
799     }
800 
801     if (closed)
802     {
803         abortTest();
804     }
805     else
806     {
807         drawBenchmark();
808 
809         // Swap is needed so that the GPU driver will occasionally flush its
810         // internal command queue to the GPU. This is enabled for null back-end
811         // devices because some back-ends (e.g. Vulkan) also accumulate internal
812         // command queues.
813         if (mSwapEnabled)
814         {
815             mGLWindow->swap();
816         }
817         mOSWindow->messageLoop();
818 
819 #if defined(ANGLE_ENABLE_ASSERTS)
820         EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
821 #endif  // defined(ANGLE_ENABLE_ASSERTS)
822     }
823 
824     endInternalTraceEvent("step");
825 }
826 
startGpuTimer()827 void ANGLERenderTest::startGpuTimer()
828 {
829     if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
830     {
831         glGenQueriesEXT(1, &mCurrentTimestampBeginQuery);
832         glQueryCounterEXT(mCurrentTimestampBeginQuery, GL_TIMESTAMP_EXT);
833     }
834 }
835 
stopGpuTimer()836 void ANGLERenderTest::stopGpuTimer()
837 {
838     if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
839     {
840         GLuint endQuery = 0;
841         glGenQueriesEXT(1, &endQuery);
842         glQueryCounterEXT(endQuery, GL_TIMESTAMP_EXT);
843         mTimestampQueries.push_back({mCurrentTimestampBeginQuery, endQuery});
844     }
845 }
846 
computeGPUTime()847 void ANGLERenderTest::computeGPUTime()
848 {
849     if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
850     {
851         for (const TimestampSample &sample : mTimestampQueries)
852         {
853             uint64_t beginGLTimeNs = 0;
854             uint64_t endGLTimeNs   = 0;
855             glGetQueryObjectui64vEXT(sample.beginQuery, GL_QUERY_RESULT_EXT, &beginGLTimeNs);
856             glGetQueryObjectui64vEXT(sample.endQuery, GL_QUERY_RESULT_EXT, &endGLTimeNs);
857             glDeleteQueriesEXT(1, &sample.beginQuery);
858             glDeleteQueriesEXT(1, &sample.endQuery);
859             mGPUTimeNs += endGLTimeNs - beginGLTimeNs;
860         }
861 
862         mTimestampQueries.clear();
863     }
864 }
865 
startTest()866 void ANGLERenderTest::startTest() {}
867 
finishTest()868 void ANGLERenderTest::finishTest()
869 {
870     if (mTestParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE &&
871         !gNoFinish)
872     {
873         glFinish();
874     }
875 }
876 
popEvent(Event * event)877 bool ANGLERenderTest::popEvent(Event *event)
878 {
879     return mOSWindow->popEvent(event);
880 }
881 
getWindow()882 OSWindow *ANGLERenderTest::getWindow()
883 {
884     return mOSWindow;
885 }
886 
getGLWindow()887 GLWindowBase *ANGLERenderTest::getGLWindow()
888 {
889     return mGLWindow;
890 }
891 
areExtensionPrerequisitesFulfilled() const892 bool ANGLERenderTest::areExtensionPrerequisitesFulfilled() const
893 {
894     for (const char *extension : mExtensionPrerequisites)
895     {
896         if (!CheckExtensionExists(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
897                                   extension))
898         {
899             std::cout << "Test skipped due to missing extension: " << extension << std::endl;
900             return false;
901         }
902     }
903     return true;
904 }
905 
setWebGLCompatibilityEnabled(bool webglCompatibility)906 void ANGLERenderTest::setWebGLCompatibilityEnabled(bool webglCompatibility)
907 {
908     mConfigParams.webGLCompatibility = webglCompatibility;
909 }
910 
setRobustResourceInit(bool enabled)911 void ANGLERenderTest::setRobustResourceInit(bool enabled)
912 {
913     mConfigParams.robustResourceInit = enabled;
914 }
915 
getTraceEventBuffer()916 std::vector<TraceEvent> &ANGLERenderTest::getTraceEventBuffer()
917 {
918     return mTraceEventBuffer;
919 }
920 
onErrorMessage(const char * errorMessage)921 void ANGLERenderTest::onErrorMessage(const char *errorMessage)
922 {
923     abortTest();
924     FAIL() << "Failing test because of unexpected internal ANGLE error:\n" << errorMessage << "\n";
925 }
926 
getCurrentThreadSerial()927 uint32_t ANGLERenderTest::getCurrentThreadSerial()
928 {
929     std::thread::id id = std::this_thread::get_id();
930 
931     for (uint32_t serial = 0; serial < static_cast<uint32_t>(mThreadIDs.size()); ++serial)
932     {
933         if (mThreadIDs[serial] == id)
934         {
935             return serial + 1;
936         }
937     }
938 
939     mThreadIDs.push_back(id);
940     return static_cast<uint32_t>(mThreadIDs.size());
941 }
942 
943 namespace angle
944 {
GetHostTimeSeconds()945 double GetHostTimeSeconds()
946 {
947     // Move the time origin to the first call to this function, to avoid generating unnecessarily
948     // large timestamps.
949     static double origin = angle::GetCurrentTime();
950     return angle::GetCurrentTime() - origin;
951 }
952 }  // namespace angle
953