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/platform.h"
15 #include "common/system_utils.h"
16 #include "common/utilities.h"
17 #include "test_utils/runner/TestSuite.h"
18 #include "third_party/perf/perf_test.h"
19 #include "third_party/trace_event/trace_event.h"
20 #include "util/shader_utils.h"
21 #include "util/test_utils.h"
22
23 #include <cassert>
24 #include <cmath>
25 #include <fstream>
26 #include <iostream>
27 #include <sstream>
28
29 #include <rapidjson/document.h>
30 #include <rapidjson/filewritestream.h>
31 #include <rapidjson/istreamwrapper.h>
32 #include <rapidjson/prettywriter.h>
33
34 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
35 # include "util/windows/WGLWindow.h"
36 #endif // defined(ANGLE_USE_UTIL_LOADER) &&defined(ANGLE_PLATFORM_WINDOWS)
37
38 using namespace angle;
39 namespace js = rapidjson;
40
41 namespace
42 {
43 constexpr size_t kInitialTraceEventBufferSize = 50000;
44 constexpr double kMilliSecondsPerSecond = 1e3;
45 constexpr double kMicroSecondsPerSecond = 1e6;
46 constexpr double kNanoSecondsPerSecond = 1e9;
47
48 struct TraceCategory
49 {
50 unsigned char enabled;
51 const char *name;
52 };
53
54 constexpr TraceCategory gTraceCategories[2] = {
55 {1, "gpu.angle"},
56 {1, "gpu.angle.gpu"},
57 };
58
EmptyPlatformMethod(angle::PlatformMethods *,const char *)59 void EmptyPlatformMethod(angle::PlatformMethods *, const char *) {}
60
CustomLogError(angle::PlatformMethods * platform,const char * errorMessage)61 void CustomLogError(angle::PlatformMethods *platform, const char *errorMessage)
62 {
63 auto *angleRenderTest = static_cast<ANGLERenderTest *>(platform->context);
64 angleRenderTest->onErrorMessage(errorMessage);
65 }
66
OverrideWorkaroundsD3D(angle::PlatformMethods * platform,angle::FeaturesD3D * featuresD3D)67 void OverrideWorkaroundsD3D(angle::PlatformMethods *platform, angle::FeaturesD3D *featuresD3D)
68 {
69 auto *angleRenderTest = static_cast<ANGLERenderTest *>(platform->context);
70 angleRenderTest->overrideWorkaroundsD3D(featuresD3D);
71 }
72
AddPerfTraceEvent(angle::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)73 angle::TraceEventHandle AddPerfTraceEvent(angle::PlatformMethods *platform,
74 char phase,
75 const unsigned char *categoryEnabledFlag,
76 const char *name,
77 unsigned long long id,
78 double timestamp,
79 int numArgs,
80 const char **argNames,
81 const unsigned char *argTypes,
82 const unsigned long long *argValues,
83 unsigned char flags)
84 {
85 if (!gEnableTrace)
86 return 0;
87
88 // Discover the category name based on categoryEnabledFlag. This flag comes from the first
89 // parameter of TraceCategory, and corresponds to one of the entries in gTraceCategories.
90 static_assert(offsetof(TraceCategory, enabled) == 0,
91 "|enabled| must be the first field of the TraceCategory class.");
92 const TraceCategory *category = reinterpret_cast<const TraceCategory *>(categoryEnabledFlag);
93
94 ANGLERenderTest *renderTest = static_cast<ANGLERenderTest *>(platform->context);
95
96 uint32_t tid = renderTest->getCurrentThreadSerial();
97
98 std::vector<TraceEvent> &buffer = renderTest->getTraceEventBuffer();
99 buffer.emplace_back(phase, category->name, name, timestamp, tid);
100 return buffer.size();
101 }
102
GetPerfTraceCategoryEnabled(angle::PlatformMethods * platform,const char * categoryName)103 const unsigned char *GetPerfTraceCategoryEnabled(angle::PlatformMethods *platform,
104 const char *categoryName)
105 {
106 if (gEnableTrace)
107 {
108 for (const TraceCategory &category : gTraceCategories)
109 {
110 if (strcmp(category.name, categoryName) == 0)
111 {
112 return &category.enabled;
113 }
114 }
115 }
116
117 constexpr static unsigned char kZero = 0;
118 return &kZero;
119 }
120
UpdateTraceEventDuration(angle::PlatformMethods * platform,const unsigned char * categoryEnabledFlag,const char * name,angle::TraceEventHandle eventHandle)121 void UpdateTraceEventDuration(angle::PlatformMethods *platform,
122 const unsigned char *categoryEnabledFlag,
123 const char *name,
124 angle::TraceEventHandle eventHandle)
125 {
126 // Not implemented.
127 }
128
MonotonicallyIncreasingTime(angle::PlatformMethods * platform)129 double MonotonicallyIncreasingTime(angle::PlatformMethods *platform)
130 {
131 return GetHostTimeSeconds();
132 }
133
WriteJsonFile(const std::string & outputFile,js::Document * doc)134 bool WriteJsonFile(const std::string &outputFile, js::Document *doc)
135 {
136 FILE *fp = fopen(outputFile.c_str(), "w");
137 if (!fp)
138 {
139 return false;
140 }
141
142 constexpr size_t kBufferSize = 0xFFFF;
143 std::vector<char> writeBuffer(kBufferSize);
144 js::FileWriteStream os(fp, writeBuffer.data(), kBufferSize);
145 js::PrettyWriter<js::FileWriteStream> writer(os);
146 if (!doc->Accept(writer))
147 {
148 fclose(fp);
149 return false;
150 }
151 fclose(fp);
152 return true;
153 }
154
DumpTraceEventsToJSONFile(const std::vector<TraceEvent> & traceEvents,const char * outputFileName)155 void DumpTraceEventsToJSONFile(const std::vector<TraceEvent> &traceEvents,
156 const char *outputFileName)
157 {
158 js::Document doc(js::kObjectType);
159 js::Document::AllocatorType &allocator = doc.GetAllocator();
160
161 js::Value events(js::kArrayType);
162
163 for (const TraceEvent &traceEvent : traceEvents)
164 {
165 js::Value value(js::kObjectType);
166
167 const uint64_t microseconds = static_cast<uint64_t>(traceEvent.timestamp * 1000.0 * 1000.0);
168
169 js::Document::StringRefType eventName(traceEvent.name);
170 js::Document::StringRefType categoryName(traceEvent.categoryName);
171 js::Document::StringRefType pidName(
172 strcmp(traceEvent.categoryName, "gpu.angle.gpu") == 0 ? "GPU" : "ANGLE");
173
174 value.AddMember("name", eventName, allocator);
175 value.AddMember("cat", categoryName, allocator);
176 value.AddMember("ph", std::string(1, traceEvent.phase), allocator);
177 value.AddMember("ts", microseconds, allocator);
178 value.AddMember("pid", pidName, allocator);
179 value.AddMember("tid", traceEvent.tid, allocator);
180
181 events.PushBack(value, allocator);
182 }
183
184 doc.AddMember("traceEvents", events, allocator);
185
186 if (WriteJsonFile(outputFileName, &doc))
187 {
188 printf("Wrote trace file to %s\n", outputFileName);
189 }
190 else
191 {
192 printf("Error writing trace file to %s\n", outputFileName);
193 }
194 }
195 } // anonymous namespace
196
TraceEvent(char phaseIn,const char * categoryNameIn,const char * nameIn,double timestampIn,uint32_t tidIn)197 TraceEvent::TraceEvent(char phaseIn,
198 const char *categoryNameIn,
199 const char *nameIn,
200 double timestampIn,
201 uint32_t tidIn)
202 : phase(phaseIn), categoryName(categoryNameIn), name{}, timestamp(timestampIn), tid(tidIn)
203 {
204 ASSERT(strlen(nameIn) < kMaxNameLen);
205 strcpy(name, nameIn);
206 }
207
ANGLEPerfTest(const std::string & name,const std::string & backend,const std::string & story,unsigned int iterationsPerStep,const char * units)208 ANGLEPerfTest::ANGLEPerfTest(const std::string &name,
209 const std::string &backend,
210 const std::string &story,
211 unsigned int iterationsPerStep,
212 const char *units)
213 : mName(name),
214 mBackend(backend),
215 mStory(story),
216 mGPUTimeNs(0),
217 mSkipTest(false),
218 mStepsToRun(std::max(gStepsPerTrial, gMaxStepsPerformed)),
219 mTrialNumStepsPerformed(0),
220 mTotalNumStepsPerformed(0),
221 mStepsPerRunLoopStep(1),
222 mIterationsPerStep(iterationsPerStep),
223 mRunning(true)
224 {
225 if (mStory == "")
226 {
227 mStory = "baseline_story";
228 }
229 if (mStory[0] == '_')
230 {
231 mStory = mStory.substr(1);
232 }
233 mReporter = std::make_unique<perf_test::PerfResultReporter>(mName + mBackend, mStory);
234 mReporter->RegisterImportantMetric(".wall_time", units);
235 mReporter->RegisterImportantMetric(".gpu_time", units);
236 mReporter->RegisterFyiMetric(".trial_steps", "count");
237 mReporter->RegisterFyiMetric(".total_steps", "count");
238 }
239
~ANGLEPerfTest()240 ANGLEPerfTest::~ANGLEPerfTest() {}
241
run()242 void ANGLEPerfTest::run()
243 {
244 if (mSkipTest)
245 {
246 return;
247 }
248
249 uint32_t numTrials = OneFrame() ? 1 : gTestTrials;
250 if (gVerboseLogging)
251 {
252 printf("Test Trials: %d\n", static_cast<int>(numTrials));
253 }
254
255 for (uint32_t trial = 0; trial < numTrials; ++trial)
256 {
257 doRunLoop(gTestTimeSeconds, mStepsToRun, RunLoopPolicy::RunContinuously);
258 printResults();
259 if (gVerboseLogging)
260 {
261 double trialTime = mTimer.getElapsedTime();
262 printf("Trial %d time: %.2lf seconds.\n", trial + 1, trialTime);
263
264 double secondsPerStep = trialTime / static_cast<double>(mTrialNumStepsPerformed);
265 double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
266 mTestTrialResults.push_back(secondsPerIteration * 1000.0);
267 }
268 }
269
270 if (gVerboseLogging)
271 {
272 double numResults = static_cast<double>(mTestTrialResults.size());
273 double mean = 0;
274 for (double trialResult : mTestTrialResults)
275 {
276 mean += trialResult;
277 }
278 mean /= numResults;
279
280 double variance = 0;
281 for (double trialResult : mTestTrialResults)
282 {
283 double difference = trialResult - mean;
284 variance += difference * difference;
285 }
286 variance /= numResults;
287
288 double standardDeviation = std::sqrt(variance);
289 double coefficientOfVariation = standardDeviation / mean;
290
291 printf("Mean result time: %.4lf ms.\n", mean);
292 printf("Coefficient of variation: %.2lf%%\n", coefficientOfVariation * 100.0);
293 }
294 }
295
setStepsPerRunLoopStep(int stepsPerRunLoop)296 void ANGLEPerfTest::setStepsPerRunLoopStep(int stepsPerRunLoop)
297 {
298 ASSERT(stepsPerRunLoop >= 1);
299 mStepsPerRunLoopStep = stepsPerRunLoop;
300 }
301
doRunLoop(double maxRunTime,int maxStepsToRun,RunLoopPolicy runPolicy)302 void ANGLEPerfTest::doRunLoop(double maxRunTime, int maxStepsToRun, RunLoopPolicy runPolicy)
303 {
304 mTrialNumStepsPerformed = 0;
305 mRunning = true;
306 mGPUTimeNs = 0;
307 mTimer.start();
308 startTest();
309
310 while (mRunning)
311 {
312 step();
313
314 if (runPolicy == RunLoopPolicy::FinishEveryStep)
315 {
316 glFinish();
317 }
318
319 if (mRunning)
320 {
321 mTrialNumStepsPerformed += mStepsPerRunLoopStep;
322 mTotalNumStepsPerformed += mStepsPerRunLoopStep;
323 if (mTimer.getElapsedTime() > maxRunTime)
324 {
325 mRunning = false;
326 }
327 else if (mTrialNumStepsPerformed >= maxStepsToRun)
328 {
329 mRunning = false;
330 }
331 else if (gMaxStepsPerformed > 0 && mTotalNumStepsPerformed >= gMaxStepsPerformed)
332 {
333 mRunning = false;
334 }
335 }
336 }
337 finishTest();
338 mTimer.stop();
339 computeGPUTime();
340 }
341
SetUp()342 void ANGLEPerfTest::SetUp() {}
343
TearDown()344 void ANGLEPerfTest::TearDown() {}
345
printResults()346 double ANGLEPerfTest::printResults()
347 {
348 double elapsedTimeSeconds[2] = {
349 mTimer.getElapsedTime(),
350 mGPUTimeNs * 1e-9,
351 };
352
353 const char *clockNames[2] = {
354 ".wall_time",
355 ".gpu_time",
356 };
357
358 // If measured gpu time is non-zero, print that too.
359 size_t clocksToOutput = mGPUTimeNs > 0 ? 2 : 1;
360
361 double retValue = 0.0;
362 for (size_t i = 0; i < clocksToOutput; ++i)
363 {
364 double secondsPerStep =
365 elapsedTimeSeconds[i] / static_cast<double>(mTrialNumStepsPerformed);
366 double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
367
368 perf_test::MetricInfo metricInfo;
369 std::string units;
370 // Lazily register the metric, re-using the existing units if it is
371 // already registered.
372 if (!mReporter->GetMetricInfo(clockNames[i], &metricInfo))
373 {
374 printf("Seconds per iteration: %lf\n", secondsPerIteration);
375 units = secondsPerIteration > 1e-3 ? "us" : "ns";
376 mReporter->RegisterImportantMetric(clockNames[i], units);
377 }
378 else
379 {
380 units = metricInfo.units;
381 }
382
383 if (units == "ms")
384 {
385 retValue = secondsPerIteration * kMilliSecondsPerSecond;
386 }
387 else if (units == "us")
388 {
389 retValue = secondsPerIteration * kMicroSecondsPerSecond;
390 }
391 else
392 {
393 retValue = secondsPerIteration * kNanoSecondsPerSecond;
394 }
395 mReporter->AddResult(clockNames[i], retValue);
396 }
397
398 if (gVerboseLogging)
399 {
400 double fps = static_cast<double>(mTrialNumStepsPerformed * mIterationsPerStep) /
401 elapsedTimeSeconds[0];
402 printf("Ran %0.2lf iterations per second\n", fps);
403 }
404
405 mReporter->AddResult(".trial_steps", static_cast<size_t>(mTrialNumStepsPerformed));
406 mReporter->AddResult(".total_steps", static_cast<size_t>(mTotalNumStepsPerformed));
407
408 // Output histogram JSON set format if enabled.
409 double secondsPerStep = elapsedTimeSeconds[0] / static_cast<double>(mTrialNumStepsPerformed);
410 double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
411 TestSuite::GetInstance()->addHistogramSample(
412 mName + mBackend, mStory, secondsPerIteration * kMilliSecondsPerSecond, "msBestFitFormat");
413 return retValue;
414 }
415
normalizedTime(size_t value) const416 double ANGLEPerfTest::normalizedTime(size_t value) const
417 {
418 return static_cast<double>(value) / static_cast<double>(mTrialNumStepsPerformed);
419 }
420
calibrateStepsToRun()421 void ANGLEPerfTest::calibrateStepsToRun()
422 {
423 doRunLoop(gCalibrationTimeSeconds, std::numeric_limits<int>::max(),
424 RunLoopPolicy::FinishEveryStep);
425
426 double elapsedTime = mTimer.getElapsedTime();
427
428 // Scale steps down according to the time that exeeded one second.
429 double scale = gCalibrationTimeSeconds / elapsedTime;
430 mStepsToRun = static_cast<unsigned int>(static_cast<double>(mTrialNumStepsPerformed) * scale);
431 mStepsToRun = std::max(1, mStepsToRun);
432
433 if (gVerboseLogging)
434 {
435 printf(
436 "Running %d steps (calibration took %.2lf seconds). Expecting trial time of %.2lf "
437 "seconds.\n",
438 mStepsToRun, elapsedTime,
439 mStepsToRun * (elapsedTime / static_cast<double>(mTrialNumStepsPerformed)));
440 }
441
442 // Calibration allows the perf test runner script to save some time.
443 if (gCalibration)
444 {
445 mReporter->AddResult(".steps", static_cast<size_t>(mStepsToRun));
446 return;
447 }
448 }
449
backend() const450 std::string RenderTestParams::backend() const
451 {
452 std::stringstream strstr;
453
454 switch (driver)
455 {
456 case angle::GLESDriverType::AngleEGL:
457 break;
458 case angle::GLESDriverType::SystemWGL:
459 case angle::GLESDriverType::SystemEGL:
460 strstr << "_native";
461 break;
462 default:
463 assert(0);
464 return "_unk";
465 }
466
467 switch (getRenderer())
468 {
469 case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
470 break;
471 case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE:
472 strstr << "_d3d11";
473 break;
474 case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE:
475 strstr << "_d3d9";
476 break;
477 case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
478 strstr << "_gl";
479 break;
480 case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
481 strstr << "_gles";
482 break;
483 case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
484 strstr << "_vulkan";
485 break;
486 default:
487 assert(0);
488 return "_unk";
489 }
490
491 switch (eglParameters.deviceType)
492 {
493 case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE:
494 strstr << "_null";
495 break;
496 case EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE:
497 strstr << "_swiftshader";
498 break;
499 default:
500 break;
501 }
502
503 return strstr.str();
504 }
505
story() const506 std::string RenderTestParams::story() const
507 {
508 switch (surfaceType)
509 {
510 case SurfaceType::Window:
511 return "";
512 case SurfaceType::WindowWithVSync:
513 return "_vsync";
514 case SurfaceType::Offscreen:
515 return "_offscreen";
516 default:
517 UNREACHABLE();
518 return "";
519 }
520 }
521
backendAndStory() const522 std::string RenderTestParams::backendAndStory() const
523 {
524 return backend() + story();
525 }
526
ANGLERenderTest(const std::string & name,const RenderTestParams & testParams,const char * units)527 ANGLERenderTest::ANGLERenderTest(const std::string &name,
528 const RenderTestParams &testParams,
529 const char *units)
530 : ANGLEPerfTest(name,
531 testParams.backend(),
532 testParams.story(),
533 OneFrame() ? 1 : testParams.iterationsPerStep,
534 units),
535 mTestParams(testParams),
536 mIsTimestampQueryAvailable(false),
537 mGLWindow(nullptr),
538 mOSWindow(nullptr),
539 mSwapEnabled(true)
540 {
541 // Force fast tests to make sure our slowest bots don't time out.
542 if (OneFrame())
543 {
544 const_cast<RenderTestParams &>(testParams).iterationsPerStep = 1;
545 }
546
547 // Try to ensure we don't trigger allocation during execution.
548 mTraceEventBuffer.reserve(kInitialTraceEventBufferSize);
549
550 switch (testParams.driver)
551 {
552 case angle::GLESDriverType::AngleEGL:
553 mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
554 mEntryPointsLib.reset(angle::OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME,
555 angle::SearchType::ApplicationDir));
556 break;
557 case angle::GLESDriverType::SystemEGL:
558 #if defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
559 mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
560 mEntryPointsLib.reset(
561 angle::OpenSharedLibraryWithExtension(GetNativeEGLLibraryNameWithExtension()));
562 #else
563 std::cerr << "Not implemented." << std::endl;
564 mSkipTest = true;
565 #endif // defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
566 break;
567 case angle::GLESDriverType::SystemWGL:
568 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
569 mGLWindow = WGLWindow::New(testParams.majorVersion, testParams.minorVersion);
570 mEntryPointsLib.reset(
571 angle::OpenSharedLibrary("opengl32", angle::SearchType::SystemDir));
572 #else
573 std::cout << "WGL driver not available. Skipping test." << std::endl;
574 mSkipTest = true;
575 #endif // defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
576 break;
577 default:
578 std::cerr << "Error in switch." << std::endl;
579 mSkipTest = true;
580 break;
581 }
582 }
583
~ANGLERenderTest()584 ANGLERenderTest::~ANGLERenderTest()
585 {
586 OSWindow::Delete(&mOSWindow);
587 GLWindowBase::Delete(&mGLWindow);
588 }
589
addExtensionPrerequisite(const char * extensionName)590 void ANGLERenderTest::addExtensionPrerequisite(const char *extensionName)
591 {
592 mExtensionPrerequisites.push_back(extensionName);
593 }
594
SetUp()595 void ANGLERenderTest::SetUp()
596 {
597 if (mSkipTest)
598 {
599 return;
600 }
601
602 ANGLEPerfTest::SetUp();
603
604 // Set a consistent CPU core affinity and high priority.
605 angle::StabilizeCPUForBenchmarking();
606
607 mOSWindow = OSWindow::New();
608
609 if (!mGLWindow)
610 {
611 mSkipTest = true;
612 return;
613 }
614
615 mPlatformMethods.overrideWorkaroundsD3D = OverrideWorkaroundsD3D;
616 mPlatformMethods.logError = CustomLogError;
617 mPlatformMethods.logWarning = EmptyPlatformMethod;
618 mPlatformMethods.logInfo = EmptyPlatformMethod;
619 mPlatformMethods.addTraceEvent = AddPerfTraceEvent;
620 mPlatformMethods.getTraceCategoryEnabledFlag = GetPerfTraceCategoryEnabled;
621 mPlatformMethods.updateTraceEventDuration = UpdateTraceEventDuration;
622 mPlatformMethods.monotonicallyIncreasingTime = MonotonicallyIncreasingTime;
623 mPlatformMethods.context = this;
624
625 if (!mOSWindow->initialize(mName, mTestParams.windowWidth, mTestParams.windowHeight))
626 {
627 mSkipTest = true;
628 FAIL() << "Failed initializing OSWindow";
629 // FAIL returns.
630 }
631
632 // Override platform method parameter.
633 EGLPlatformParameters withMethods = mTestParams.eglParameters;
634 withMethods.platformMethods = &mPlatformMethods;
635
636 // Request a common framebuffer config
637 mConfigParams.redBits = 8;
638 mConfigParams.greenBits = 8;
639 mConfigParams.blueBits = 8;
640 mConfigParams.alphaBits = 8;
641 mConfigParams.depthBits = 24;
642 mConfigParams.stencilBits = 8;
643
644 if (!mGLWindow->initializeGL(mOSWindow, mEntryPointsLib.get(), mTestParams.driver, withMethods,
645 mConfigParams))
646 {
647 mSkipTest = true;
648 FAIL() << "Failed initializing GL Window";
649 // FAIL returns.
650 }
651
652 // Disable vsync.
653 if (mTestParams.surfaceType != SurfaceType::WindowWithVSync)
654 {
655 if (!mGLWindow->setSwapInterval(0))
656 {
657 mSkipTest = true;
658 FAIL() << "Failed setting swap interval";
659 // FAIL returns.
660 }
661 }
662
663 mIsTimestampQueryAvailable = IsGLExtensionEnabled("GL_EXT_disjoint_timer_query");
664
665 if (!areExtensionPrerequisitesFulfilled())
666 {
667 mSkipTest = true;
668 }
669
670 if (mSkipTest)
671 {
672 return;
673 }
674
675 #if defined(ANGLE_ENABLE_ASSERTS)
676 if (IsGLExtensionEnabled("GL_KHR_debug"))
677 {
678 EnableDebugCallback(this);
679 }
680 #endif
681
682 initializeBenchmark();
683
684 if (mTestParams.iterationsPerStep == 0)
685 {
686 mSkipTest = true;
687 FAIL() << "Please initialize 'iterationsPerStep'.";
688 // FAIL returns.
689 }
690
691 mTestTrialResults.reserve(gTestTrials);
692
693 // Capture a screenshot if enabled.
694 if (gScreenShotDir != nullptr)
695 {
696 std::stringstream screenshotNameStr;
697 screenshotNameStr << gScreenShotDir << GetPathSeparator() << "angle" << mBackend << "_"
698 << mStory << ".png";
699 std::string screenshotName = screenshotNameStr.str();
700 saveScreenshot(screenshotName);
701 }
702
703 for (int loopIndex = 0; loopIndex < gWarmupLoops; ++loopIndex)
704 {
705 doRunLoop(gCalibrationTimeSeconds, std::numeric_limits<int>::max(),
706 RunLoopPolicy::FinishEveryStep);
707 if (gVerboseLogging)
708 {
709 printf("Warm-up loop took %.2lf seconds.\n", mTimer.getElapsedTime());
710 }
711 }
712
713 if (mStepsToRun <= 0)
714 {
715 calibrateStepsToRun();
716 }
717 }
718
TearDown()719 void ANGLERenderTest::TearDown()
720 {
721 if (!mSkipTest)
722 {
723 destroyBenchmark();
724 }
725
726 if (mGLWindow)
727 {
728 mGLWindow->destroyGL();
729 mGLWindow = nullptr;
730 }
731
732 if (mOSWindow)
733 {
734 mOSWindow->destroy();
735 mOSWindow = nullptr;
736 }
737
738 // Dump trace events to json file.
739 if (gEnableTrace)
740 {
741 DumpTraceEventsToJSONFile(mTraceEventBuffer, gTraceFile);
742 }
743
744 ANGLEPerfTest::TearDown();
745 }
746
beginInternalTraceEvent(const char * name)747 void ANGLERenderTest::beginInternalTraceEvent(const char *name)
748 {
749 if (gEnableTrace)
750 {
751 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[0].name, name,
752 MonotonicallyIncreasingTime(&mPlatformMethods),
753 getCurrentThreadSerial());
754 }
755 }
756
endInternalTraceEvent(const char * name)757 void ANGLERenderTest::endInternalTraceEvent(const char *name)
758 {
759 if (gEnableTrace)
760 {
761 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[0].name, name,
762 MonotonicallyIncreasingTime(&mPlatformMethods),
763 getCurrentThreadSerial());
764 }
765 }
766
beginGLTraceEvent(const char * name,double hostTimeSec)767 void ANGLERenderTest::beginGLTraceEvent(const char *name, double hostTimeSec)
768 {
769 if (gEnableTrace)
770 {
771 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[1].name, name,
772 hostTimeSec, getCurrentThreadSerial());
773 }
774 }
775
endGLTraceEvent(const char * name,double hostTimeSec)776 void ANGLERenderTest::endGLTraceEvent(const char *name, double hostTimeSec)
777 {
778 if (gEnableTrace)
779 {
780 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[1].name, name,
781 hostTimeSec, getCurrentThreadSerial());
782 }
783 }
784
step()785 void ANGLERenderTest::step()
786 {
787 beginInternalTraceEvent("step");
788
789 // Clear events that the application did not process from this frame
790 Event event;
791 bool closed = false;
792 while (popEvent(&event))
793 {
794 // If the application did not catch a close event, close now
795 if (event.Type == Event::EVENT_CLOSED)
796 {
797 closed = true;
798 }
799 }
800
801 if (closed)
802 {
803 abortTest();
804 }
805 else
806 {
807 drawBenchmark();
808
809 // Swap is needed so that the GPU driver will occasionally flush its
810 // internal command queue to the GPU. This is enabled for null back-end
811 // devices because some back-ends (e.g. Vulkan) also accumulate internal
812 // command queues.
813 if (mSwapEnabled)
814 {
815 mGLWindow->swap();
816 }
817 mOSWindow->messageLoop();
818
819 #if defined(ANGLE_ENABLE_ASSERTS)
820 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
821 #endif // defined(ANGLE_ENABLE_ASSERTS)
822 }
823
824 endInternalTraceEvent("step");
825 }
826
startGpuTimer()827 void ANGLERenderTest::startGpuTimer()
828 {
829 if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
830 {
831 glGenQueriesEXT(1, &mCurrentTimestampBeginQuery);
832 glQueryCounterEXT(mCurrentTimestampBeginQuery, GL_TIMESTAMP_EXT);
833 }
834 }
835
stopGpuTimer()836 void ANGLERenderTest::stopGpuTimer()
837 {
838 if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
839 {
840 GLuint endQuery = 0;
841 glGenQueriesEXT(1, &endQuery);
842 glQueryCounterEXT(endQuery, GL_TIMESTAMP_EXT);
843 mTimestampQueries.push_back({mCurrentTimestampBeginQuery, endQuery});
844 }
845 }
846
computeGPUTime()847 void ANGLERenderTest::computeGPUTime()
848 {
849 if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
850 {
851 for (const TimestampSample &sample : mTimestampQueries)
852 {
853 uint64_t beginGLTimeNs = 0;
854 uint64_t endGLTimeNs = 0;
855 glGetQueryObjectui64vEXT(sample.beginQuery, GL_QUERY_RESULT_EXT, &beginGLTimeNs);
856 glGetQueryObjectui64vEXT(sample.endQuery, GL_QUERY_RESULT_EXT, &endGLTimeNs);
857 glDeleteQueriesEXT(1, &sample.beginQuery);
858 glDeleteQueriesEXT(1, &sample.endQuery);
859 mGPUTimeNs += endGLTimeNs - beginGLTimeNs;
860 }
861
862 mTimestampQueries.clear();
863 }
864 }
865
startTest()866 void ANGLERenderTest::startTest() {}
867
finishTest()868 void ANGLERenderTest::finishTest()
869 {
870 if (mTestParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE &&
871 !gNoFinish)
872 {
873 glFinish();
874 }
875 }
876
popEvent(Event * event)877 bool ANGLERenderTest::popEvent(Event *event)
878 {
879 return mOSWindow->popEvent(event);
880 }
881
getWindow()882 OSWindow *ANGLERenderTest::getWindow()
883 {
884 return mOSWindow;
885 }
886
getGLWindow()887 GLWindowBase *ANGLERenderTest::getGLWindow()
888 {
889 return mGLWindow;
890 }
891
areExtensionPrerequisitesFulfilled() const892 bool ANGLERenderTest::areExtensionPrerequisitesFulfilled() const
893 {
894 for (const char *extension : mExtensionPrerequisites)
895 {
896 if (!CheckExtensionExists(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
897 extension))
898 {
899 std::cout << "Test skipped due to missing extension: " << extension << std::endl;
900 return false;
901 }
902 }
903 return true;
904 }
905
setWebGLCompatibilityEnabled(bool webglCompatibility)906 void ANGLERenderTest::setWebGLCompatibilityEnabled(bool webglCompatibility)
907 {
908 mConfigParams.webGLCompatibility = webglCompatibility;
909 }
910
setRobustResourceInit(bool enabled)911 void ANGLERenderTest::setRobustResourceInit(bool enabled)
912 {
913 mConfigParams.robustResourceInit = enabled;
914 }
915
getTraceEventBuffer()916 std::vector<TraceEvent> &ANGLERenderTest::getTraceEventBuffer()
917 {
918 return mTraceEventBuffer;
919 }
920
onErrorMessage(const char * errorMessage)921 void ANGLERenderTest::onErrorMessage(const char *errorMessage)
922 {
923 abortTest();
924 FAIL() << "Failing test because of unexpected internal ANGLE error:\n" << errorMessage << "\n";
925 }
926
getCurrentThreadSerial()927 uint32_t ANGLERenderTest::getCurrentThreadSerial()
928 {
929 std::thread::id id = std::this_thread::get_id();
930
931 for (uint32_t serial = 0; serial < static_cast<uint32_t>(mThreadIDs.size()); ++serial)
932 {
933 if (mThreadIDs[serial] == id)
934 {
935 return serial + 1;
936 }
937 }
938
939 mThreadIDs.push_back(id);
940 return static_cast<uint32_t>(mThreadIDs.size());
941 }
942
943 namespace angle
944 {
GetHostTimeSeconds()945 double GetHostTimeSeconds()
946 {
947 // Move the time origin to the first call to this function, to avoid generating unnecessarily
948 // large timestamps.
949 static double origin = angle::GetCurrentTime();
950 return angle::GetCurrentTime() - origin;
951 }
952 } // namespace angle
953