• 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 #if defined(ANGLE_PLATFORM_ANDROID)
13 #    include <android/log.h>
14 #    include <dlfcn.h>
15 #endif
16 #include "ANGLEPerfTestArgs.h"
17 #include "common/base/anglebase/trace_event/trace_event.h"
18 #include "common/debug.h"
19 #include "common/gl_enum_utils.h"
20 #include "common/mathutil.h"
21 #include "common/platform.h"
22 #include "common/string_utils.h"
23 #include "common/system_utils.h"
24 #include "common/utilities.h"
25 #include "test_utils/runner/TestSuite.h"
26 #include "third_party/perf/perf_test.h"
27 #include "util/shader_utils.h"
28 #include "util/test_utils.h"
29 
30 #if defined(ANGLE_PLATFORM_ANDROID)
31 #    include "util/android/AndroidWindow.h"
32 #endif
33 
34 #include <cassert>
35 #include <cmath>
36 #include <fstream>
37 #include <iostream>
38 #include <numeric>
39 #include <sstream>
40 #include <string>
41 
42 #include <rapidjson/document.h>
43 #include <rapidjson/filewritestream.h>
44 #include <rapidjson/istreamwrapper.h>
45 #include <rapidjson/prettywriter.h>
46 
47 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
48 #    include "util/windows/WGLWindow.h"
49 #endif  // defined(ANGLE_USE_UTIL_LOADER) &&defined(ANGLE_PLATFORM_WINDOWS)
50 
51 using namespace angle;
52 namespace js = rapidjson;
53 
54 namespace
55 {
56 constexpr size_t kInitialTraceEventBufferSize            = 50000;
57 constexpr double kMilliSecondsPerSecond                  = 1e3;
58 constexpr double kMicroSecondsPerSecond                  = 1e6;
59 constexpr double kNanoSecondsPerSecond                   = 1e9;
60 constexpr size_t kNumberOfStepsPerformedToComputeGPUTime = 16;
61 constexpr char kPeakMemoryMetric[]                       = ".memory_max";
62 constexpr char kMedianMemoryMetric[]                     = ".memory_median";
63 
64 struct TraceCategory
65 {
66     unsigned char enabled;
67     const char *name;
68 };
69 
70 constexpr TraceCategory gTraceCategories[2] = {
71     {1, "gpu.angle"},
72     {1, "gpu.angle.gpu"},
73 };
74 
EmptyPlatformMethod(PlatformMethods *,const char *)75 void EmptyPlatformMethod(PlatformMethods *, const char *) {}
76 
CustomLogError(PlatformMethods * platform,const char * errorMessage)77 void CustomLogError(PlatformMethods *platform, const char *errorMessage)
78 {
79     auto *angleRenderTest = static_cast<ANGLERenderTest *>(platform->context);
80     angleRenderTest->onErrorMessage(errorMessage);
81 }
82 
AddPerfTraceEvent(PlatformMethods * platform,char phase,const unsigned char * categoryEnabledFlag,const char * name,unsigned long long id,double,int numArgs,const char ** argNames,const unsigned char * argTypes,const unsigned long long * argValues,unsigned char flags)83 TraceEventHandle AddPerfTraceEvent(PlatformMethods *platform,
84                                    char phase,
85                                    const unsigned char *categoryEnabledFlag,
86                                    const char *name,
87                                    unsigned long long id,
88                                    double /*timestamp*/,
89                                    int numArgs,
90                                    const char **argNames,
91                                    const unsigned char *argTypes,
92                                    const unsigned long long *argValues,
93                                    unsigned char flags)
94 {
95     if (!gEnableTrace)
96         return 0;
97 
98     // Discover the category name based on categoryEnabledFlag.  This flag comes from the first
99     // parameter of TraceCategory, and corresponds to one of the entries in gTraceCategories.
100     static_assert(offsetof(TraceCategory, enabled) == 0,
101                   "|enabled| must be the first field of the TraceCategory class.");
102     const TraceCategory *category = reinterpret_cast<const TraceCategory *>(categoryEnabledFlag);
103 
104     ANGLERenderTest *renderTest = static_cast<ANGLERenderTest *>(platform->context);
105 
106     std::lock_guard<std::mutex> lock(renderTest->getTraceEventMutex());
107 
108     uint32_t tid = renderTest->getCurrentThreadSerial();
109 
110     std::vector<TraceEvent> &buffer = renderTest->getTraceEventBuffer();
111     buffer.emplace_back(phase, category->name, name,
112                         platform->monotonicallyIncreasingTime(platform), tid);
113     return buffer.size();
114 }
115 
GetPerfTraceCategoryEnabled(PlatformMethods * platform,const char * categoryName)116 const unsigned char *GetPerfTraceCategoryEnabled(PlatformMethods *platform,
117                                                  const char *categoryName)
118 {
119     if (gEnableTrace)
120     {
121         for (const TraceCategory &category : gTraceCategories)
122         {
123             if (strcmp(category.name, categoryName) == 0)
124             {
125                 return &category.enabled;
126             }
127         }
128     }
129 
130     constexpr static unsigned char kZero = 0;
131     return &kZero;
132 }
133 
UpdateTraceEventDuration(PlatformMethods * platform,const unsigned char * categoryEnabledFlag,const char * name,TraceEventHandle eventHandle)134 void UpdateTraceEventDuration(PlatformMethods *platform,
135                               const unsigned char *categoryEnabledFlag,
136                               const char *name,
137                               TraceEventHandle eventHandle)
138 {
139     // Not implemented.
140 }
141 
MonotonicallyIncreasingTime(PlatformMethods * platform)142 double MonotonicallyIncreasingTime(PlatformMethods *platform)
143 {
144     return GetHostTimeSeconds();
145 }
146 
WriteJsonFile(const std::string & outputFile,js::Document * doc)147 bool WriteJsonFile(const std::string &outputFile, js::Document *doc)
148 {
149     FILE *fp = fopen(outputFile.c_str(), "w");
150     if (!fp)
151     {
152         return false;
153     }
154 
155     constexpr size_t kBufferSize = 0xFFFF;
156     std::vector<char> writeBuffer(kBufferSize);
157     js::FileWriteStream os(fp, writeBuffer.data(), kBufferSize);
158     js::PrettyWriter<js::FileWriteStream> writer(os);
159     if (!doc->Accept(writer))
160     {
161         fclose(fp);
162         return false;
163     }
164     fclose(fp);
165     return true;
166 }
167 
DumpTraceEventsToJSONFile(const std::vector<TraceEvent> & traceEvents,const char * outputFileName)168 void DumpTraceEventsToJSONFile(const std::vector<TraceEvent> &traceEvents,
169                                const char *outputFileName)
170 {
171     js::Document doc(js::kObjectType);
172     js::Document::AllocatorType &allocator = doc.GetAllocator();
173 
174     js::Value events(js::kArrayType);
175 
176     for (const TraceEvent &traceEvent : traceEvents)
177     {
178         js::Value value(js::kObjectType);
179 
180         const uint64_t microseconds = static_cast<uint64_t>(traceEvent.timestamp * 1000.0 * 1000.0);
181 
182         js::Document::StringRefType eventName(traceEvent.name);
183         js::Document::StringRefType categoryName(traceEvent.categoryName);
184         js::Document::StringRefType pidName(
185             strcmp(traceEvent.categoryName, "gpu.angle.gpu") == 0 ? "GPU" : "ANGLE");
186 
187         value.AddMember("name", eventName, allocator);
188         value.AddMember("cat", categoryName, allocator);
189         value.AddMember("ph", std::string(1, traceEvent.phase), allocator);
190         value.AddMember("ts", microseconds, allocator);
191         value.AddMember("pid", pidName, allocator);
192         value.AddMember("tid", traceEvent.tid, allocator);
193 
194         events.PushBack(value, allocator);
195     }
196 
197     doc.AddMember("traceEvents", events, allocator);
198 
199     if (WriteJsonFile(outputFileName, &doc))
200     {
201         printf("Wrote trace file to %s\n", outputFileName);
202     }
203     else
204     {
205         printf("Error writing trace file to %s\n", outputFileName);
206     }
207 }
208 
PerfTestDebugCallback(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar * message,const void * userParam)209 [[maybe_unused]] void KHRONOS_APIENTRY PerfTestDebugCallback(GLenum source,
210                                                              GLenum type,
211                                                              GLuint id,
212                                                              GLenum severity,
213                                                              GLsizei length,
214                                                              const GLchar *message,
215                                                              const void *userParam)
216 {
217     // Early exit on non-errors.
218     if (type != GL_DEBUG_TYPE_ERROR || !userParam)
219     {
220         return;
221     }
222 
223     ANGLERenderTest *renderTest =
224         const_cast<ANGLERenderTest *>(reinterpret_cast<const ANGLERenderTest *>(userParam));
225     renderTest->onErrorMessage(message);
226 }
227 
ComputeMean(const std::vector<double> & values)228 double ComputeMean(const std::vector<double> &values)
229 {
230     double sum = std::accumulate(values.begin(), values.end(), 0.0);
231 
232     double mean = sum / static_cast<double>(values.size());
233     return mean;
234 }
235 
FinishAndCheckForContextLoss()236 void FinishAndCheckForContextLoss()
237 {
238     glFinish();
239     if (glGetError() == GL_CONTEXT_LOST)
240     {
241         FAIL() << "Context lost";
242     }
243 }
244 
DumpFpsValues(const char * test,double mean_time)245 void DumpFpsValues(const char *test, double mean_time)
246 {
247 #if defined(ANGLE_PLATFORM_ANDROID)
248     std::ofstream fp(AndroidWindow::GetExternalStorageDirectory() + "/traces_fps.txt",
249                      std::ios::app);
250 #else
251     std::ofstream fp("traces_fps.txt", std::ios::app);
252 #endif
253     double fps_value = 1000 / mean_time;
254     fp << test << " " << fps_value << std::endl;
255     fp.close();
256 }
257 
258 #if defined(ANGLE_PLATFORM_ANDROID)
259 constexpr bool kHasATrace = true;
260 
261 void *gLibAndroid = nullptr;
262 bool (*gATraceIsEnabled)(void);
263 bool (*gATraceSetCounter)(const char *counterName, int64_t counterValue);
264 
SetupATrace()265 void SetupATrace()
266 {
267     if (gLibAndroid == nullptr)
268     {
269         gLibAndroid       = dlopen("libandroid.so", RTLD_NOW | RTLD_LOCAL);
270         gATraceIsEnabled  = (decltype(gATraceIsEnabled))dlsym(gLibAndroid, "ATrace_isEnabled");
271         gATraceSetCounter = (decltype(gATraceSetCounter))dlsym(gLibAndroid, "ATrace_setCounter");
272     }
273 }
274 
ATraceEnabled()275 bool ATraceEnabled()
276 {
277     return gATraceIsEnabled();
278 }
279 #else
280 constexpr bool kHasATrace = false;
SetupATrace()281 void SetupATrace() {}
ATraceEnabled()282 bool ATraceEnabled()
283 {
284     return false;
285 }
286 #endif
287 }  // anonymous namespace
288 
TraceEvent(char phaseIn,const char * categoryNameIn,const char * nameIn,double timestampIn,uint32_t tidIn)289 TraceEvent::TraceEvent(char phaseIn,
290                        const char *categoryNameIn,
291                        const char *nameIn,
292                        double timestampIn,
293                        uint32_t tidIn)
294     : phase(phaseIn), categoryName(categoryNameIn), name{}, timestamp(timestampIn), tid(tidIn)
295 {
296     ASSERT(strlen(nameIn) < kMaxNameLen);
297     strcpy(name, nameIn);
298 }
299 
ANGLEPerfTest(const std::string & name,const std::string & backend,const std::string & story,unsigned int iterationsPerStep,const char * units)300 ANGLEPerfTest::ANGLEPerfTest(const std::string &name,
301                              const std::string &backend,
302                              const std::string &story,
303                              unsigned int iterationsPerStep,
304                              const char *units)
305     : mName(name),
306       mBackend(backend),
307       mStory(story),
308       mGPUTimeNs(0),
309       mSkipTest(false),
310       mStepsToRun(std::max(gStepsPerTrial, gMaxStepsPerformed)),
311       mTrialNumStepsPerformed(0),
312       mTotalNumStepsPerformed(0),
313       mIterationsPerStep(iterationsPerStep),
314       mRunning(true),
315       mPerfMonitor(0)
316 {
317     if (mStory == "")
318     {
319         mStory = "baseline_story";
320     }
321     if (mStory[0] == '_')
322     {
323         mStory = mStory.substr(1);
324     }
325     mReporter = std::make_unique<perf_test::PerfResultReporter>(mName + mBackend, mStory);
326     mReporter->RegisterImportantMetric(".wall_time", units);
327     mReporter->RegisterImportantMetric(".cpu_time", units);
328     mReporter->RegisterImportantMetric(".gpu_time", units);
329     mReporter->RegisterFyiMetric(".trial_steps", "count");
330     mReporter->RegisterFyiMetric(".total_steps", "count");
331 
332     if (kHasATrace)
333     {
334         SetupATrace();
335     }
336 }
337 
~ANGLEPerfTest()338 ANGLEPerfTest::~ANGLEPerfTest() {}
339 
run()340 void ANGLEPerfTest::run()
341 {
342     printf("running test name: \"%s\", backend: \"%s\", story: \"%s\"\n", mName.c_str(),
343            mBackend.c_str(), mStory.c_str());
344 #if defined(ANGLE_PLATFORM_ANDROID)
345     __android_log_print(ANDROID_LOG_INFO, "ANGLE",
346                         "running test name: \"%s\", backend: \"%s\", story: \"%s\"", mName.c_str(),
347                         mBackend.c_str(), mStory.c_str());
348 #endif
349     if (mSkipTest)
350     {
351         GTEST_SKIP() << mSkipTestReason;
352         // GTEST_SKIP returns.
353     }
354 
355     uint32_t numTrials = OneFrame() ? 1 : gTestTrials;
356     if (gVerboseLogging)
357     {
358         printf("Test Trials: %d\n", static_cast<int>(numTrials));
359     }
360 
361     atraceCounter("TraceStage", 3);
362 
363     for (uint32_t trial = 0; trial < numTrials; ++trial)
364     {
365         runTrial(gTrialTimeSeconds, mStepsToRun, RunTrialPolicy::RunContinuously);
366         processResults();
367         if (gVerboseLogging)
368         {
369             double trialTime = mTrialTimer.getElapsedWallClockTime();
370             printf("Trial %d time: %.2lf seconds.\n", trial + 1, trialTime);
371 
372             double secondsPerStep      = trialTime / static_cast<double>(mTrialNumStepsPerformed);
373             double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
374             mTestTrialResults.push_back(secondsPerIteration * 1000.0);
375         }
376     }
377 
378     atraceCounter("TraceStage", 0);
379 
380     if (gVerboseLogging && !mTestTrialResults.empty())
381     {
382         double numResults = static_cast<double>(mTestTrialResults.size());
383         double mean       = ComputeMean(mTestTrialResults);
384 
385         double variance = 0;
386         for (double trialResult : mTestTrialResults)
387         {
388             double difference = trialResult - mean;
389             variance += difference * difference;
390         }
391         variance /= numResults;
392 
393         double standardDeviation      = std::sqrt(variance);
394         double coefficientOfVariation = standardDeviation / mean;
395 
396         if (mean < 0.001)
397         {
398             printf("Mean result time: %.4lf ns.\n", mean * 1000.0);
399         }
400         else
401         {
402             printf("Mean result time: %.4lf ms.\n", mean);
403         }
404 
405         if (kStandaloneBenchmark)
406         {
407             DumpFpsValues(mStory.c_str(), mean);
408         }
409 
410         printf("Coefficient of variation: %.2lf%%\n", coefficientOfVariation * 100.0);
411     }
412 }
413 
runTrial(double maxRunTime,int maxStepsToRun,RunTrialPolicy runPolicy)414 void ANGLEPerfTest::runTrial(double maxRunTime, int maxStepsToRun, RunTrialPolicy runPolicy)
415 {
416     mTrialNumStepsPerformed = 0;
417     mRunning                = true;
418     mGPUTimeNs              = 0;
419     int stepAlignment       = getStepAlignment();
420     mTrialTimer.start();
421     startTest();
422 
423     int loopStepsPerformed  = 0;
424     double lastLoopWallTime = 0;
425     while (mRunning)
426     {
427         // When ATrace enabled, track average frame time before the first frame of each trace loop.
428         if (ATraceEnabled() && stepAlignment > 1 && runPolicy == RunTrialPolicy::RunContinuously &&
429             mTrialNumStepsPerformed % stepAlignment == 0)
430         {
431             double wallTime = mTrialTimer.getElapsedWallClockTime();
432             if (loopStepsPerformed > 0)  // 0 at the first frame of the first loop
433             {
434                 int frameTimeAvgUs = int(1e6 * (wallTime - lastLoopWallTime) / loopStepsPerformed);
435                 atraceCounter("TraceLoopFrameTimeAvgUs", frameTimeAvgUs);
436                 loopStepsPerformed = 0;
437             }
438             lastLoopWallTime = wallTime;
439         }
440 
441         // Only stop on aligned steps or in a few special case modes
442         if (mTrialNumStepsPerformed % stepAlignment == 0 || gStepsPerTrial == 1 || gRunToKeyFrame ||
443             gMaxStepsPerformed != kDefaultMaxStepsPerformed)
444         {
445             if (gMaxStepsPerformed > 0 && mTotalNumStepsPerformed >= gMaxStepsPerformed)
446             {
447                 if (gVerboseLogging)
448                 {
449                     printf("Stopping test after %d total steps.\n", mTotalNumStepsPerformed);
450                 }
451                 mRunning = false;
452                 break;
453             }
454             if (mTrialTimer.getElapsedWallClockTime() > maxRunTime)
455             {
456                 if (gVerboseLogging)
457                 {
458                     printf("Stopping test after %.2lf seconds.\n",
459                            mTrialTimer.getElapsedWallClockTime());
460                 }
461                 mRunning = false;
462                 break;
463             }
464             if (mTrialNumStepsPerformed >= maxStepsToRun)
465             {
466                 if (gVerboseLogging)
467                 {
468                     printf("Stopping test after %d trial steps.\n", mTrialNumStepsPerformed);
469                 }
470                 mRunning = false;
471                 break;
472             }
473         }
474 
475         if (gFpsLimit)
476         {
477             double wantTime    = mTrialNumStepsPerformed / double(gFpsLimit);
478             double currentTime = mTrialTimer.getElapsedWallClockTime();
479             if (currentTime < wantTime)
480             {
481                 std::this_thread::sleep_for(std::chrono::duration<double>(wantTime - currentTime));
482             }
483         }
484         step();
485 
486         if (runPolicy == RunTrialPolicy::FinishEveryStep)
487         {
488             FinishAndCheckForContextLoss();
489         }
490 
491         if (mRunning)
492         {
493             mTrialNumStepsPerformed++;
494             mTotalNumStepsPerformed++;
495             loopStepsPerformed++;
496         }
497 
498         if ((mTotalNumStepsPerformed % kNumberOfStepsPerformedToComputeGPUTime) == 0)
499         {
500             computeGPUTime();
501         }
502     }
503 
504     if (runPolicy == RunTrialPolicy::RunContinuously)
505     {
506         atraceCounter("TraceLoopFrameTimeAvgUs", 0);
507     }
508     finishTest();
509     mTrialTimer.stop();
510     computeGPUTime();
511 }
512 
SetUp()513 void ANGLEPerfTest::SetUp()
514 {
515     if (gWarmup)
516     {
517         atraceCounter("TraceStage", 1);
518 
519         // Trace tests run with glFinish for a loop (getStepAlignment == frameCount).
520         int warmupSteps = getStepAlignment();
521         if (gVerboseLogging)
522         {
523             printf("Warmup: %d steps\n", warmupSteps);
524         }
525 
526         Timer warmupTimer;
527         warmupTimer.start();
528 
529         runTrial(gTrialTimeSeconds, warmupSteps, RunTrialPolicy::FinishEveryStep);
530 
531         if (warmupSteps > 1)  // trace tests only: getStepAlignment() is 1 otherwise
532         {
533             atraceCounter("TraceStage", 2);
534 
535             // Short traces (e.g. 10 frames) have some spikes after the first loop b/308975999
536             const double kMinWarmupTime = 1.5;
537             double remainingTime        = kMinWarmupTime - warmupTimer.getElapsedWallClockTime();
538             if (remainingTime > 0)
539             {
540                 printf("Warmup: Looping for remaining warmup time (%.2f seconds).\n",
541                        remainingTime);
542                 runTrial(remainingTime, std::numeric_limits<int>::max(),
543                          RunTrialPolicy::RunContinuouslyWarmup);
544             }
545         }
546 
547         if (gVerboseLogging)
548         {
549             printf("Warmup took %.2lf seconds.\n", warmupTimer.getElapsedWallClockTime());
550         }
551     }
552 }
553 
TearDown()554 void ANGLEPerfTest::TearDown() {}
555 
recordIntegerMetric(const char * metric,size_t value,const std::string & units)556 void ANGLEPerfTest::recordIntegerMetric(const char *metric, size_t value, const std::string &units)
557 {
558     // Prints "RESULT ..." to stdout
559     mReporter->AddResult(metric, value);
560 
561     // Saves results to file if enabled
562     TestSuite::GetMetricWriter().writeInfo(mName, mBackend, mStory, metric, units);
563     TestSuite::GetMetricWriter().writeIntegerValue(value);
564 }
565 
recordDoubleMetric(const char * metric,double value,const std::string & units)566 void ANGLEPerfTest::recordDoubleMetric(const char *metric, double value, const std::string &units)
567 {
568     // Prints "RESULT ..." to stdout
569     mReporter->AddResult(metric, value);
570 
571     // Saves results to file if enabled
572     TestSuite::GetMetricWriter().writeInfo(mName, mBackend, mStory, metric, units);
573     TestSuite::GetMetricWriter().writeDoubleValue(value);
574 }
575 
addHistogramSample(const char * metric,double value,const std::string & units)576 void ANGLEPerfTest::addHistogramSample(const char *metric, double value, const std::string &units)
577 {
578     std::string measurement = mName + mBackend + metric;
579     // Output histogram JSON set format if enabled.
580     TestSuite::GetInstance()->addHistogramSample(measurement, mStory, value, units);
581 }
582 
processResults()583 void ANGLEPerfTest::processResults()
584 {
585     processClockResult(".cpu_time", mTrialTimer.getElapsedCpuTime());
586     processClockResult(".wall_time", mTrialTimer.getElapsedWallClockTime());
587 
588     if (mGPUTimeNs > 0)
589     {
590         processClockResult(".gpu_time", mGPUTimeNs * 1e-9);
591     }
592 
593     if (gVerboseLogging)
594     {
595         double fps = static_cast<double>(mTrialNumStepsPerformed * mIterationsPerStep) /
596                      mTrialTimer.getElapsedWallClockTime();
597         printf("Ran %0.2lf iterations per second\n", fps);
598     }
599 
600     mReporter->AddResult(".trial_steps", static_cast<size_t>(mTrialNumStepsPerformed));
601     mReporter->AddResult(".total_steps", static_cast<size_t>(mTotalNumStepsPerformed));
602 
603     if (!mProcessMemoryUsageKBSamples.empty())
604     {
605         std::sort(mProcessMemoryUsageKBSamples.begin(), mProcessMemoryUsageKBSamples.end());
606 
607         // Compute median.
608         size_t medianIndex      = mProcessMemoryUsageKBSamples.size() / 2;
609         uint64_t medianMemoryKB = mProcessMemoryUsageKBSamples[medianIndex];
610         auto peakMemoryIterator = std::max_element(mProcessMemoryUsageKBSamples.begin(),
611                                                    mProcessMemoryUsageKBSamples.end());
612         uint64_t peakMemoryKB   = *peakMemoryIterator;
613 
614         processMemoryResult(kMedianMemoryMetric, medianMemoryKB);
615         processMemoryResult(kPeakMemoryMetric, peakMemoryKB);
616     }
617 
618     for (const auto &iter : mPerfCounterInfo)
619     {
620         const std::string &counterName = iter.second.name;
621         std::vector<GLuint64> samples  = iter.second.samples;
622 
623         // Median
624         {
625             size_t midpoint = samples.size() / 2;
626             std::nth_element(samples.begin(), samples.begin() + midpoint, samples.end());
627 
628             std::string medianName = "." + counterName + "_median";
629             recordIntegerMetric(medianName.c_str(), static_cast<size_t>(samples[midpoint]),
630                                 "count");
631             addHistogramSample(medianName.c_str(), static_cast<double>(samples[midpoint]), "count");
632         }
633 
634         // Maximum
635         {
636             const auto &maxIt = std::max_element(samples.begin(), samples.end());
637 
638             std::string maxName = "." + counterName + "_max";
639             recordIntegerMetric(maxName.c_str(), static_cast<size_t>(*maxIt), "count");
640             addHistogramSample(maxName.c_str(), static_cast<double>(*maxIt), "count");
641         }
642 
643         // Sum
644         {
645             GLuint64 sum =
646                 std::accumulate(samples.begin(), samples.end(), static_cast<GLuint64>(0));
647 
648             std::string sumName = "." + counterName + "_max";
649             recordIntegerMetric(sumName.c_str(), static_cast<size_t>(sum), "count");
650             addHistogramSample(sumName.c_str(), static_cast<double>(sum), "count");
651         }
652     }
653 }
654 
processClockResult(const char * metric,double resultSeconds)655 void ANGLEPerfTest::processClockResult(const char *metric, double resultSeconds)
656 {
657     double secondsPerStep      = resultSeconds / static_cast<double>(mTrialNumStepsPerformed);
658     double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
659 
660     perf_test::MetricInfo metricInfo;
661     std::string units;
662     bool foundMetric = mReporter->GetMetricInfo(metric, &metricInfo);
663     if (!foundMetric)
664     {
665         fprintf(stderr, "Error getting metric info for %s.\n", metric);
666         return;
667     }
668     units = metricInfo.units;
669 
670     double result;
671 
672     if (units == "ms")
673     {
674         result = secondsPerIteration * kMilliSecondsPerSecond;
675     }
676     else if (units == "us")
677     {
678         result = secondsPerIteration * kMicroSecondsPerSecond;
679     }
680     else
681     {
682         result = secondsPerIteration * kNanoSecondsPerSecond;
683     }
684     recordDoubleMetric(metric, result, units);
685     addHistogramSample(metric, secondsPerIteration * kMilliSecondsPerSecond,
686                        "msBestFitFormat_smallerIsBetter");
687 }
688 
processMemoryResult(const char * metric,uint64_t resultKB)689 void ANGLEPerfTest::processMemoryResult(const char *metric, uint64_t resultKB)
690 {
691     perf_test::MetricInfo metricInfo;
692     if (!mReporter->GetMetricInfo(metric, &metricInfo))
693     {
694         mReporter->RegisterImportantMetric(metric, "sizeInBytes");
695     }
696 
697     recordIntegerMetric(metric, static_cast<size_t>(resultKB * 1000), "sizeInBytes");
698     addHistogramSample(metric, static_cast<double>(resultKB) * 1000.0,
699                        "sizeInBytes_smallerIsBetter");
700 }
701 
normalizedTime(size_t value) const702 double ANGLEPerfTest::normalizedTime(size_t value) const
703 {
704     return static_cast<double>(value) / static_cast<double>(mTrialNumStepsPerformed);
705 }
706 
getStepAlignment() const707 int ANGLEPerfTest::getStepAlignment() const
708 {
709     // Default: No special alignment rules.
710     return 1;
711 }
712 
atraceCounter(const char * counterName,int64_t counterValue)713 void ANGLEPerfTest::atraceCounter(const char *counterName, int64_t counterValue)
714 {
715 #if defined(ANGLE_PLATFORM_ANDROID)
716     if (ATraceEnabled())
717     {
718         gATraceSetCounter(counterName, counterValue);
719     }
720 #endif
721 }
722 
RenderTestParams()723 RenderTestParams::RenderTestParams()
724 {
725 #if defined(ANGLE_DEBUG_LAYERS_ENABLED)
726     eglParameters.debugLayersEnabled = true;
727 #else
728     eglParameters.debugLayersEnabled = false;
729 #endif
730 }
731 
backend() const732 std::string RenderTestParams::backend() const
733 {
734     std::stringstream strstr;
735 
736     switch (driver)
737     {
738         case GLESDriverType::AngleEGL:
739             break;
740         case GLESDriverType::AngleVulkanSecondariesEGL:
741             strstr << "_vulkan_secondaries";
742             break;
743         case GLESDriverType::SystemWGL:
744         case GLESDriverType::SystemEGL:
745             strstr << "_native";
746             break;
747         case GLESDriverType::ZinkEGL:
748             strstr << "_zink";
749             break;
750         default:
751             assert(0);
752             return "_unk";
753     }
754 
755     switch (getRenderer())
756     {
757         case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
758             break;
759         case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE:
760             strstr << "_d3d11";
761             break;
762         case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE:
763             strstr << "_d3d9";
764             break;
765         case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
766             strstr << "_gl";
767             break;
768         case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
769             strstr << "_gles";
770             break;
771         case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
772             strstr << "_vulkan";
773             break;
774         case EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE:
775             strstr << "_metal";
776             break;
777         default:
778             assert(0);
779             return "_unk";
780     }
781 
782     switch (eglParameters.deviceType)
783     {
784         case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE:
785             strstr << "_null";
786             break;
787         case EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE:
788             strstr << "_swiftshader";
789             break;
790         default:
791             break;
792     }
793 
794     return strstr.str();
795 }
796 
story() const797 std::string RenderTestParams::story() const
798 {
799     std::stringstream strstr;
800 
801     switch (surfaceType)
802     {
803         case SurfaceType::Window:
804             break;
805         case SurfaceType::WindowWithVSync:
806             strstr << "_vsync";
807             break;
808         case SurfaceType::Offscreen:
809             strstr << "_offscreen";
810             break;
811         default:
812             UNREACHABLE();
813             return "";
814     }
815 
816     if (multisample)
817     {
818         strstr << "_" << samples << "_samples";
819     }
820 
821     return strstr.str();
822 }
823 
backendAndStory() const824 std::string RenderTestParams::backendAndStory() const
825 {
826     return backend() + story();
827 }
828 
ANGLERenderTest(const std::string & name,const RenderTestParams & testParams,const char * units)829 ANGLERenderTest::ANGLERenderTest(const std::string &name,
830                                  const RenderTestParams &testParams,
831                                  const char *units)
832     : ANGLEPerfTest(name,
833                     testParams.backend(),
834                     testParams.story(),
835                     OneFrame() ? 1 : testParams.iterationsPerStep,
836                     units),
837       mTestParams(testParams),
838       mIsTimestampQueryAvailable(false),
839       mGLWindow(nullptr),
840       mOSWindow(nullptr),
841       mSwapEnabled(true)
842 {
843     // Force fast tests to make sure our slowest bots don't time out.
844     if (OneFrame())
845     {
846         const_cast<RenderTestParams &>(testParams).iterationsPerStep = 1;
847     }
848 
849     // Try to ensure we don't trigger allocation during execution.
850     mTraceEventBuffer.reserve(kInitialTraceEventBufferSize);
851 
852     switch (testParams.driver)
853     {
854         case GLESDriverType::AngleEGL:
855             mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
856             mEntryPointsLib.reset(OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME, SearchType::ModuleDir));
857             break;
858         case GLESDriverType::AngleVulkanSecondariesEGL:
859             mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
860             mEntryPointsLib.reset(OpenSharedLibrary(ANGLE_VULKAN_SECONDARIES_EGL_LIBRARY_NAME,
861                                                     SearchType::ModuleDir));
862             break;
863         case GLESDriverType::SystemEGL:
864 #if defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
865             mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
866             mEntryPointsLib.reset(OpenSharedLibraryWithExtension(
867                 GetNativeEGLLibraryNameWithExtension(), SearchType::SystemDir));
868 #else
869             skipTest("Not implemented.");
870 #endif  // defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
871             break;
872         case GLESDriverType::SystemWGL:
873 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
874             mGLWindow = WGLWindow::New(testParams.majorVersion, testParams.minorVersion);
875             mEntryPointsLib.reset(OpenSharedLibrary("opengl32", SearchType::SystemDir));
876 #else
877             skipTest("WGL driver not available.");
878 #endif  // defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
879             break;
880         case GLESDriverType::ZinkEGL:
881             mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
882             mEntryPointsLib.reset(
883                 OpenSharedLibrary(ANGLE_MESA_EGL_LIBRARY_NAME, SearchType::ModuleDir));
884             break;
885         default:
886             skipTest("Error in switch.");
887             break;
888     }
889 }
890 
~ANGLERenderTest()891 ANGLERenderTest::~ANGLERenderTest()
892 {
893     OSWindow::Delete(&mOSWindow);
894     GLWindowBase::Delete(&mGLWindow);
895 }
896 
addExtensionPrerequisite(std::string extensionName)897 void ANGLERenderTest::addExtensionPrerequisite(std::string extensionName)
898 {
899     mExtensionPrerequisites.push_back(extensionName);
900 }
901 
addIntegerPrerequisite(GLenum target,int min)902 void ANGLERenderTest::addIntegerPrerequisite(GLenum target, int min)
903 {
904     mIntegerPrerequisites.push_back({target, min});
905 }
906 
SetUp()907 void ANGLERenderTest::SetUp()
908 {
909     if (mSkipTest)
910     {
911         return;
912     }
913 
914     // Set a consistent CPU core affinity and high priority.
915     StabilizeCPUForBenchmarking();
916 
917     mOSWindow = OSWindow::New();
918 
919     if (!mGLWindow)
920     {
921         skipTest("!mGLWindow");
922         return;
923     }
924 
925     mPlatformMethods.logError                    = CustomLogError;
926     mPlatformMethods.logWarning                  = EmptyPlatformMethod;
927     mPlatformMethods.logInfo                     = EmptyPlatformMethod;
928     mPlatformMethods.addTraceEvent               = AddPerfTraceEvent;
929     mPlatformMethods.getTraceCategoryEnabledFlag = GetPerfTraceCategoryEnabled;
930     mPlatformMethods.updateTraceEventDuration    = UpdateTraceEventDuration;
931     mPlatformMethods.monotonicallyIncreasingTime = MonotonicallyIncreasingTime;
932     mPlatformMethods.context                     = this;
933 
934     if (!mOSWindow->initialize(mName, mTestParams.windowWidth, mTestParams.windowHeight))
935     {
936         failTest("Failed initializing OSWindow");
937         return;
938     }
939 
940     // Override platform method parameter.
941     EGLPlatformParameters withMethods = mTestParams.eglParameters;
942     withMethods.platformMethods       = &mPlatformMethods;
943 
944     // Request a common framebuffer config
945     mConfigParams.redBits     = 8;
946     mConfigParams.greenBits   = 8;
947     mConfigParams.blueBits    = 8;
948     mConfigParams.alphaBits   = 8;
949     mConfigParams.depthBits   = 24;
950     mConfigParams.stencilBits = 8;
951     mConfigParams.colorSpace  = mTestParams.colorSpace;
952     mConfigParams.multisample = mTestParams.multisample;
953     mConfigParams.samples     = mTestParams.samples;
954     if (mTestParams.surfaceType != SurfaceType::WindowWithVSync)
955     {
956         mConfigParams.swapInterval = 0;
957     }
958 
959     if (gPrintExtensionsToFile != nullptr || gRequestedExtensions != nullptr)
960     {
961         mConfigParams.extensionsEnabled = false;
962     }
963 
964     GLWindowResult res = mGLWindow->initializeGLWithResult(
965         mOSWindow, mEntryPointsLib.get(), mTestParams.driver, withMethods, mConfigParams);
966     switch (res)
967     {
968         case GLWindowResult::NoColorspaceSupport:
969             skipTest("Missing support for color spaces.");
970             return;
971         case GLWindowResult::Error:
972             failTest("Failed initializing GL Window");
973             return;
974         default:
975             break;
976     }
977 
978     if (gPrintExtensionsToFile)
979     {
980         std::ofstream fout(gPrintExtensionsToFile);
981         if (fout.is_open())
982         {
983             int numExtensions = 0;
984             glGetIntegerv(GL_NUM_REQUESTABLE_EXTENSIONS_ANGLE, &numExtensions);
985             for (int ext = 0; ext < numExtensions; ext++)
986             {
987                 fout << glGetStringi(GL_REQUESTABLE_EXTENSIONS_ANGLE, ext) << std::endl;
988             }
989             fout.close();
990             std::stringstream statusString;
991             statusString << "Wrote out to file: " << gPrintExtensionsToFile;
992             skipTest(statusString.str());
993         }
994         else
995         {
996             std::stringstream failStr;
997             failStr << "Failed to open file: " << gPrintExtensionsToFile;
998             failTest(failStr.str());
999         }
1000         return;
1001     }
1002 
1003     if (gRequestedExtensions != nullptr)
1004     {
1005         std::istringstream ss{gRequestedExtensions};
1006         std::string ext;
1007         while (std::getline(ss, ext, ' '))
1008         {
1009             glRequestExtensionANGLE(ext.c_str());
1010         }
1011     }
1012 
1013     // Disable vsync (if not done by the window init).
1014     if (mTestParams.surfaceType != SurfaceType::WindowWithVSync)
1015     {
1016         if (!mGLWindow->setSwapInterval(0))
1017         {
1018             failTest("Failed setting swap interval");
1019             return;
1020         }
1021     }
1022 
1023     if (mTestParams.trackGpuTime)
1024     {
1025         mIsTimestampQueryAvailable = EnsureGLExtensionEnabled("GL_EXT_disjoint_timer_query");
1026     }
1027 
1028     skipTestIfMissingExtensionPrerequisites();
1029     skipTestIfFailsIntegerPrerequisite();
1030 
1031     if (mSkipTest)
1032     {
1033         GTEST_SKIP() << mSkipTestReason;
1034         // GTEST_SKIP returns.
1035     }
1036 
1037 #if defined(ANGLE_ENABLE_ASSERTS)
1038     if (IsGLExtensionEnabled("GL_KHR_debug") && mEnableDebugCallback)
1039     {
1040         EnableDebugCallback(&PerfTestDebugCallback, this);
1041     }
1042 #endif
1043 
1044     initializeBenchmark();
1045 
1046     if (mSkipTest)
1047     {
1048         GTEST_SKIP() << mSkipTestReason;
1049         // GTEST_SKIP returns.
1050     }
1051 
1052     if (mTestParams.iterationsPerStep == 0)
1053     {
1054         failTest("Please initialize 'iterationsPerStep'.");
1055         return;
1056     }
1057 
1058     if (gVerboseLogging)
1059     {
1060         printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER));
1061         printf("GL_VERSION: %s\n", glGetString(GL_VERSION));
1062     }
1063 
1064     mTestTrialResults.reserve(gTestTrials);
1065 
1066     // Runs warmup if enabled
1067     ANGLEPerfTest::SetUp();
1068 
1069     initPerfCounters();
1070 }
1071 
TearDown()1072 void ANGLERenderTest::TearDown()
1073 {
1074     ASSERT(mTimestampQueries.empty());
1075 
1076     if (!mPerfCounterInfo.empty())
1077     {
1078         glDeletePerfMonitorsAMD(1, &mPerfMonitor);
1079         mPerfMonitor = 0;
1080     }
1081 
1082     if (!mSkipTest)
1083     {
1084         destroyBenchmark();
1085     }
1086 
1087     if (mGLWindow)
1088     {
1089         mGLWindow->destroyGL();
1090         mGLWindow = nullptr;
1091     }
1092 
1093     if (mOSWindow)
1094     {
1095         mOSWindow->destroy();
1096         mOSWindow = nullptr;
1097     }
1098 
1099     // Dump trace events to json file.
1100     if (gEnableTrace)
1101     {
1102         DumpTraceEventsToJSONFile(mTraceEventBuffer, gTraceFile);
1103     }
1104 
1105     ANGLEPerfTest::TearDown();
1106 }
1107 
initPerfCounters()1108 void ANGLERenderTest::initPerfCounters()
1109 {
1110     if (!gPerfCounters)
1111     {
1112         return;
1113     }
1114 
1115     if (!IsGLExtensionEnabled(kPerfMonitorExtensionName))
1116     {
1117         fprintf(stderr, "Cannot report perf metrics because %s is not available.\n",
1118                 kPerfMonitorExtensionName);
1119         return;
1120     }
1121 
1122     CounterNameToIndexMap indexMap = BuildCounterNameToIndexMap();
1123 
1124     std::vector<std::string> counters =
1125         angle::SplitString(gPerfCounters, ":", angle::WhitespaceHandling::TRIM_WHITESPACE,
1126                            angle::SplitResult::SPLIT_WANT_NONEMPTY);
1127     for (const std::string &counter : counters)
1128     {
1129         bool found = false;
1130 
1131         for (const auto &indexMapIter : indexMap)
1132         {
1133             const std::string &indexMapName = indexMapIter.first;
1134             if (NamesMatchWithWildcard(counter.c_str(), indexMapName.c_str()))
1135             {
1136                 {
1137                     std::stringstream medianStr;
1138                     medianStr << '.' << indexMapName << "_median";
1139                     std::string medianName = medianStr.str();
1140                     mReporter->RegisterImportantMetric(medianName, "count");
1141                 }
1142 
1143                 {
1144                     std::stringstream maxStr;
1145                     maxStr << '.' << indexMapName << "_max";
1146                     std::string maxName = maxStr.str();
1147                     mReporter->RegisterImportantMetric(maxName, "count");
1148                 }
1149 
1150                 {
1151                     std::stringstream sumStr;
1152                     sumStr << '.' << indexMapName << "_sum";
1153                     std::string sumName = sumStr.str();
1154                     mReporter->RegisterImportantMetric(sumName, "count");
1155                 }
1156 
1157                 GLuint index            = indexMapIter.second;
1158                 mPerfCounterInfo[index] = {indexMapName, {}};
1159 
1160                 found = true;
1161             }
1162         }
1163 
1164         if (!found)
1165         {
1166             fprintf(stderr, "'%s' does not match any available perf counters.\n", counter.c_str());
1167         }
1168     }
1169 
1170     if (!mPerfCounterInfo.empty())
1171     {
1172         glGenPerfMonitorsAMD(1, &mPerfMonitor);
1173         // Note: technically, glSelectPerfMonitorCountersAMD should be used to select the counters,
1174         // but currently ANGLE always captures all counters.
1175     }
1176 }
1177 
updatePerfCounters()1178 void ANGLERenderTest::updatePerfCounters()
1179 {
1180     if (mPerfCounterInfo.empty())
1181     {
1182         return;
1183     }
1184 
1185     std::vector<PerfMonitorTriplet> perfData = GetPerfMonitorTriplets();
1186     ASSERT(!perfData.empty());
1187 
1188     for (auto &iter : mPerfCounterInfo)
1189     {
1190         uint32_t counter               = iter.first;
1191         std::vector<GLuint64> &samples = iter.second.samples;
1192         samples.push_back(perfData[counter].value);
1193     }
1194 }
1195 
beginInternalTraceEvent(const char * name)1196 void ANGLERenderTest::beginInternalTraceEvent(const char *name)
1197 {
1198     if (gEnableTrace)
1199     {
1200         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[0].name, name,
1201                                        MonotonicallyIncreasingTime(&mPlatformMethods),
1202                                        getCurrentThreadSerial());
1203     }
1204 }
1205 
endInternalTraceEvent(const char * name)1206 void ANGLERenderTest::endInternalTraceEvent(const char *name)
1207 {
1208     if (gEnableTrace)
1209     {
1210         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[0].name, name,
1211                                        MonotonicallyIncreasingTime(&mPlatformMethods),
1212                                        getCurrentThreadSerial());
1213     }
1214 }
1215 
beginGLTraceEvent(const char * name,double hostTimeSec)1216 void ANGLERenderTest::beginGLTraceEvent(const char *name, double hostTimeSec)
1217 {
1218     if (gEnableTrace)
1219     {
1220         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[1].name, name,
1221                                        hostTimeSec, getCurrentThreadSerial());
1222     }
1223 }
1224 
endGLTraceEvent(const char * name,double hostTimeSec)1225 void ANGLERenderTest::endGLTraceEvent(const char *name, double hostTimeSec)
1226 {
1227     if (gEnableTrace)
1228     {
1229         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[1].name, name,
1230                                        hostTimeSec, getCurrentThreadSerial());
1231     }
1232 }
1233 
step()1234 void ANGLERenderTest::step()
1235 {
1236     beginInternalTraceEvent("step");
1237 
1238     // Clear events that the application did not process from this frame
1239     Event event;
1240     bool closed = false;
1241     while (popEvent(&event))
1242     {
1243         // If the application did not catch a close event, close now
1244         if (event.Type == Event::EVENT_CLOSED)
1245         {
1246             closed = true;
1247         }
1248     }
1249 
1250     if (closed)
1251     {
1252         abortTest();
1253     }
1254     else
1255     {
1256         drawBenchmark();
1257 
1258         // Swap is needed so that the GPU driver will occasionally flush its
1259         // internal command queue to the GPU. This is enabled for null back-end
1260         // devices because some back-ends (e.g. Vulkan) also accumulate internal
1261         // command queues.
1262         if (mSwapEnabled)
1263         {
1264             updatePerfCounters();
1265             mGLWindow->swap();
1266         }
1267         mOSWindow->messageLoop();
1268 
1269 #if defined(ANGLE_ENABLE_ASSERTS)
1270         if (!gRetraceMode)
1271         {
1272             EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
1273         }
1274 #endif  // defined(ANGLE_ENABLE_ASSERTS)
1275 
1276         // Sample system memory
1277         uint64_t processMemoryUsageKB = GetProcessMemoryUsageKB();
1278         if (processMemoryUsageKB)
1279         {
1280             mProcessMemoryUsageKBSamples.push_back(processMemoryUsageKB);
1281         }
1282     }
1283 
1284     endInternalTraceEvent("step");
1285 }
1286 
startGpuTimer()1287 void ANGLERenderTest::startGpuTimer()
1288 {
1289     if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
1290     {
1291         glGenQueriesEXT(1, &mCurrentTimestampBeginQuery);
1292         glQueryCounterEXT(mCurrentTimestampBeginQuery, GL_TIMESTAMP_EXT);
1293     }
1294 }
1295 
stopGpuTimer()1296 void ANGLERenderTest::stopGpuTimer()
1297 {
1298     if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
1299     {
1300         GLuint endQuery = 0;
1301         glGenQueriesEXT(1, &endQuery);
1302         glQueryCounterEXT(endQuery, GL_TIMESTAMP_EXT);
1303         mTimestampQueries.push({mCurrentTimestampBeginQuery, endQuery});
1304     }
1305 }
1306 
computeGPUTime()1307 void ANGLERenderTest::computeGPUTime()
1308 {
1309     if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
1310     {
1311         while (!mTimestampQueries.empty())
1312         {
1313             const TimestampSample &sample = mTimestampQueries.front();
1314             GLuint available              = GL_FALSE;
1315             glGetQueryObjectuivEXT(sample.endQuery, GL_QUERY_RESULT_AVAILABLE_EXT, &available);
1316             if (available != GL_TRUE)
1317             {
1318                 // query is not completed yet, bail out
1319                 break;
1320             }
1321 
1322             // frame's begin query must also completed.
1323             glGetQueryObjectuivEXT(sample.beginQuery, GL_QUERY_RESULT_AVAILABLE_EXT, &available);
1324             ASSERT(available == GL_TRUE);
1325 
1326             // Retrieve query result
1327             uint64_t beginGLTimeNs = 0;
1328             uint64_t endGLTimeNs   = 0;
1329             glGetQueryObjectui64vEXT(sample.beginQuery, GL_QUERY_RESULT_EXT, &beginGLTimeNs);
1330             glGetQueryObjectui64vEXT(sample.endQuery, GL_QUERY_RESULT_EXT, &endGLTimeNs);
1331             glDeleteQueriesEXT(1, &sample.beginQuery);
1332             glDeleteQueriesEXT(1, &sample.endQuery);
1333             mTimestampQueries.pop();
1334 
1335             // compute GPU time
1336             mGPUTimeNs += endGLTimeNs - beginGLTimeNs;
1337         }
1338     }
1339 }
1340 
startTest()1341 void ANGLERenderTest::startTest()
1342 {
1343     if (!mPerfCounterInfo.empty())
1344     {
1345         glBeginPerfMonitorAMD(mPerfMonitor);
1346     }
1347 }
1348 
finishTest()1349 void ANGLERenderTest::finishTest()
1350 {
1351     if (mTestParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE &&
1352         !gNoFinish && !gRetraceMode)
1353     {
1354         FinishAndCheckForContextLoss();
1355     }
1356 
1357     if (!mPerfCounterInfo.empty())
1358     {
1359         glEndPerfMonitorAMD(mPerfMonitor);
1360     }
1361 }
1362 
popEvent(Event * event)1363 bool ANGLERenderTest::popEvent(Event *event)
1364 {
1365     return mOSWindow->popEvent(event);
1366 }
1367 
getWindow()1368 OSWindow *ANGLERenderTest::getWindow()
1369 {
1370     return mOSWindow;
1371 }
1372 
getGLWindow()1373 GLWindowBase *ANGLERenderTest::getGLWindow()
1374 {
1375     return mGLWindow;
1376 }
1377 
skipTestIfMissingExtensionPrerequisites()1378 void ANGLERenderTest::skipTestIfMissingExtensionPrerequisites()
1379 {
1380     for (std::string extension : mExtensionPrerequisites)
1381     {
1382         if (!CheckExtensionExists(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
1383                                   extension))
1384         {
1385             skipTest(std::string("Test skipped due to missing extension: ") + extension);
1386             return;
1387         }
1388     }
1389 }
1390 
skipTestIfFailsIntegerPrerequisite()1391 void ANGLERenderTest::skipTestIfFailsIntegerPrerequisite()
1392 {
1393     for (const auto [target, minRequired] : mIntegerPrerequisites)
1394     {
1395         GLint driverValue;
1396         glGetIntegerv(target, &driverValue);
1397         if (static_cast<int>(driverValue) < minRequired)
1398         {
1399             std::stringstream ss;
1400             ss << "Test skipped due to value (" << std::to_string(static_cast<int>(driverValue))
1401                << ") being less than the prerequisite minimum (" << std::to_string(minRequired)
1402                << ") for GL constant " << gl::GLenumToString(gl::GLESEnum::AllEnums, target);
1403             skipTest(ss.str());
1404         }
1405     }
1406 }
1407 
setWebGLCompatibilityEnabled(bool webglCompatibility)1408 void ANGLERenderTest::setWebGLCompatibilityEnabled(bool webglCompatibility)
1409 {
1410     mConfigParams.webGLCompatibility = webglCompatibility;
1411 }
1412 
setRobustResourceInit(bool enabled)1413 void ANGLERenderTest::setRobustResourceInit(bool enabled)
1414 {
1415     mConfigParams.robustResourceInit = enabled;
1416 }
1417 
getTraceEventBuffer()1418 std::vector<TraceEvent> &ANGLERenderTest::getTraceEventBuffer()
1419 {
1420     return mTraceEventBuffer;
1421 }
1422 
onErrorMessage(const char * errorMessage)1423 void ANGLERenderTest::onErrorMessage(const char *errorMessage)
1424 {
1425     abortTest();
1426     std::ostringstream err;
1427     err << "Failing test because of unexpected error:\n" << errorMessage << "\n";
1428     failTest(err.str());
1429 }
1430 
getCurrentThreadSerial()1431 uint32_t ANGLERenderTest::getCurrentThreadSerial()
1432 {
1433     uint64_t id = angle::GetCurrentThreadUniqueId();
1434 
1435     for (uint32_t serial = 0; serial < static_cast<uint32_t>(mThreadIDs.size()); ++serial)
1436     {
1437         if (mThreadIDs[serial] == id)
1438         {
1439             return serial + 1;
1440         }
1441     }
1442 
1443     mThreadIDs.push_back(id);
1444     return static_cast<uint32_t>(mThreadIDs.size());
1445 }
1446 
1447 namespace angle
1448 {
GetHostTimeSeconds()1449 double GetHostTimeSeconds()
1450 {
1451     // Move the time origin to the first call to this function, to avoid generating unnecessarily
1452     // large timestamps.
1453     static double origin = GetCurrentSystemTime();
1454     return GetCurrentSystemTime() - origin;
1455 }
1456 }  // namespace angle
1457