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