• 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 }  // anonymous namespace
239 
TraceEvent(char phaseIn,const char * categoryNameIn,const char * nameIn,double timestampIn,uint32_t tidIn)240 TraceEvent::TraceEvent(char phaseIn,
241                        const char *categoryNameIn,
242                        const char *nameIn,
243                        double timestampIn,
244                        uint32_t tidIn)
245     : phase(phaseIn), categoryName(categoryNameIn), name{}, timestamp(timestampIn), tid(tidIn)
246 {
247     ASSERT(strlen(nameIn) < kMaxNameLen);
248     strcpy(name, nameIn);
249 }
250 
ANGLEPerfTest(const std::string & name,const std::string & backend,const std::string & story,unsigned int iterationsPerStep,const char * units)251 ANGLEPerfTest::ANGLEPerfTest(const std::string &name,
252                              const std::string &backend,
253                              const std::string &story,
254                              unsigned int iterationsPerStep,
255                              const char *units)
256     : mName(name),
257       mBackend(backend),
258       mStory(story),
259       mGPUTimeNs(0),
260       mSkipTest(false),
261       mStepsToRun(std::max(gStepsPerTrial, gMaxStepsPerformed)),
262       mTrialNumStepsPerformed(0),
263       mTotalNumStepsPerformed(0),
264       mIterationsPerStep(iterationsPerStep),
265       mRunning(true)
266 {
267     if (mStory == "")
268     {
269         mStory = "baseline_story";
270     }
271     if (mStory[0] == '_')
272     {
273         mStory = mStory.substr(1);
274     }
275     mReporter = std::make_unique<perf_test::PerfResultReporter>(mName + mBackend, mStory);
276     mReporter->RegisterImportantMetric(".wall_time", units);
277     mReporter->RegisterImportantMetric(".cpu_time", units);
278     mReporter->RegisterImportantMetric(".gpu_time", units);
279     mReporter->RegisterFyiMetric(".trial_steps", "count");
280     mReporter->RegisterFyiMetric(".total_steps", "count");
281 }
282 
~ANGLEPerfTest()283 ANGLEPerfTest::~ANGLEPerfTest() {}
284 
run()285 void ANGLEPerfTest::run()
286 {
287     printf("running test name: \"%s\", backend: \"%s\", story: \"%s\"\n", mName.c_str(),
288            mBackend.c_str(), mStory.c_str());
289 #if defined(ANGLE_PLATFORM_ANDROID)
290     __android_log_print(ANDROID_LOG_INFO, "ANGLE",
291                         "running test name: \"%s\", backend: \"%s\", story: \"%s\"", mName.c_str(),
292                         mBackend.c_str(), mStory.c_str());
293 #endif
294     if (mSkipTest)
295     {
296         GTEST_SKIP() << mSkipTestReason;
297         // GTEST_SKIP returns.
298     }
299 
300     uint32_t numTrials = OneFrame() ? 1 : gTestTrials;
301     if (gVerboseLogging)
302     {
303         printf("Test Trials: %d\n", static_cast<int>(numTrials));
304     }
305 
306     for (uint32_t trial = 0; trial < numTrials; ++trial)
307     {
308         runTrial(gTrialTimeSeconds, mStepsToRun, RunTrialPolicy::RunContinuously);
309         processResults();
310         if (gVerboseLogging)
311         {
312             double trialTime = mTrialTimer.getElapsedWallClockTime();
313             printf("Trial %d time: %.2lf seconds.\n", trial + 1, trialTime);
314 
315             double secondsPerStep      = trialTime / static_cast<double>(mTrialNumStepsPerformed);
316             double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
317             mTestTrialResults.push_back(secondsPerIteration * 1000.0);
318         }
319     }
320 
321     if (gVerboseLogging && !mTestTrialResults.empty())
322     {
323         double numResults = static_cast<double>(mTestTrialResults.size());
324         double mean       = ComputeMean(mTestTrialResults);
325 
326         double variance = 0;
327         for (double trialResult : mTestTrialResults)
328         {
329             double difference = trialResult - mean;
330             variance += difference * difference;
331         }
332         variance /= numResults;
333 
334         double standardDeviation      = std::sqrt(variance);
335         double coefficientOfVariation = standardDeviation / mean;
336 
337         if (mean < 0.001)
338         {
339             printf("Mean result time: %.4lf ns.\n", mean * 1000.0);
340         }
341         else
342         {
343             printf("Mean result time: %.4lf ms.\n", mean);
344         }
345         printf("Coefficient of variation: %.2lf%%\n", coefficientOfVariation * 100.0);
346     }
347 }
348 
runTrial(double maxRunTime,int maxStepsToRun,RunTrialPolicy runPolicy)349 void ANGLEPerfTest::runTrial(double maxRunTime, int maxStepsToRun, RunTrialPolicy runPolicy)
350 {
351     mTrialNumStepsPerformed = 0;
352     mRunning                = true;
353     mGPUTimeNs              = 0;
354     int stepAlignment       = getStepAlignment();
355     mTrialTimer.start();
356     startTest();
357 
358     while (mRunning)
359     {
360         // Only stop on aligned steps or in a few special case modes
361         if (mTrialNumStepsPerformed % stepAlignment == 0 || gStepsPerTrial == 1 || gRunToKeyFrame ||
362             gMaxStepsPerformed != kDefaultMaxStepsPerformed)
363         {
364             if (gMaxStepsPerformed > 0 && mTotalNumStepsPerformed >= gMaxStepsPerformed)
365             {
366                 if (gVerboseLogging)
367                 {
368                     printf("Stopping test after %d total steps.\n", mTotalNumStepsPerformed);
369                 }
370                 mRunning = false;
371                 break;
372             }
373             if (mTrialTimer.getElapsedWallClockTime() > maxRunTime)
374             {
375                 if (gVerboseLogging)
376                 {
377                     printf("Stopping test after %.2lf seconds.\n",
378                            mTrialTimer.getElapsedWallClockTime());
379                 }
380                 mRunning = false;
381                 break;
382             }
383             if (mTrialNumStepsPerformed >= maxStepsToRun)
384             {
385                 if (gVerboseLogging)
386                 {
387                     printf("Stopping test after %d trial steps.\n", mTrialNumStepsPerformed);
388                 }
389                 mRunning = false;
390                 break;
391             }
392         }
393 
394         step();
395 
396         if (runPolicy == RunTrialPolicy::FinishEveryStep)
397         {
398             FinishAndCheckForContextLoss();
399         }
400 
401         if (mRunning)
402         {
403             mTrialNumStepsPerformed++;
404             mTotalNumStepsPerformed++;
405         }
406 
407         if ((mTotalNumStepsPerformed % kNumberOfStepsPerformedToComputeGPUTime) == 0)
408         {
409             computeGPUTime();
410         }
411     }
412     finishTest();
413     mTrialTimer.stop();
414     computeGPUTime();
415 }
416 
SetUp()417 void ANGLEPerfTest::SetUp()
418 {
419     if (gWarmup)
420     {
421         // Trace tests run with glFinish for a loop (getStepAlignment == frameCount).
422         int warmupSteps = getStepAlignment();
423         if (gVerboseLogging)
424         {
425             printf("Warmup: %d steps\n", warmupSteps);
426         }
427 
428         Timer warmupTimer;
429         warmupTimer.start();
430 
431         runTrial(gTrialTimeSeconds, warmupSteps, RunTrialPolicy::FinishEveryStep);
432 
433         if (warmupSteps > 1)  // trace tests only: getStepAlignment() is 1 otherwise
434         {
435             // Short traces (e.g. 10 frames) have some spikes after the first loop b/308975999
436             const double kMinWarmupTime = 1.5;
437             double remainingTime        = kMinWarmupTime - warmupTimer.getElapsedWallClockTime();
438             if (remainingTime > 0)
439             {
440                 printf("Warmup: Looping for remaining warmup time (%.2f seconds).\n",
441                        remainingTime);
442                 runTrial(remainingTime, std::numeric_limits<int>::max(),
443                          RunTrialPolicy::RunContinuously);
444             }
445         }
446 
447         if (gVerboseLogging)
448         {
449             printf("Warmup took %.2lf seconds.\n", warmupTimer.getElapsedWallClockTime());
450         }
451     }
452 }
453 
TearDown()454 void ANGLEPerfTest::TearDown() {}
455 
recordIntegerMetric(const char * metric,size_t value,const std::string & units)456 void ANGLEPerfTest::recordIntegerMetric(const char *metric, size_t value, const std::string &units)
457 {
458     // Prints "RESULT ..." to stdout
459     mReporter->AddResult(metric, value);
460 
461     // Saves results to file if enabled
462     TestSuite::GetMetricWriter().writeInfo(mName, mBackend, mStory, metric, units);
463     TestSuite::GetMetricWriter().writeIntegerValue(value);
464 }
465 
recordDoubleMetric(const char * metric,double value,const std::string & units)466 void ANGLEPerfTest::recordDoubleMetric(const char *metric, double value, const std::string &units)
467 {
468     // Prints "RESULT ..." to stdout
469     mReporter->AddResult(metric, value);
470 
471     // Saves results to file if enabled
472     TestSuite::GetMetricWriter().writeInfo(mName, mBackend, mStory, metric, units);
473     TestSuite::GetMetricWriter().writeDoubleValue(value);
474 }
475 
addHistogramSample(const char * metric,double value,const std::string & units)476 void ANGLEPerfTest::addHistogramSample(const char *metric, double value, const std::string &units)
477 {
478     std::string measurement = mName + mBackend + metric;
479     // Output histogram JSON set format if enabled.
480     TestSuite::GetInstance()->addHistogramSample(measurement, mStory, value, units);
481 }
482 
processResults()483 void ANGLEPerfTest::processResults()
484 {
485     processClockResult(".cpu_time", mTrialTimer.getElapsedCpuTime());
486     processClockResult(".wall_time", mTrialTimer.getElapsedWallClockTime());
487 
488     if (mGPUTimeNs > 0)
489     {
490         processClockResult(".gpu_time", mGPUTimeNs * 1e-9);
491     }
492 
493     if (gVerboseLogging)
494     {
495         double fps = static_cast<double>(mTrialNumStepsPerformed * mIterationsPerStep) /
496                      mTrialTimer.getElapsedWallClockTime();
497         printf("Ran %0.2lf iterations per second\n", fps);
498     }
499 
500     mReporter->AddResult(".trial_steps", static_cast<size_t>(mTrialNumStepsPerformed));
501     mReporter->AddResult(".total_steps", static_cast<size_t>(mTotalNumStepsPerformed));
502 
503     if (!mProcessMemoryUsageKBSamples.empty())
504     {
505         std::sort(mProcessMemoryUsageKBSamples.begin(), mProcessMemoryUsageKBSamples.end());
506 
507         // Compute median.
508         size_t medianIndex      = mProcessMemoryUsageKBSamples.size() / 2;
509         uint64_t medianMemoryKB = mProcessMemoryUsageKBSamples[medianIndex];
510         auto peakMemoryIterator = std::max_element(mProcessMemoryUsageKBSamples.begin(),
511                                                    mProcessMemoryUsageKBSamples.end());
512         uint64_t peakMemoryKB   = *peakMemoryIterator;
513 
514         processMemoryResult(kMedianMemoryMetric, medianMemoryKB);
515         processMemoryResult(kPeakMemoryMetric, peakMemoryKB);
516     }
517 
518     for (const auto &iter : mPerfCounterInfo)
519     {
520         const std::string &counterName = iter.second.name;
521         std::vector<GLuint64> samples  = iter.second.samples;
522 
523         // Median
524         {
525             size_t midpoint = samples.size() / 2;
526             std::nth_element(samples.begin(), samples.begin() + midpoint, samples.end());
527 
528             std::string medianName = "." + counterName + "_median";
529             recordIntegerMetric(medianName.c_str(), static_cast<size_t>(samples[midpoint]),
530                                 "count");
531             addHistogramSample(medianName.c_str(), static_cast<double>(samples[midpoint]), "count");
532         }
533 
534         // Maximum
535         {
536             const auto &maxIt = std::max_element(samples.begin(), samples.end());
537 
538             std::string maxName = "." + counterName + "_max";
539             recordIntegerMetric(maxName.c_str(), static_cast<size_t>(*maxIt), "count");
540             addHistogramSample(maxName.c_str(), static_cast<double>(*maxIt), "count");
541         }
542 
543         // Sum
544         {
545             GLuint64 sum =
546                 std::accumulate(samples.begin(), samples.end(), static_cast<GLuint64>(0));
547 
548             std::string sumName = "." + counterName + "_max";
549             recordIntegerMetric(sumName.c_str(), static_cast<size_t>(sum), "count");
550             addHistogramSample(sumName.c_str(), static_cast<double>(sum), "count");
551         }
552     }
553 }
554 
processClockResult(const char * metric,double resultSeconds)555 void ANGLEPerfTest::processClockResult(const char *metric, double resultSeconds)
556 {
557     double secondsPerStep      = resultSeconds / static_cast<double>(mTrialNumStepsPerformed);
558     double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
559 
560     perf_test::MetricInfo metricInfo;
561     std::string units;
562     bool foundMetric = mReporter->GetMetricInfo(metric, &metricInfo);
563     if (!foundMetric)
564     {
565         fprintf(stderr, "Error getting metric info for %s.\n", metric);
566         return;
567     }
568     units = metricInfo.units;
569 
570     double result;
571 
572     if (units == "ms")
573     {
574         result = secondsPerIteration * kMilliSecondsPerSecond;
575     }
576     else if (units == "us")
577     {
578         result = secondsPerIteration * kMicroSecondsPerSecond;
579     }
580     else
581     {
582         result = secondsPerIteration * kNanoSecondsPerSecond;
583     }
584     recordDoubleMetric(metric, result, units);
585     addHistogramSample(metric, secondsPerIteration * kMilliSecondsPerSecond,
586                        "msBestFitFormat_smallerIsBetter");
587 }
588 
processMemoryResult(const char * metric,uint64_t resultKB)589 void ANGLEPerfTest::processMemoryResult(const char *metric, uint64_t resultKB)
590 {
591     perf_test::MetricInfo metricInfo;
592     if (!mReporter->GetMetricInfo(metric, &metricInfo))
593     {
594         mReporter->RegisterImportantMetric(metric, "sizeInBytes");
595     }
596 
597     recordIntegerMetric(metric, static_cast<size_t>(resultKB * 1000), "sizeInBytes");
598     addHistogramSample(metric, static_cast<double>(resultKB) * 1000.0,
599                        "sizeInBytes_smallerIsBetter");
600 }
601 
normalizedTime(size_t value) const602 double ANGLEPerfTest::normalizedTime(size_t value) const
603 {
604     return static_cast<double>(value) / static_cast<double>(mTrialNumStepsPerformed);
605 }
606 
getStepAlignment() const607 int ANGLEPerfTest::getStepAlignment() const
608 {
609     // Default: No special alignment rules.
610     return 1;
611 }
612 
RenderTestParams()613 RenderTestParams::RenderTestParams()
614 {
615 #if defined(ANGLE_DEBUG_LAYERS_ENABLED)
616     eglParameters.debugLayersEnabled = true;
617 #else
618     eglParameters.debugLayersEnabled = false;
619 #endif
620 }
621 
backend() const622 std::string RenderTestParams::backend() const
623 {
624     std::stringstream strstr;
625 
626     switch (driver)
627     {
628         case GLESDriverType::AngleEGL:
629             break;
630         case GLESDriverType::AngleVulkanSecondariesEGL:
631             strstr << "_vulkan_secondaries";
632             break;
633         case GLESDriverType::SystemWGL:
634         case GLESDriverType::SystemEGL:
635             strstr << "_native";
636             break;
637         case GLESDriverType::ZinkEGL:
638             strstr << "_zink";
639             break;
640         default:
641             assert(0);
642             return "_unk";
643     }
644 
645     switch (getRenderer())
646     {
647         case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
648             break;
649         case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE:
650             strstr << "_d3d11";
651             break;
652         case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE:
653             strstr << "_d3d9";
654             break;
655         case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
656             strstr << "_gl";
657             break;
658         case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
659             strstr << "_gles";
660             break;
661         case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
662             strstr << "_vulkan";
663             break;
664         case EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE:
665             strstr << "_metal";
666             break;
667         default:
668             assert(0);
669             return "_unk";
670     }
671 
672     switch (eglParameters.deviceType)
673     {
674         case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE:
675             strstr << "_null";
676             break;
677         case EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE:
678             strstr << "_swiftshader";
679             break;
680         default:
681             break;
682     }
683 
684     return strstr.str();
685 }
686 
story() const687 std::string RenderTestParams::story() const
688 {
689     std::stringstream strstr;
690 
691     switch (surfaceType)
692     {
693         case SurfaceType::Window:
694             break;
695         case SurfaceType::WindowWithVSync:
696             strstr << "_vsync";
697             break;
698         case SurfaceType::Offscreen:
699             strstr << "_offscreen";
700             break;
701         default:
702             UNREACHABLE();
703             return "";
704     }
705 
706     if (multisample)
707     {
708         strstr << "_" << samples << "_samples";
709     }
710 
711     return strstr.str();
712 }
713 
backendAndStory() const714 std::string RenderTestParams::backendAndStory() const
715 {
716     return backend() + story();
717 }
718 
ANGLERenderTest(const std::string & name,const RenderTestParams & testParams,const char * units)719 ANGLERenderTest::ANGLERenderTest(const std::string &name,
720                                  const RenderTestParams &testParams,
721                                  const char *units)
722     : ANGLEPerfTest(name,
723                     testParams.backend(),
724                     testParams.story(),
725                     OneFrame() ? 1 : testParams.iterationsPerStep,
726                     units),
727       mTestParams(testParams),
728       mIsTimestampQueryAvailable(false),
729       mGLWindow(nullptr),
730       mOSWindow(nullptr),
731       mSwapEnabled(true)
732 {
733     // Force fast tests to make sure our slowest bots don't time out.
734     if (OneFrame())
735     {
736         const_cast<RenderTestParams &>(testParams).iterationsPerStep = 1;
737     }
738 
739     // Try to ensure we don't trigger allocation during execution.
740     mTraceEventBuffer.reserve(kInitialTraceEventBufferSize);
741 
742     switch (testParams.driver)
743     {
744         case GLESDriverType::AngleEGL:
745             mGLWindow = EGLWindow::New(testParams.clientType, testParams.majorVersion,
746                                        testParams.minorVersion, testParams.profileMask);
747             mEntryPointsLib.reset(OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME, SearchType::ModuleDir));
748             break;
749         case GLESDriverType::AngleVulkanSecondariesEGL:
750             mGLWindow = EGLWindow::New(testParams.clientType, testParams.majorVersion,
751                                        testParams.minorVersion, testParams.profileMask);
752             mEntryPointsLib.reset(OpenSharedLibrary(ANGLE_VULKAN_SECONDARIES_EGL_LIBRARY_NAME,
753                                                     SearchType::ModuleDir));
754             break;
755         case GLESDriverType::SystemEGL:
756 #if defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
757             mGLWindow = EGLWindow::New(testParams.clientType, testParams.majorVersion,
758                                        testParams.minorVersion, testParams.profileMask);
759             mEntryPointsLib.reset(OpenSharedLibraryWithExtension(
760                 GetNativeEGLLibraryNameWithExtension(), SearchType::SystemDir));
761 #else
762             skipTest("Not implemented.");
763 #endif  // defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
764             break;
765         case GLESDriverType::SystemWGL:
766 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
767             mGLWindow = WGLWindow::New(testParams.clientType, testParams.majorVersion,
768                                        testParams.minorVersion, testParams.profileMask);
769             mEntryPointsLib.reset(OpenSharedLibrary("opengl32", SearchType::SystemDir));
770 #else
771             skipTest("WGL driver not available.");
772 #endif  // defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
773             break;
774         case GLESDriverType::ZinkEGL:
775             mGLWindow = EGLWindow::New(testParams.clientType, testParams.majorVersion,
776                                        testParams.minorVersion, testParams.profileMask);
777             mEntryPointsLib.reset(
778                 OpenSharedLibrary(ANGLE_MESA_EGL_LIBRARY_NAME, SearchType::ModuleDir));
779             break;
780         default:
781             skipTest("Error in switch.");
782             break;
783     }
784 }
785 
~ANGLERenderTest()786 ANGLERenderTest::~ANGLERenderTest()
787 {
788     OSWindow::Delete(&mOSWindow);
789     GLWindowBase::Delete(&mGLWindow);
790 }
791 
addExtensionPrerequisite(std::string extensionName)792 void ANGLERenderTest::addExtensionPrerequisite(std::string extensionName)
793 {
794     mExtensionPrerequisites.push_back(extensionName);
795 }
796 
addIntegerPrerequisite(GLenum target,int min)797 void ANGLERenderTest::addIntegerPrerequisite(GLenum target, int min)
798 {
799     mIntegerPrerequisites.push_back({target, min});
800 }
801 
SetUp()802 void ANGLERenderTest::SetUp()
803 {
804     if (mSkipTest)
805     {
806         return;
807     }
808 
809     // Set a consistent CPU core affinity and high priority.
810     StabilizeCPUForBenchmarking();
811 
812     mOSWindow = OSWindow::New();
813 
814     if (!mGLWindow)
815     {
816         skipTest("!mGLWindow");
817         return;
818     }
819 
820     mPlatformMethods.logError                    = CustomLogError;
821     mPlatformMethods.logWarning                  = EmptyPlatformMethod;
822     mPlatformMethods.logInfo                     = EmptyPlatformMethod;
823     mPlatformMethods.addTraceEvent               = AddPerfTraceEvent;
824     mPlatformMethods.getTraceCategoryEnabledFlag = GetPerfTraceCategoryEnabled;
825     mPlatformMethods.updateTraceEventDuration    = UpdateTraceEventDuration;
826     mPlatformMethods.monotonicallyIncreasingTime = MonotonicallyIncreasingTime;
827     mPlatformMethods.context                     = this;
828 
829     if (!mOSWindow->initialize(mName, mTestParams.windowWidth, mTestParams.windowHeight))
830     {
831         failTest("Failed initializing OSWindow");
832         return;
833     }
834 
835     // Override platform method parameter.
836     EGLPlatformParameters withMethods = mTestParams.eglParameters;
837     withMethods.platformMethods       = &mPlatformMethods;
838 
839     // Request a common framebuffer config
840     mConfigParams.redBits     = 8;
841     mConfigParams.greenBits   = 8;
842     mConfigParams.blueBits    = 8;
843     mConfigParams.alphaBits   = 8;
844     mConfigParams.depthBits   = 24;
845     mConfigParams.stencilBits = 8;
846     mConfigParams.colorSpace  = mTestParams.colorSpace;
847     mConfigParams.multisample = mTestParams.multisample;
848     mConfigParams.samples     = mTestParams.samples;
849     if (mTestParams.surfaceType != SurfaceType::WindowWithVSync)
850     {
851         mConfigParams.swapInterval = 0;
852     }
853 
854     if (gPrintExtensionsToFile != nullptr || gRequestedExtensions != nullptr)
855     {
856         mConfigParams.extensionsEnabled = false;
857     }
858 
859     GLWindowResult res = mGLWindow->initializeGLWithResult(
860         mOSWindow, mEntryPointsLib.get(), mTestParams.driver, withMethods, mConfigParams);
861     switch (res)
862     {
863         case GLWindowResult::NoColorspaceSupport:
864             skipTest("Missing support for color spaces.");
865             return;
866         case GLWindowResult::Error:
867             failTest("Failed initializing GL Window");
868             return;
869         default:
870             break;
871     }
872 
873     if (gPrintExtensionsToFile)
874     {
875         std::ofstream fout(gPrintExtensionsToFile);
876         if (fout.is_open())
877         {
878             int numExtensions = 0;
879             glGetIntegerv(GL_NUM_REQUESTABLE_EXTENSIONS_ANGLE, &numExtensions);
880             for (int ext = 0; ext < numExtensions; ext++)
881             {
882                 fout << glGetStringi(GL_REQUESTABLE_EXTENSIONS_ANGLE, ext) << std::endl;
883             }
884             fout.close();
885             std::stringstream statusString;
886             statusString << "Wrote out to file: " << gPrintExtensionsToFile;
887             skipTest(statusString.str());
888         }
889         else
890         {
891             std::stringstream failStr;
892             failStr << "Failed to open file: " << gPrintExtensionsToFile;
893             failTest(failStr.str());
894         }
895         return;
896     }
897 
898     if (gRequestedExtensions != nullptr)
899     {
900         std::istringstream ss{gRequestedExtensions};
901         std::string ext;
902         while (std::getline(ss, ext, ' '))
903         {
904             glRequestExtensionANGLE(ext.c_str());
905         }
906     }
907 
908     // Disable vsync (if not done by the window init).
909     if (mTestParams.surfaceType != SurfaceType::WindowWithVSync)
910     {
911         if (!mGLWindow->setSwapInterval(0))
912         {
913             failTest("Failed setting swap interval");
914             return;
915         }
916     }
917 
918     if (mTestParams.trackGpuTime)
919     {
920         mIsTimestampQueryAvailable = EnsureGLExtensionEnabled("GL_EXT_disjoint_timer_query");
921     }
922 
923     skipTestIfMissingExtensionPrerequisites();
924     skipTestIfFailsIntegerPrerequisite();
925 
926     if (mSkipTest)
927     {
928         GTEST_SKIP() << mSkipTestReason;
929         // GTEST_SKIP returns.
930     }
931 
932 #if defined(ANGLE_ENABLE_ASSERTS)
933     if (IsGLExtensionEnabled("GL_KHR_debug") && mEnableDebugCallback)
934     {
935         EnableDebugCallback(&PerfTestDebugCallback, this);
936     }
937 #endif
938 
939     initializeBenchmark();
940 
941     if (mSkipTest)
942     {
943         GTEST_SKIP() << mSkipTestReason;
944         // GTEST_SKIP returns.
945     }
946 
947     if (mTestParams.iterationsPerStep == 0)
948     {
949         failTest("Please initialize 'iterationsPerStep'.");
950         return;
951     }
952 
953     if (gVerboseLogging)
954     {
955         printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER));
956         printf("GL_VERSION: %s\n", glGetString(GL_VERSION));
957     }
958 
959     mTestTrialResults.reserve(gTestTrials);
960 
961     // Runs warmup if enabled
962     ANGLEPerfTest::SetUp();
963 
964     initPerfCounters();
965 }
966 
TearDown()967 void ANGLERenderTest::TearDown()
968 {
969     ASSERT(mTimestampQueries.empty());
970 
971     if (!mSkipTest)
972     {
973         destroyBenchmark();
974     }
975 
976     if (mGLWindow)
977     {
978         mGLWindow->destroyGL();
979         mGLWindow = nullptr;
980     }
981 
982     if (mOSWindow)
983     {
984         mOSWindow->destroy();
985         mOSWindow = nullptr;
986     }
987 
988     // Dump trace events to json file.
989     if (gEnableTrace)
990     {
991         DumpTraceEventsToJSONFile(mTraceEventBuffer, gTraceFile);
992     }
993 
994     ANGLEPerfTest::TearDown();
995 }
996 
initPerfCounters()997 void ANGLERenderTest::initPerfCounters()
998 {
999     if (!gPerfCounters)
1000     {
1001         return;
1002     }
1003 
1004     if (!IsGLExtensionEnabled(kPerfMonitorExtensionName))
1005     {
1006         fprintf(stderr, "Cannot report perf metrics because %s is not available.\n",
1007                 kPerfMonitorExtensionName);
1008         return;
1009     }
1010 
1011     CounterNameToIndexMap indexMap = BuildCounterNameToIndexMap();
1012 
1013     std::vector<std::string> counters =
1014         angle::SplitString(gPerfCounters, ":", angle::WhitespaceHandling::TRIM_WHITESPACE,
1015                            angle::SplitResult::SPLIT_WANT_NONEMPTY);
1016     for (const std::string &counter : counters)
1017     {
1018         bool found = false;
1019 
1020         for (const auto &indexMapIter : indexMap)
1021         {
1022             const std::string &indexMapName = indexMapIter.first;
1023             if (NamesMatchWithWildcard(counter.c_str(), indexMapName.c_str()))
1024             {
1025                 {
1026                     std::stringstream medianStr;
1027                     medianStr << '.' << indexMapName << "_median";
1028                     std::string medianName = medianStr.str();
1029                     mReporter->RegisterImportantMetric(medianName, "count");
1030                 }
1031 
1032                 {
1033                     std::stringstream maxStr;
1034                     maxStr << '.' << indexMapName << "_max";
1035                     std::string maxName = maxStr.str();
1036                     mReporter->RegisterImportantMetric(maxName, "count");
1037                 }
1038 
1039                 {
1040                     std::stringstream sumStr;
1041                     sumStr << '.' << indexMapName << "_sum";
1042                     std::string sumName = sumStr.str();
1043                     mReporter->RegisterImportantMetric(sumName, "count");
1044                 }
1045 
1046                 GLuint index            = indexMapIter.second;
1047                 mPerfCounterInfo[index] = {indexMapName, {}};
1048 
1049                 found = true;
1050             }
1051         }
1052 
1053         if (!found)
1054         {
1055             fprintf(stderr, "'%s' does not match any available perf counters.\n", counter.c_str());
1056         }
1057     }
1058 }
1059 
updatePerfCounters()1060 void ANGLERenderTest::updatePerfCounters()
1061 {
1062     if (mPerfCounterInfo.empty())
1063     {
1064         return;
1065     }
1066 
1067     std::vector<PerfMonitorTriplet> perfData = GetPerfMonitorTriplets();
1068     ASSERT(!perfData.empty());
1069 
1070     for (auto &iter : mPerfCounterInfo)
1071     {
1072         uint32_t counter               = iter.first;
1073         std::vector<GLuint64> &samples = iter.second.samples;
1074         samples.push_back(perfData[counter].value);
1075     }
1076 }
1077 
beginInternalTraceEvent(const char * name)1078 void ANGLERenderTest::beginInternalTraceEvent(const char *name)
1079 {
1080     if (gEnableTrace)
1081     {
1082         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[0].name, name,
1083                                        MonotonicallyIncreasingTime(&mPlatformMethods),
1084                                        getCurrentThreadSerial());
1085     }
1086 }
1087 
endInternalTraceEvent(const char * name)1088 void ANGLERenderTest::endInternalTraceEvent(const char *name)
1089 {
1090     if (gEnableTrace)
1091     {
1092         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[0].name, name,
1093                                        MonotonicallyIncreasingTime(&mPlatformMethods),
1094                                        getCurrentThreadSerial());
1095     }
1096 }
1097 
beginGLTraceEvent(const char * name,double hostTimeSec)1098 void ANGLERenderTest::beginGLTraceEvent(const char *name, double hostTimeSec)
1099 {
1100     if (gEnableTrace)
1101     {
1102         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[1].name, name,
1103                                        hostTimeSec, getCurrentThreadSerial());
1104     }
1105 }
1106 
endGLTraceEvent(const char * name,double hostTimeSec)1107 void ANGLERenderTest::endGLTraceEvent(const char *name, double hostTimeSec)
1108 {
1109     if (gEnableTrace)
1110     {
1111         mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[1].name, name,
1112                                        hostTimeSec, getCurrentThreadSerial());
1113     }
1114 }
1115 
step()1116 void ANGLERenderTest::step()
1117 {
1118     beginInternalTraceEvent("step");
1119 
1120     // Clear events that the application did not process from this frame
1121     Event event;
1122     bool closed = false;
1123     while (popEvent(&event))
1124     {
1125         // If the application did not catch a close event, close now
1126         if (event.Type == Event::EVENT_CLOSED)
1127         {
1128             closed = true;
1129         }
1130     }
1131 
1132     if (closed)
1133     {
1134         abortTest();
1135     }
1136     else
1137     {
1138         drawBenchmark();
1139 
1140         // Swap is needed so that the GPU driver will occasionally flush its
1141         // internal command queue to the GPU. This is enabled for null back-end
1142         // devices because some back-ends (e.g. Vulkan) also accumulate internal
1143         // command queues.
1144         if (mSwapEnabled)
1145         {
1146             updatePerfCounters();
1147             mGLWindow->swap();
1148         }
1149         mOSWindow->messageLoop();
1150 
1151 #if defined(ANGLE_ENABLE_ASSERTS)
1152         if (!gRetraceMode)
1153         {
1154             EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
1155         }
1156 #endif  // defined(ANGLE_ENABLE_ASSERTS)
1157 
1158         // Sample system memory
1159         uint64_t processMemoryUsageKB = GetProcessMemoryUsageKB();
1160         if (processMemoryUsageKB)
1161         {
1162             mProcessMemoryUsageKBSamples.push_back(processMemoryUsageKB);
1163         }
1164     }
1165 
1166     endInternalTraceEvent("step");
1167 }
1168 
startGpuTimer()1169 void ANGLERenderTest::startGpuTimer()
1170 {
1171     if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
1172     {
1173         glGenQueriesEXT(1, &mCurrentTimestampBeginQuery);
1174         glQueryCounterEXT(mCurrentTimestampBeginQuery, GL_TIMESTAMP_EXT);
1175     }
1176 }
1177 
stopGpuTimer()1178 void ANGLERenderTest::stopGpuTimer()
1179 {
1180     if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
1181     {
1182         GLuint endQuery = 0;
1183         glGenQueriesEXT(1, &endQuery);
1184         glQueryCounterEXT(endQuery, GL_TIMESTAMP_EXT);
1185         mTimestampQueries.push({mCurrentTimestampBeginQuery, endQuery});
1186     }
1187 }
1188 
computeGPUTime()1189 void ANGLERenderTest::computeGPUTime()
1190 {
1191     if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
1192     {
1193         while (!mTimestampQueries.empty())
1194         {
1195             const TimestampSample &sample = mTimestampQueries.front();
1196             GLuint available              = GL_FALSE;
1197             glGetQueryObjectuivEXT(sample.endQuery, GL_QUERY_RESULT_AVAILABLE_EXT, &available);
1198             if (available != GL_TRUE)
1199             {
1200                 // query is not completed yet, bail out
1201                 break;
1202             }
1203 
1204             // frame's begin query must also completed.
1205             glGetQueryObjectuivEXT(sample.beginQuery, GL_QUERY_RESULT_AVAILABLE_EXT, &available);
1206             ASSERT(available == GL_TRUE);
1207 
1208             // Retrieve query result
1209             uint64_t beginGLTimeNs = 0;
1210             uint64_t endGLTimeNs   = 0;
1211             glGetQueryObjectui64vEXT(sample.beginQuery, GL_QUERY_RESULT_EXT, &beginGLTimeNs);
1212             glGetQueryObjectui64vEXT(sample.endQuery, GL_QUERY_RESULT_EXT, &endGLTimeNs);
1213             glDeleteQueriesEXT(1, &sample.beginQuery);
1214             glDeleteQueriesEXT(1, &sample.endQuery);
1215             mTimestampQueries.pop();
1216 
1217             // compute GPU time
1218             mGPUTimeNs += endGLTimeNs - beginGLTimeNs;
1219         }
1220     }
1221 }
1222 
startTest()1223 void ANGLERenderTest::startTest() {}
1224 
finishTest()1225 void ANGLERenderTest::finishTest()
1226 {
1227     if (mTestParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE &&
1228         !gNoFinish && !gRetraceMode)
1229     {
1230         FinishAndCheckForContextLoss();
1231     }
1232 }
1233 
popEvent(Event * event)1234 bool ANGLERenderTest::popEvent(Event *event)
1235 {
1236     return mOSWindow->popEvent(event);
1237 }
1238 
getWindow()1239 OSWindow *ANGLERenderTest::getWindow()
1240 {
1241     return mOSWindow;
1242 }
1243 
getGLWindow()1244 GLWindowBase *ANGLERenderTest::getGLWindow()
1245 {
1246     return mGLWindow;
1247 }
1248 
skipTestIfMissingExtensionPrerequisites()1249 void ANGLERenderTest::skipTestIfMissingExtensionPrerequisites()
1250 {
1251     for (std::string extension : mExtensionPrerequisites)
1252     {
1253         if (!CheckExtensionExists(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
1254                                   extension))
1255         {
1256             skipTest(std::string("Test skipped due to missing extension: ") + extension);
1257             return;
1258         }
1259     }
1260 }
1261 
skipTestIfFailsIntegerPrerequisite()1262 void ANGLERenderTest::skipTestIfFailsIntegerPrerequisite()
1263 {
1264     for (const auto [target, minRequired] : mIntegerPrerequisites)
1265     {
1266         GLint driverValue;
1267         glGetIntegerv(target, &driverValue);
1268         if (static_cast<int>(driverValue) < minRequired)
1269         {
1270             std::stringstream ss;
1271             ss << "Test skipped due to value (" << std::to_string(static_cast<int>(driverValue))
1272                << ") being less than the prerequisite minimum (" << std::to_string(minRequired)
1273                << ") for GL constant " << gl::GLenumToString(gl::GLESEnum::AllEnums, target);
1274             skipTest(ss.str());
1275         }
1276     }
1277 }
1278 
setWebGLCompatibilityEnabled(bool webglCompatibility)1279 void ANGLERenderTest::setWebGLCompatibilityEnabled(bool webglCompatibility)
1280 {
1281     mConfigParams.webGLCompatibility = webglCompatibility;
1282 }
1283 
setRobustResourceInit(bool enabled)1284 void ANGLERenderTest::setRobustResourceInit(bool enabled)
1285 {
1286     mConfigParams.robustResourceInit = enabled;
1287 }
1288 
getTraceEventBuffer()1289 std::vector<TraceEvent> &ANGLERenderTest::getTraceEventBuffer()
1290 {
1291     return mTraceEventBuffer;
1292 }
1293 
onErrorMessage(const char * errorMessage)1294 void ANGLERenderTest::onErrorMessage(const char *errorMessage)
1295 {
1296     abortTest();
1297     std::ostringstream err;
1298     err << "Failing test because of unexpected error:\n" << errorMessage << "\n";
1299     failTest(err.str());
1300 }
1301 
getCurrentThreadSerial()1302 uint32_t ANGLERenderTest::getCurrentThreadSerial()
1303 {
1304     uint64_t id = angle::GetCurrentThreadUniqueId();
1305 
1306     for (uint32_t serial = 0; serial < static_cast<uint32_t>(mThreadIDs.size()); ++serial)
1307     {
1308         if (mThreadIDs[serial] == id)
1309         {
1310             return serial + 1;
1311         }
1312     }
1313 
1314     mThreadIDs.push_back(id);
1315     return static_cast<uint32_t>(mThreadIDs.size());
1316 }
1317 
1318 namespace angle
1319 {
GetHostTimeSeconds()1320 double GetHostTimeSeconds()
1321 {
1322     // Move the time origin to the first call to this function, to avoid generating unnecessarily
1323     // large timestamps.
1324     static double origin = GetCurrentSystemTime();
1325     return GetCurrentSystemTime() - origin;
1326 }
1327 }  // namespace angle
1328