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