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