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