• 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/mathutil.h"
15 #include "common/platform.h"
16 #include "common/string_utils.h"
17 #include "common/system_utils.h"
18 #include "common/utilities.h"
19 #include "test_utils/runner/TestSuite.h"
20 #include "third_party/perf/perf_test.h"
21 #include "third_party/trace_event/trace_event.h"
22 #include "util/shader_utils.h"
23 #include "util/test_utils.h"
24 
25 #include <cassert>
26 #include <cmath>
27 #include <fstream>
28 #include <iostream>
29 #include <numeric>
30 #include <sstream>
31 
32 #include <rapidjson/document.h>
33 #include <rapidjson/filewritestream.h>
34 #include <rapidjson/istreamwrapper.h>
35 #include <rapidjson/prettywriter.h>
36 
37 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
38 #    include "util/windows/WGLWindow.h"
39 #endif  // defined(ANGLE_USE_UTIL_LOADER) &&defined(ANGLE_PLATFORM_WINDOWS)
40 
41 using namespace angle;
42 namespace js = rapidjson;
43 
44 namespace
45 {
46 constexpr size_t kInitialTraceEventBufferSize = 50000;
47 constexpr double kMilliSecondsPerSecond       = 1e3;
48 constexpr double kMicroSecondsPerSecond       = 1e6;
49 constexpr double kNanoSecondsPerSecond        = 1e9;
50 constexpr char kPeakMemoryMetric[]            = ".memory_max";
51 constexpr char kMedianMemoryMetric[]          = ".memory_median";
52 
53 struct TraceCategory
54 {
55     unsigned char enabled;
56     const char *name;
57 };
58 
59 constexpr TraceCategory gTraceCategories[2] = {
60     {1, "gpu.angle"},
61     {1, "gpu.angle.gpu"},
62 };
63 
EmptyPlatformMethod(PlatformMethods *,const char *)64 void EmptyPlatformMethod(PlatformMethods *, const char *) {}
65 
CustomLogError(PlatformMethods * platform,const char * errorMessage)66 void CustomLogError(PlatformMethods *platform, const char *errorMessage)
67 {
68     auto *angleRenderTest = static_cast<ANGLERenderTest *>(platform->context);
69     angleRenderTest->onErrorMessage(errorMessage);
70 }
71 
AddPerfTraceEvent(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)72 TraceEventHandle AddPerfTraceEvent(PlatformMethods *platform,
73                                    char phase,
74                                    const unsigned char *categoryEnabledFlag,
75                                    const char *name,
76                                    unsigned long long id,
77                                    double timestamp,
78                                    int numArgs,
79                                    const char **argNames,
80                                    const unsigned char *argTypes,
81                                    const unsigned long long *argValues,
82                                    unsigned char flags)
83 {
84     if (!gEnableTrace)
85         return 0;
86 
87     // Discover the category name based on categoryEnabledFlag.  This flag comes from the first
88     // parameter of TraceCategory, and corresponds to one of the entries in gTraceCategories.
89     static_assert(offsetof(TraceCategory, enabled) == 0,
90                   "|enabled| must be the first field of the TraceCategory class.");
91     const TraceCategory *category = reinterpret_cast<const TraceCategory *>(categoryEnabledFlag);
92 
93     ANGLERenderTest *renderTest = static_cast<ANGLERenderTest *>(platform->context);
94 
95     std::lock_guard<std::mutex> lock(renderTest->getTraceEventMutex());
96 
97     uint32_t tid = renderTest->getCurrentThreadSerial();
98 
99     std::vector<TraceEvent> &buffer = renderTest->getTraceEventBuffer();
100     buffer.emplace_back(phase, category->name, name, timestamp, tid);
101     return buffer.size();
102 }
103 
GetPerfTraceCategoryEnabled(PlatformMethods * platform,const char * categoryName)104 const unsigned char *GetPerfTraceCategoryEnabled(PlatformMethods *platform,
105                                                  const char *categoryName)
106 {
107     if (gEnableTrace)
108     {
109         for (const TraceCategory &category : gTraceCategories)
110         {
111             if (strcmp(category.name, categoryName) == 0)
112             {
113                 return &category.enabled;
114             }
115         }
116     }
117 
118     constexpr static unsigned char kZero = 0;
119     return &kZero;
120 }
121 
UpdateTraceEventDuration(PlatformMethods * platform,const unsigned char * categoryEnabledFlag,const char * name,TraceEventHandle eventHandle)122 void UpdateTraceEventDuration(PlatformMethods *platform,
123                               const unsigned char *categoryEnabledFlag,
124                               const char *name,
125                               TraceEventHandle eventHandle)
126 {
127     // Not implemented.
128 }
129 
MonotonicallyIncreasingTime(PlatformMethods * platform)130 double MonotonicallyIncreasingTime(PlatformMethods *platform)
131 {
132     return GetHostTimeSeconds();
133 }
134 
WriteJsonFile(const std::string & outputFile,js::Document * doc)135 bool WriteJsonFile(const std::string &outputFile, js::Document *doc)
136 {
137     FILE *fp = fopen(outputFile.c_str(), "w");
138     if (!fp)
139     {
140         return false;
141     }
142 
143     constexpr size_t kBufferSize = 0xFFFF;
144     std::vector<char> writeBuffer(kBufferSize);
145     js::FileWriteStream os(fp, writeBuffer.data(), kBufferSize);
146     js::PrettyWriter<js::FileWriteStream> writer(os);
147     if (!doc->Accept(writer))
148     {
149         fclose(fp);
150         return false;
151     }
152     fclose(fp);
153     return true;
154 }
155 
DumpTraceEventsToJSONFile(const std::vector<TraceEvent> & traceEvents,const char * outputFileName)156 void DumpTraceEventsToJSONFile(const std::vector<TraceEvent> &traceEvents,
157                                const char *outputFileName)
158 {
159     js::Document doc(js::kObjectType);
160     js::Document::AllocatorType &allocator = doc.GetAllocator();
161 
162     js::Value events(js::kArrayType);
163 
164     for (const TraceEvent &traceEvent : traceEvents)
165     {
166         js::Value value(js::kObjectType);
167 
168         const uint64_t microseconds = static_cast<uint64_t>(traceEvent.timestamp * 1000.0 * 1000.0);
169 
170         js::Document::StringRefType eventName(traceEvent.name);
171         js::Document::StringRefType categoryName(traceEvent.categoryName);
172         js::Document::StringRefType pidName(
173             strcmp(traceEvent.categoryName, "gpu.angle.gpu") == 0 ? "GPU" : "ANGLE");
174 
175         value.AddMember("name", eventName, allocator);
176         value.AddMember("cat", categoryName, allocator);
177         value.AddMember("ph", std::string(1, traceEvent.phase), allocator);
178         value.AddMember("ts", microseconds, allocator);
179         value.AddMember("pid", pidName, allocator);
180         value.AddMember("tid", traceEvent.tid, allocator);
181 
182         events.PushBack(value, allocator);
183     }
184 
185     doc.AddMember("traceEvents", events, allocator);
186 
187     if (WriteJsonFile(outputFileName, &doc))
188     {
189         printf("Wrote trace file to %s\n", outputFileName);
190     }
191     else
192     {
193         printf("Error writing trace file to %s\n", outputFileName);
194     }
195 }
196 
PerfTestDebugCallback(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar * message,const void * userParam)197 ANGLE_MAYBE_UNUSED void KHRONOS_APIENTRY PerfTestDebugCallback(GLenum source,
198                                                                GLenum type,
199                                                                GLuint id,
200                                                                GLenum severity,
201                                                                GLsizei length,
202                                                                const GLchar *message,
203                                                                const void *userParam)
204 {
205     // Early exit on non-errors.
206     if (type != GL_DEBUG_TYPE_ERROR || !userParam)
207     {
208         return;
209     }
210 
211     ANGLERenderTest *renderTest =
212         const_cast<ANGLERenderTest *>(reinterpret_cast<const ANGLERenderTest *>(userParam));
213     renderTest->onErrorMessage(message);
214 }
215 
ComputeMean(const std::vector<double> & values)216 double ComputeMean(const std::vector<double> &values)
217 {
218     double sum = std::accumulate(values.begin(), values.end(), 0.0);
219 
220     double mean = sum / static_cast<double>(values.size());
221     return mean;
222 }
223 }  // anonymous namespace
224 
TraceEvent(char phaseIn,const char * categoryNameIn,const char * nameIn,double timestampIn,uint32_t tidIn)225 TraceEvent::TraceEvent(char phaseIn,
226                        const char *categoryNameIn,
227                        const char *nameIn,
228                        double timestampIn,
229                        uint32_t tidIn)
230     : phase(phaseIn), categoryName(categoryNameIn), name{}, timestamp(timestampIn), tid(tidIn)
231 {
232     ASSERT(strlen(nameIn) < kMaxNameLen);
233     strcpy(name, nameIn);
234 }
235 
ANGLEPerfTest(const std::string & name,const std::string & backend,const std::string & story,unsigned int iterationsPerStep,const char * units)236 ANGLEPerfTest::ANGLEPerfTest(const std::string &name,
237                              const std::string &backend,
238                              const std::string &story,
239                              unsigned int iterationsPerStep,
240                              const char *units)
241     : mName(name),
242       mBackend(backend),
243       mStory(story),
244       mGPUTimeNs(0),
245       mSkipTest(false),
246       mStepsToRun(std::max(gStepsPerTrial, gMaxStepsPerformed)),
247       mTrialNumStepsPerformed(0),
248       mTotalNumStepsPerformed(0),
249       mIterationsPerStep(iterationsPerStep),
250       mRunning(true)
251 {
252     if (mStory == "")
253     {
254         mStory = "baseline_story";
255     }
256     if (mStory[0] == '_')
257     {
258         mStory = mStory.substr(1);
259     }
260     mReporter = std::make_unique<perf_test::PerfResultReporter>(mName + mBackend, mStory);
261     mReporter->RegisterImportantMetric(".wall_time", units);
262     mReporter->RegisterImportantMetric(".cpu_time", units);
263     mReporter->RegisterImportantMetric(".gpu_time", units);
264     mReporter->RegisterFyiMetric(".trial_steps", "count");
265     mReporter->RegisterFyiMetric(".total_steps", "count");
266     mReporter->RegisterFyiMetric(".steps_to_run", "count");
267 }
268 
~ANGLEPerfTest()269 ANGLEPerfTest::~ANGLEPerfTest() {}
270 
run()271 void ANGLEPerfTest::run()
272 {
273     if (mSkipTest)
274     {
275         GTEST_SKIP() << mSkipTestReason;
276         // GTEST_SKIP returns.
277     }
278 
279     if (mStepsToRun <= 0)
280     {
281         // We don't call finish between calibration steps when calibrating non-Render tests. The
282         // Render tests will have already calibrated when this code is run.
283         calibrateStepsToRun(RunLoopPolicy::RunContinuously);
284         ASSERT(mStepsToRun > 0);
285     }
286 
287     uint32_t numTrials = OneFrame() ? 1 : gTestTrials;
288     if (gVerboseLogging)
289     {
290         printf("Test Trials: %d\n", static_cast<int>(numTrials));
291     }
292 
293     for (uint32_t trial = 0; trial < numTrials; ++trial)
294     {
295         doRunLoop(gMaxTrialTimeSeconds, mStepsToRun, RunLoopPolicy::RunContinuously);
296         processResults();
297         if (gVerboseLogging)
298         {
299             double trialTime = mTimer.getElapsedWallClockTime();
300             printf("Trial %d time: %.2lf seconds.\n", trial + 1, trialTime);
301 
302             double secondsPerStep      = trialTime / static_cast<double>(mTrialNumStepsPerformed);
303             double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
304             mTestTrialResults.push_back(secondsPerIteration * 1000.0);
305         }
306     }
307 
308     if (gVerboseLogging && !mTestTrialResults.empty())
309     {
310         double numResults = static_cast<double>(mTestTrialResults.size());
311         double mean       = ComputeMean(mTestTrialResults);
312 
313         double variance = 0;
314         for (double trialResult : mTestTrialResults)
315         {
316             double difference = trialResult - mean;
317             variance += difference * difference;
318         }
319         variance /= numResults;
320 
321         double standardDeviation      = std::sqrt(variance);
322         double coefficientOfVariation = standardDeviation / mean;
323 
324         if (mean < 0.001)
325         {
326             printf("Mean result time: %.4lf ns.\n", mean * 1000.0);
327         }
328         else
329         {
330             printf("Mean result time: %.4lf ms.\n", mean);
331         }
332         printf("Coefficient of variation: %.2lf%%\n", coefficientOfVariation * 100.0);
333     }
334 }
335 
doRunLoop(double maxRunTime,int maxStepsToRun,RunLoopPolicy runPolicy)336 void ANGLEPerfTest::doRunLoop(double maxRunTime, int maxStepsToRun, RunLoopPolicy runPolicy)
337 {
338     mTrialNumStepsPerformed = 0;
339     mRunning                = true;
340     mGPUTimeNs              = 0;
341     mTimer.start();
342     startTest();
343 
344     while (mRunning)
345     {
346         if (gMaxStepsPerformed > 0 && mTotalNumStepsPerformed >= gMaxStepsPerformed)
347         {
348             if (gVerboseLogging)
349             {
350                 printf("Stopping test after %d steps.\n", mTotalNumStepsPerformed);
351             }
352             mRunning = false;
353         }
354         else if (mTimer.getElapsedWallClockTime() > maxRunTime)
355         {
356             mRunning = false;
357         }
358         else if (mTrialNumStepsPerformed >= maxStepsToRun)
359         {
360             mRunning = false;
361         }
362         else
363         {
364             step();
365 
366             if (runPolicy == RunLoopPolicy::FinishEveryStep)
367             {
368                 glFinish();
369             }
370 
371             if (mRunning)
372             {
373                 mTrialNumStepsPerformed++;
374                 mTotalNumStepsPerformed++;
375             }
376         }
377     }
378     finishTest();
379     mTimer.stop();
380     computeGPUTime();
381 }
382 
SetUp()383 void ANGLEPerfTest::SetUp() {}
384 
TearDown()385 void ANGLEPerfTest::TearDown() {}
386 
processResults()387 void ANGLEPerfTest::processResults()
388 {
389     processClockResult(".cpu_time", mTimer.getElapsedCpuTime());
390     processClockResult(".wall_time", mTimer.getElapsedWallClockTime());
391 
392     if (mGPUTimeNs > 0)
393     {
394         processClockResult(".gpu_time", mGPUTimeNs * 1e-9);
395     }
396 
397     if (gVerboseLogging)
398     {
399         double fps = static_cast<double>(mTrialNumStepsPerformed * mIterationsPerStep) /
400                      mTimer.getElapsedWallClockTime();
401         printf("Ran %0.2lf iterations per second\n", fps);
402     }
403 
404     if (gCalibration)
405     {
406         mReporter->AddResult(".steps_to_run", static_cast<size_t>(mStepsToRun));
407     }
408     else
409     {
410         mReporter->AddResult(".trial_steps", static_cast<size_t>(mTrialNumStepsPerformed));
411         mReporter->AddResult(".total_steps", static_cast<size_t>(mTotalNumStepsPerformed));
412     }
413 
414     if (!mProcessMemoryUsageKBSamples.empty())
415     {
416         std::sort(mProcessMemoryUsageKBSamples.begin(), mProcessMemoryUsageKBSamples.end());
417 
418         // Compute median.
419         size_t medianIndex      = mProcessMemoryUsageKBSamples.size() / 2;
420         uint64_t medianMemoryKB = mProcessMemoryUsageKBSamples[medianIndex];
421         auto peakMemoryIterator = std::max_element(mProcessMemoryUsageKBSamples.begin(),
422                                                    mProcessMemoryUsageKBSamples.end());
423         uint64_t peakMemoryKB   = *peakMemoryIterator;
424 
425         processMemoryResult(kMedianMemoryMetric, medianMemoryKB);
426         processMemoryResult(kPeakMemoryMetric, peakMemoryKB);
427     }
428 
429     for (const auto &iter : mPerfCounterInfo)
430     {
431         const std::string &counterName = iter.second.name;
432         std::vector<GLuint> samples    = iter.second.samples;
433 
434         // Median
435         {
436             size_t midpoint = samples.size() >> 1;
437             std::nth_element(samples.begin(), samples.begin() + midpoint, samples.end());
438 
439             std::stringstream medianStr;
440             medianStr << "." << counterName << "_median";
441             std::string medianName = medianStr.str();
442 
443             mReporter->AddResult(medianName, static_cast<size_t>(samples[midpoint]));
444 
445             std::string measurement = mName + mBackend + "." + counterName + "_median";
446             TestSuite::GetInstance()->addHistogramSample(measurement, mStory, samples[midpoint],
447                                                          "count");
448         }
449 
450         // Maximum
451         {
452             const auto &maxIt = std::max_element(samples.begin(), samples.end());
453 
454             std::stringstream maxStr;
455             maxStr << "." << counterName << "_max";
456             std::string maxName = maxStr.str();
457             mReporter->AddResult(maxName, static_cast<size_t>(*maxIt));
458 
459             std::string measurement = mName + mBackend + "." + counterName + "_max";
460             TestSuite::GetInstance()->addHistogramSample(measurement, mStory, *maxIt, "count");
461         }
462 
463         // Sum
464         {
465             GLuint sum = std::accumulate(samples.begin(), samples.end(), 0);
466 
467             std::stringstream sumStr;
468             sumStr << "." << counterName << "_sum";
469             std::string sumName = sumStr.str();
470             mReporter->AddResult(sumName, static_cast<size_t>(sum));
471 
472             std::string measurement = mName + mBackend + "." + counterName + "_sum";
473             TestSuite::GetInstance()->addHistogramSample(measurement, mStory, sum, "count");
474         }
475     }
476 }
477 
processClockResult(const char * metric,double resultSeconds)478 void ANGLEPerfTest::processClockResult(const char *metric, double resultSeconds)
479 {
480     double secondsPerStep      = resultSeconds / static_cast<double>(mTrialNumStepsPerformed);
481     double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
482 
483     perf_test::MetricInfo metricInfo;
484     std::string units;
485     bool foundMetric = mReporter->GetMetricInfo(metric, &metricInfo);
486     if (!foundMetric)
487     {
488         fprintf(stderr, "Error getting metric info for %s.\n", metric);
489         return;
490     }
491     units = metricInfo.units;
492 
493     double result;
494 
495     if (units == "ms")
496     {
497         result = secondsPerIteration * kMilliSecondsPerSecond;
498     }
499     else if (units == "us")
500     {
501         result = secondsPerIteration * kMicroSecondsPerSecond;
502     }
503     else
504     {
505         result = secondsPerIteration * kNanoSecondsPerSecond;
506     }
507     mReporter->AddResult(metric, result);
508 
509     // Output histogram JSON set format if enabled.
510     TestSuite::GetInstance()->addHistogramSample(mName + mBackend + metric, mStory,
511                                                  secondsPerIteration * kMilliSecondsPerSecond,
512                                                  "msBestFitFormat_smallerIsBetter");
513 }
514 
processMemoryResult(const char * metric,uint64_t resultKB)515 void ANGLEPerfTest::processMemoryResult(const char *metric, uint64_t resultKB)
516 {
517     perf_test::MetricInfo metricInfo;
518     if (!mReporter->GetMetricInfo(metric, &metricInfo))
519     {
520         mReporter->RegisterImportantMetric(metric, "sizeInBytes");
521     }
522 
523     mReporter->AddResult(metric, static_cast<size_t>(resultKB * 1000));
524 
525     TestSuite::GetInstance()->addHistogramSample(mName + mBackend + metric, mStory,
526                                                  static_cast<double>(resultKB) * 1000.0,
527                                                  "sizeInBytes_smallerIsBetter");
528 }
529 
normalizedTime(size_t value) const530 double ANGLEPerfTest::normalizedTime(size_t value) const
531 {
532     return static_cast<double>(value) / static_cast<double>(mTrialNumStepsPerformed);
533 }
534 
calibrateStepsToRun(RunLoopPolicy policy)535 void ANGLEPerfTest::calibrateStepsToRun(RunLoopPolicy policy)
536 {
537     // Run initially for "gCalibrationTimeSeconds" using the run loop policy.
538     doRunLoop(gCalibrationTimeSeconds, std::numeric_limits<int>::max(), policy);
539 
540     double elapsedTime = mTimer.getElapsedWallClockTime();
541     int stepsPerformed = mTrialNumStepsPerformed;
542 
543     double scale   = gCalibrationTimeSeconds / elapsedTime;
544     int stepsToRun = static_cast<int>(static_cast<double>(stepsPerformed) * scale);
545     stepsToRun     = std::max(1, stepsPerformed);
546     if (getStepAlignment() != 1)
547     {
548         stepsToRun = rx::roundUp(stepsToRun, getStepAlignment());
549     }
550 
551     // The run loop policy "FinishEveryStep" indicates we're running GPU tests. GPU work
552     // completes asynchronously from the issued CPU steps. Therefore we need to call
553     // glFinish before we can compute an accurate time elapsed by the test.
554     //
555     // To compute an accurate value for "mStepsToRun" we do a two-pass calibration. The
556     // first pass runs for "gCalibrationTimeSeconds" and calls glFinish every step. The
557     // estimated steps to run using this method is very inaccurate but is guaranteed to
558     // complete in a fixed amount of time. Using that estimate we then run a second pass
559     // and call glFinish a single time after "mStepsToRun" steps. We can then use the
560     // "actual" time elapsed to compute an accurate estimate for "mStepsToRun".
561 
562     if (policy == RunLoopPolicy::FinishEveryStep)
563     {
564         for (int loopIndex = 0; loopIndex < gWarmupLoops; ++loopIndex)
565         {
566             doRunLoop(gMaxTrialTimeSeconds, stepsToRun, RunLoopPolicy::RunContinuously);
567 
568             // Compute mean of the calibration results.
569             double sampleElapsedTime = mTimer.getElapsedWallClockTime();
570             int sampleStepsPerformed = mTrialNumStepsPerformed;
571 
572             if (gVerboseLogging)
573             {
574                 printf("Calibration loop took %.2lf seconds, with %d steps.\n", sampleElapsedTime,
575                        sampleStepsPerformed);
576             }
577 
578             // Scale steps down according to the time that exceeded one second.
579             double sampleScale = gCalibrationTimeSeconds / sampleElapsedTime;
580             stepsToRun = static_cast<int>(static_cast<double>(sampleStepsPerformed) * sampleScale);
581             stepsToRun = std::max(1, stepsToRun);
582             if (getStepAlignment() != 1)
583             {
584                 stepsToRun = rx::roundUp(stepsToRun, getStepAlignment());
585             }
586         }
587     }
588 
589     // Scale steps down according to the time that exceeded one second.
590     mStepsToRun = stepsToRun;
591 
592     if (gVerboseLogging)
593     {
594         printf("Running %d steps after calibration.", mStepsToRun);
595     }
596 
597     // Calibration allows the perf test runner script to save some time.
598     if (gCalibration)
599     {
600         processResults();
601         return;
602     }
603 }
604 
getStepAlignment() const605 int ANGLEPerfTest::getStepAlignment() const
606 {
607     // Default: No special alignment rules.
608     return 1;
609 }
610 
backend() const611 std::string RenderTestParams::backend() const
612 {
613     std::stringstream strstr;
614 
615     switch (driver)
616     {
617         case GLESDriverType::AngleEGL:
618             break;
619         case GLESDriverType::SystemWGL:
620         case GLESDriverType::SystemEGL:
621             strstr << "_native";
622             break;
623         default:
624             assert(0);
625             return "_unk";
626     }
627 
628     switch (getRenderer())
629     {
630         case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
631             break;
632         case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE:
633             strstr << "_d3d11";
634             break;
635         case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE:
636             strstr << "_d3d9";
637             break;
638         case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
639             strstr << "_gl";
640             break;
641         case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
642             strstr << "_gles";
643             break;
644         case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
645             strstr << "_vulkan";
646             break;
647         default:
648             assert(0);
649             return "_unk";
650     }
651 
652     switch (eglParameters.deviceType)
653     {
654         case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE:
655             strstr << "_null";
656             break;
657         case EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE:
658             strstr << "_swiftshader";
659             break;
660         default:
661             break;
662     }
663 
664     return strstr.str();
665 }
666 
story() const667 std::string RenderTestParams::story() const
668 {
669     switch (surfaceType)
670     {
671         case SurfaceType::Window:
672             return "";
673         case SurfaceType::WindowWithVSync:
674             return "_vsync";
675         case SurfaceType::Offscreen:
676             return "_offscreen";
677         default:
678             UNREACHABLE();
679             return "";
680     }
681 }
682 
backendAndStory() const683 std::string RenderTestParams::backendAndStory() const
684 {
685     return backend() + story();
686 }
687 
ANGLERenderTest(const std::string & name,const RenderTestParams & testParams,const char * units)688 ANGLERenderTest::ANGLERenderTest(const std::string &name,
689                                  const RenderTestParams &testParams,
690                                  const char *units)
691     : ANGLEPerfTest(name,
692                     testParams.backend(),
693                     testParams.story(),
694                     OneFrame() ? 1 : testParams.iterationsPerStep,
695                     units),
696       mTestParams(testParams),
697       mIsTimestampQueryAvailable(false),
698       mGLWindow(nullptr),
699       mOSWindow(nullptr),
700       mSwapEnabled(true)
701 {
702     // Force fast tests to make sure our slowest bots don't time out.
703     if (OneFrame())
704     {
705         const_cast<RenderTestParams &>(testParams).iterationsPerStep = 1;
706     }
707 
708     // Try to ensure we don't trigger allocation during execution.
709     mTraceEventBuffer.reserve(kInitialTraceEventBufferSize);
710 
711     switch (testParams.driver)
712     {
713         case GLESDriverType::AngleEGL:
714             mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
715             mEntryPointsLib.reset(OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME, SearchType::ModuleDir));
716             break;
717         case GLESDriverType::SystemEGL:
718 #if defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
719             mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
720             mEntryPointsLib.reset(OpenSharedLibraryWithExtension(
721                 GetNativeEGLLibraryNameWithExtension(), SearchType::SystemDir));
722 #else
723             skipTest("Not implemented.");
724 #endif  // defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
725             break;
726         case GLESDriverType::SystemWGL:
727 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
728             mGLWindow = WGLWindow::New(testParams.majorVersion, testParams.minorVersion);
729             mEntryPointsLib.reset(OpenSharedLibrary("opengl32", SearchType::SystemDir));
730 #else
731             skipTest("WGL driver not available.");
732 #endif  // defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
733             break;
734         default:
735             skipTest("Error in switch.");
736             break;
737     }
738 }
739 
~ANGLERenderTest()740 ANGLERenderTest::~ANGLERenderTest()
741 {
742     OSWindow::Delete(&mOSWindow);
743     GLWindowBase::Delete(&mGLWindow);
744 }
745 
addExtensionPrerequisite(const char * extensionName)746 void ANGLERenderTest::addExtensionPrerequisite(const char *extensionName)
747 {
748     mExtensionPrerequisites.push_back(extensionName);
749 }
750 
SetUp()751 void ANGLERenderTest::SetUp()
752 {
753     if (mSkipTest)
754     {
755         return;
756     }
757 
758     ANGLEPerfTest::SetUp();
759 
760     // Set a consistent CPU core affinity and high priority.
761     StabilizeCPUForBenchmarking();
762 
763     mOSWindow = OSWindow::New();
764 
765     if (!mGLWindow)
766     {
767         skipTest("!mGLWindow");
768         return;
769     }
770 
771     mPlatformMethods.logError                    = CustomLogError;
772     mPlatformMethods.logWarning                  = EmptyPlatformMethod;
773     mPlatformMethods.logInfo                     = EmptyPlatformMethod;
774     mPlatformMethods.addTraceEvent               = AddPerfTraceEvent;
775     mPlatformMethods.getTraceCategoryEnabledFlag = GetPerfTraceCategoryEnabled;
776     mPlatformMethods.updateTraceEventDuration    = UpdateTraceEventDuration;
777     mPlatformMethods.monotonicallyIncreasingTime = MonotonicallyIncreasingTime;
778     mPlatformMethods.context                     = this;
779 
780     if (!mOSWindow->initialize(mName, mTestParams.windowWidth, mTestParams.windowHeight))
781     {
782         failTest("Failed initializing OSWindow");
783         return;
784     }
785 
786     // Override platform method parameter.
787     EGLPlatformParameters withMethods = mTestParams.eglParameters;
788     withMethods.platformMethods       = &mPlatformMethods;
789 
790     // Request a common framebuffer config
791     mConfigParams.redBits     = 8;
792     mConfigParams.greenBits   = 8;
793     mConfigParams.blueBits    = 8;
794     mConfigParams.alphaBits   = 8;
795     mConfigParams.depthBits   = 24;
796     mConfigParams.stencilBits = 8;
797     mConfigParams.colorSpace  = mTestParams.colorSpace;
798     if (mTestParams.surfaceType != SurfaceType::WindowWithVSync)
799     {
800         mConfigParams.swapInterval = 0;
801     }
802 
803     GLWindowResult res = mGLWindow->initializeGLWithResult(
804         mOSWindow, mEntryPointsLib.get(), mTestParams.driver, withMethods, mConfigParams);
805     switch (res)
806     {
807         case GLWindowResult::NoColorspaceSupport:
808             skipTest("Missing support for color spaces.");
809             return;
810         case GLWindowResult::Error:
811             failTest("Failed initializing GL Window");
812             return;
813         default:
814             break;
815     }
816 
817     // Disable vsync (if not done by the window init).
818     if (mTestParams.surfaceType != SurfaceType::WindowWithVSync)
819     {
820         if (!mGLWindow->setSwapInterval(0))
821         {
822             failTest("Failed setting swap interval");
823             return;
824         }
825     }
826 
827     if (mTestParams.trackGpuTime)
828     {
829         mIsTimestampQueryAvailable = EnsureGLExtensionEnabled("GL_EXT_disjoint_timer_query");
830     }
831 
832     skipTestIfMissingExtensionPrerequisites();
833 
834     if (mSkipTest)
835     {
836         GTEST_SKIP() << mSkipTestReason;
837         // GTEST_SKIP returns.
838     }
839 
840 #if defined(ANGLE_ENABLE_ASSERTS)
841     if (IsGLExtensionEnabled("GL_KHR_debug"))
842     {
843         EnableDebugCallback(&PerfTestDebugCallback, this);
844     }
845 #endif
846 
847     initializeBenchmark();
848 
849     if (mSkipTest)
850     {
851         GTEST_SKIP() << mSkipTestReason;
852         // GTEST_SKIP returns.
853     }
854 
855     if (mTestParams.iterationsPerStep == 0)
856     {
857         failTest("Please initialize 'iterationsPerStep'.");
858         return;
859     }
860 
861     if (gVerboseLogging)
862     {
863         printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER));
864         printf("GL_VERSION: %s\n", glGetString(GL_VERSION));
865     }
866 
867     mTestTrialResults.reserve(gTestTrials);
868 
869     for (int loopIndex = 0; loopIndex < gWarmupLoops; ++loopIndex)
870     {
871         doRunLoop(gCalibrationTimeSeconds, gWarmupSteps, RunLoopPolicy::FinishEveryStep);
872         if (gVerboseLogging)
873         {
874             printf("Warm-up loop took %.2lf seconds.\n", mTimer.getElapsedWallClockTime());
875         }
876     }
877 
878     if (mStepsToRun <= 0)
879     {
880         // Ensure we always call Finish when calibrating Render tests. This completes our work
881         // between calibration measurements.
882         calibrateStepsToRun(RunLoopPolicy::FinishEveryStep);
883     }
884 
885     initPerfCounters();
886 }
887 
TearDown()888 void ANGLERenderTest::TearDown()
889 {
890     if (!mSkipTest)
891     {
892         destroyBenchmark();
893     }
894 
895     if (mGLWindow)
896     {
897         mGLWindow->destroyGL();
898         mGLWindow = nullptr;
899     }
900 
901     if (mOSWindow)
902     {
903         mOSWindow->destroy();
904         mOSWindow = nullptr;
905     }
906 
907     // Dump trace events to json file.
908     if (gEnableTrace)
909     {
910         DumpTraceEventsToJSONFile(mTraceEventBuffer, gTraceFile);
911     }
912 
913     ANGLEPerfTest::TearDown();
914 }
915 
initPerfCounters()916 void ANGLERenderTest::initPerfCounters()
917 {
918     if (!gPerfCounters)
919     {
920         return;
921     }
922 
923     if (!IsGLExtensionEnabled(kPerfMonitorExtensionName))
924     {
925         fprintf(stderr, "Cannot report perf metrics because %s is not available.\n",
926                 kPerfMonitorExtensionName);
927         return;
928     }
929 
930     CounterNameToIndexMap indexMap = BuildCounterNameToIndexMap();
931 
932     std::vector<std::string> counters =
933         angle::SplitString(gPerfCounters, ":", angle::WhitespaceHandling::TRIM_WHITESPACE,
934                            angle::SplitResult::SPLIT_WANT_NONEMPTY);
935     for (const std::string &counter : counters)
936     {
937         bool found = false;
938 
939         for (const auto &indexMapIter : indexMap)
940         {
941             const std::string &indexMapName = indexMapIter.first;
942             if (NamesMatchWithWildcard(counter.c_str(), indexMapName.c_str()))
943             {
944                 {
945                     std::stringstream medianStr;
946                     medianStr << '.' << indexMapName << "_median";
947                     std::string medianName = medianStr.str();
948                     mReporter->RegisterImportantMetric(medianName, "count");
949                 }
950 
951                 {
952                     std::stringstream maxStr;
953                     maxStr << '.' << indexMapName << "_max";
954                     std::string maxName = maxStr.str();
955                     mReporter->RegisterImportantMetric(maxName, "count");
956                 }
957 
958                 {
959                     std::stringstream sumStr;
960                     sumStr << '.' << indexMapName << "_sum";
961                     std::string sumName = sumStr.str();
962                     mReporter->RegisterImportantMetric(sumName, "count");
963                 }
964 
965                 GLuint index            = indexMapIter.second;
966                 mPerfCounterInfo[index] = {indexMapName, {}};
967 
968                 found = true;
969             }
970         }
971 
972         if (!found)
973         {
974             fprintf(stderr, "'%s' does not match any available perf counters.\n", counter.c_str());
975         }
976     }
977 }
978 
updatePerfCounters()979 void ANGLERenderTest::updatePerfCounters()
980 {
981     if (mPerfCounterInfo.empty())
982     {
983         return;
984     }
985 
986     std::vector<PerfMonitorTriplet> perfData = GetPerfMonitorTriplets();
987     ASSERT(!perfData.empty());
988 
989     for (auto &iter : mPerfCounterInfo)
990     {
991         uint32_t counter             = iter.first;
992         std::vector<GLuint> &samples = iter.second.samples;
993         samples.push_back(perfData[counter].value);
994     }
995 }
996 
beginInternalTraceEvent(const char * name)997 void ANGLERenderTest::beginInternalTraceEvent(const char *name)
998 {
999     if (gEnableTrace)
1000     {
1001         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[0].name, name,
1002                                        MonotonicallyIncreasingTime(&mPlatformMethods),
1003                                        getCurrentThreadSerial());
1004     }
1005 }
1006 
endInternalTraceEvent(const char * name)1007 void ANGLERenderTest::endInternalTraceEvent(const char *name)
1008 {
1009     if (gEnableTrace)
1010     {
1011         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[0].name, name,
1012                                        MonotonicallyIncreasingTime(&mPlatformMethods),
1013                                        getCurrentThreadSerial());
1014     }
1015 }
1016 
beginGLTraceEvent(const char * name,double hostTimeSec)1017 void ANGLERenderTest::beginGLTraceEvent(const char *name, double hostTimeSec)
1018 {
1019     if (gEnableTrace)
1020     {
1021         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[1].name, name,
1022                                        hostTimeSec, getCurrentThreadSerial());
1023     }
1024 }
1025 
endGLTraceEvent(const char * name,double hostTimeSec)1026 void ANGLERenderTest::endGLTraceEvent(const char *name, double hostTimeSec)
1027 {
1028     if (gEnableTrace)
1029     {
1030         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[1].name, name,
1031                                        hostTimeSec, getCurrentThreadSerial());
1032     }
1033 }
1034 
step()1035 void ANGLERenderTest::step()
1036 {
1037     beginInternalTraceEvent("step");
1038 
1039     // Clear events that the application did not process from this frame
1040     Event event;
1041     bool closed = false;
1042     while (popEvent(&event))
1043     {
1044         // If the application did not catch a close event, close now
1045         if (event.Type == Event::EVENT_CLOSED)
1046         {
1047             closed = true;
1048         }
1049     }
1050 
1051     if (closed)
1052     {
1053         abortTest();
1054     }
1055     else
1056     {
1057         drawBenchmark();
1058 
1059         // Swap is needed so that the GPU driver will occasionally flush its
1060         // internal command queue to the GPU. This is enabled for null back-end
1061         // devices because some back-ends (e.g. Vulkan) also accumulate internal
1062         // command queues.
1063         if (mSwapEnabled)
1064         {
1065             updatePerfCounters();
1066             mGLWindow->swap();
1067         }
1068         mOSWindow->messageLoop();
1069 
1070 #if defined(ANGLE_ENABLE_ASSERTS)
1071         if (!gRetraceMode)
1072         {
1073             EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
1074         }
1075 #endif  // defined(ANGLE_ENABLE_ASSERTS)
1076 
1077         // Sample system memory
1078         uint64_t processMemoryUsageKB = GetProcessMemoryUsageKB();
1079         if (processMemoryUsageKB)
1080         {
1081             mProcessMemoryUsageKBSamples.push_back(processMemoryUsageKB);
1082         }
1083     }
1084 
1085     endInternalTraceEvent("step");
1086 }
1087 
startGpuTimer()1088 void ANGLERenderTest::startGpuTimer()
1089 {
1090     if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
1091     {
1092         glGenQueriesEXT(1, &mCurrentTimestampBeginQuery);
1093         glQueryCounterEXT(mCurrentTimestampBeginQuery, GL_TIMESTAMP_EXT);
1094     }
1095 }
1096 
stopGpuTimer()1097 void ANGLERenderTest::stopGpuTimer()
1098 {
1099     if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
1100     {
1101         GLuint endQuery = 0;
1102         glGenQueriesEXT(1, &endQuery);
1103         glQueryCounterEXT(endQuery, GL_TIMESTAMP_EXT);
1104         mTimestampQueries.push_back({mCurrentTimestampBeginQuery, endQuery});
1105     }
1106 }
1107 
computeGPUTime()1108 void ANGLERenderTest::computeGPUTime()
1109 {
1110     if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
1111     {
1112         for (const TimestampSample &sample : mTimestampQueries)
1113         {
1114             uint64_t beginGLTimeNs = 0;
1115             uint64_t endGLTimeNs   = 0;
1116             glGetQueryObjectui64vEXT(sample.beginQuery, GL_QUERY_RESULT_EXT, &beginGLTimeNs);
1117             glGetQueryObjectui64vEXT(sample.endQuery, GL_QUERY_RESULT_EXT, &endGLTimeNs);
1118             glDeleteQueriesEXT(1, &sample.beginQuery);
1119             glDeleteQueriesEXT(1, &sample.endQuery);
1120             mGPUTimeNs += endGLTimeNs - beginGLTimeNs;
1121         }
1122 
1123         mTimestampQueries.clear();
1124     }
1125 }
1126 
startTest()1127 void ANGLERenderTest::startTest() {}
1128 
finishTest()1129 void ANGLERenderTest::finishTest()
1130 {
1131     if (mTestParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE &&
1132         !gNoFinish && !gRetraceMode)
1133     {
1134         glFinish();
1135     }
1136 }
1137 
popEvent(Event * event)1138 bool ANGLERenderTest::popEvent(Event *event)
1139 {
1140     return mOSWindow->popEvent(event);
1141 }
1142 
getWindow()1143 OSWindow *ANGLERenderTest::getWindow()
1144 {
1145     return mOSWindow;
1146 }
1147 
getGLWindow()1148 GLWindowBase *ANGLERenderTest::getGLWindow()
1149 {
1150     return mGLWindow;
1151 }
1152 
skipTestIfMissingExtensionPrerequisites()1153 void ANGLERenderTest::skipTestIfMissingExtensionPrerequisites()
1154 {
1155     for (const char *extension : mExtensionPrerequisites)
1156     {
1157         if (!CheckExtensionExists(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
1158                                   extension))
1159         {
1160             skipTest(std::string("Test skipped due to missing extension: ") + extension);
1161             return;
1162         }
1163     }
1164 }
1165 
setWebGLCompatibilityEnabled(bool webglCompatibility)1166 void ANGLERenderTest::setWebGLCompatibilityEnabled(bool webglCompatibility)
1167 {
1168     mConfigParams.webGLCompatibility = webglCompatibility;
1169 }
1170 
setRobustResourceInit(bool enabled)1171 void ANGLERenderTest::setRobustResourceInit(bool enabled)
1172 {
1173     mConfigParams.robustResourceInit = enabled;
1174 }
1175 
getTraceEventBuffer()1176 std::vector<TraceEvent> &ANGLERenderTest::getTraceEventBuffer()
1177 {
1178     return mTraceEventBuffer;
1179 }
1180 
onErrorMessage(const char * errorMessage)1181 void ANGLERenderTest::onErrorMessage(const char *errorMessage)
1182 {
1183     abortTest();
1184     std::ostringstream err;
1185     err << "Failing test because of unexpected error:\n" << errorMessage << "\n";
1186     failTest(err.str());
1187 }
1188 
getCurrentThreadSerial()1189 uint32_t ANGLERenderTest::getCurrentThreadSerial()
1190 {
1191     std::thread::id id = std::this_thread::get_id();
1192 
1193     for (uint32_t serial = 0; serial < static_cast<uint32_t>(mThreadIDs.size()); ++serial)
1194     {
1195         if (mThreadIDs[serial] == id)
1196         {
1197             return serial + 1;
1198         }
1199     }
1200 
1201     mThreadIDs.push_back(id);
1202     return static_cast<uint32_t>(mThreadIDs.size());
1203 }
1204 
1205 namespace angle
1206 {
GetHostTimeSeconds()1207 double GetHostTimeSeconds()
1208 {
1209     // Move the time origin to the first call to this function, to avoid generating unnecessarily
1210     // large timestamps.
1211     static double origin = GetCurrentSystemTime();
1212     return GetCurrentSystemTime() - origin;
1213 }
1214 }  // namespace angle
1215