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