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