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(angle::PlatformMethods *,const char *)60 void EmptyPlatformMethod(angle::PlatformMethods *, const char *) {}
61
CustomLogError(angle::PlatformMethods * platform,const char * errorMessage)62 void CustomLogError(angle::PlatformMethods *platform, const char *errorMessage)
63 {
64 auto *angleRenderTest = static_cast<ANGLERenderTest *>(platform->context);
65 angleRenderTest->onErrorMessage(errorMessage);
66 }
67
OverrideWorkaroundsD3D(angle::PlatformMethods * platform,angle::FeaturesD3D * featuresD3D)68 void OverrideWorkaroundsD3D(angle::PlatformMethods *platform, angle::FeaturesD3D *featuresD3D)
69 {
70 auto *angleRenderTest = static_cast<ANGLERenderTest *>(platform->context);
71 angleRenderTest->overrideWorkaroundsD3D(featuresD3D);
72 }
73
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)74 angle::TraceEventHandle AddPerfTraceEvent(angle::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(angle::PlatformMethods * platform,const char * categoryName)106 const unsigned char *GetPerfTraceCategoryEnabled(angle::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(angle::PlatformMethods * platform,const unsigned char * categoryEnabledFlag,const char * name,angle::TraceEventHandle eventHandle)124 void UpdateTraceEventDuration(angle::PlatformMethods *platform,
125 const unsigned char *categoryEnabledFlag,
126 const char *name,
127 angle::TraceEventHandle eventHandle)
128 {
129 // Not implemented.
130 }
131
MonotonicallyIncreasingTime(angle::PlatformMethods * platform)132 double MonotonicallyIncreasingTime(angle::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 } // anonymous namespace
218
TraceEvent(char phaseIn,const char * categoryNameIn,const char * nameIn,double timestampIn,uint32_t tidIn)219 TraceEvent::TraceEvent(char phaseIn,
220 const char *categoryNameIn,
221 const char *nameIn,
222 double timestampIn,
223 uint32_t tidIn)
224 : phase(phaseIn), categoryName(categoryNameIn), name{}, timestamp(timestampIn), tid(tidIn)
225 {
226 ASSERT(strlen(nameIn) < kMaxNameLen);
227 strcpy(name, nameIn);
228 }
229
ANGLEPerfTest(const std::string & name,const std::string & backend,const std::string & story,unsigned int iterationsPerStep,const char * units)230 ANGLEPerfTest::ANGLEPerfTest(const std::string &name,
231 const std::string &backend,
232 const std::string &story,
233 unsigned int iterationsPerStep,
234 const char *units)
235 : mName(name),
236 mBackend(backend),
237 mStory(story),
238 mGPUTimeNs(0),
239 mSkipTest(false),
240 mStepsToRun(std::max(gStepsPerTrial, gMaxStepsPerformed)),
241 mTrialNumStepsPerformed(0),
242 mTotalNumStepsPerformed(0),
243 mIterationsPerStep(iterationsPerStep),
244 mRunning(true)
245 {
246 if (mStory == "")
247 {
248 mStory = "baseline_story";
249 }
250 if (mStory[0] == '_')
251 {
252 mStory = mStory.substr(1);
253 }
254 mReporter = std::make_unique<perf_test::PerfResultReporter>(mName + mBackend, mStory);
255 mReporter->RegisterImportantMetric(".wall_time", units);
256 mReporter->RegisterImportantMetric(".gpu_time", units);
257 mReporter->RegisterFyiMetric(".trial_steps", "count");
258 mReporter->RegisterFyiMetric(".total_steps", "count");
259 mReporter->RegisterFyiMetric(".steps_to_run", "count");
260 }
261
~ANGLEPerfTest()262 ANGLEPerfTest::~ANGLEPerfTest() {}
263
run()264 void ANGLEPerfTest::run()
265 {
266 if (mSkipTest)
267 {
268 return;
269 }
270
271 if (mStepsToRun <= 0)
272 {
273 // We don't call finish between calibration steps when calibrating non-Render tests. The
274 // Render tests will have already calibrated when this code is run.
275 calibrateStepsToRun(RunLoopPolicy::RunContinuously);
276 ASSERT(mStepsToRun > 0);
277 }
278
279 uint32_t numTrials = OneFrame() ? 1 : gTestTrials;
280 if (gVerboseLogging)
281 {
282 printf("Test Trials: %d\n", static_cast<int>(numTrials));
283 }
284
285 for (uint32_t trial = 0; trial < numTrials; ++trial)
286 {
287 doRunLoop(gMaxTrialTimeSeconds, mStepsToRun, RunLoopPolicy::RunContinuously);
288 printResults();
289 if (gVerboseLogging)
290 {
291 double trialTime = mTimer.getElapsedTime();
292 printf("Trial %d time: %.2lf seconds.\n", trial + 1, trialTime);
293
294 double secondsPerStep = trialTime / static_cast<double>(mTrialNumStepsPerformed);
295 double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
296 mTestTrialResults.push_back(secondsPerIteration * 1000.0);
297 }
298 }
299
300 if (gVerboseLogging)
301 {
302 double numResults = static_cast<double>(mTestTrialResults.size());
303 double mean = 0;
304 for (double trialResult : mTestTrialResults)
305 {
306 mean += trialResult;
307 }
308 mean /= numResults;
309
310 double variance = 0;
311 for (double trialResult : mTestTrialResults)
312 {
313 double difference = trialResult - mean;
314 variance += difference * difference;
315 }
316 variance /= numResults;
317
318 double standardDeviation = std::sqrt(variance);
319 double coefficientOfVariation = standardDeviation / mean;
320
321 if (mean < 0.001)
322 {
323 printf("Mean result time: %.4lf ns.\n", mean * 1000.0);
324 }
325 else
326 {
327 printf("Mean result time: %.4lf ms.\n", mean);
328 }
329 printf("Coefficient of variation: %.2lf%%\n", coefficientOfVariation * 100.0);
330 }
331 }
332
doRunLoop(double maxRunTime,int maxStepsToRun,RunLoopPolicy runPolicy)333 void ANGLEPerfTest::doRunLoop(double maxRunTime, int maxStepsToRun, RunLoopPolicy runPolicy)
334 {
335 mTrialNumStepsPerformed = 0;
336 mRunning = true;
337 mGPUTimeNs = 0;
338 mTimer.start();
339 startTest();
340
341 while (mRunning)
342 {
343 step();
344
345 if (runPolicy == RunLoopPolicy::FinishEveryStep)
346 {
347 glFinish();
348 }
349
350 if (mRunning)
351 {
352 mTrialNumStepsPerformed++;
353 mTotalNumStepsPerformed++;
354 if (gMaxStepsPerformed > 0 && mTotalNumStepsPerformed >= gMaxStepsPerformed)
355 {
356 mRunning = false;
357 }
358 else if (mTimer.getElapsedTime() > maxRunTime)
359 {
360 mRunning = false;
361 }
362 else if (mTrialNumStepsPerformed >= maxStepsToRun)
363 {
364 mRunning = false;
365 }
366 }
367 }
368 finishTest();
369 mTimer.stop();
370 computeGPUTime();
371 }
372
SetUp()373 void ANGLEPerfTest::SetUp() {}
374
TearDown()375 void ANGLEPerfTest::TearDown() {}
376
printResults()377 double ANGLEPerfTest::printResults()
378 {
379 double elapsedTimeSeconds[2] = {
380 mTimer.getElapsedTime(),
381 mGPUTimeNs * 1e-9,
382 };
383
384 const char *clockNames[2] = {
385 ".wall_time",
386 ".gpu_time",
387 };
388
389 // If measured gpu time is non-zero, print that too.
390 size_t clocksToOutput = mGPUTimeNs > 0 ? 2 : 1;
391
392 double retValue = 0.0;
393 for (size_t i = 0; i < clocksToOutput; ++i)
394 {
395 double secondsPerStep =
396 elapsedTimeSeconds[i] / static_cast<double>(mTrialNumStepsPerformed);
397 double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
398
399 perf_test::MetricInfo metricInfo;
400 std::string units;
401 // Lazily register the metric, re-using the existing units if it is
402 // already registered.
403 if (!mReporter->GetMetricInfo(clockNames[i], &metricInfo))
404 {
405 printf("Seconds per iteration: %lf\n", secondsPerIteration);
406 units = secondsPerIteration > 1e-3 ? "us" : "ns";
407 mReporter->RegisterImportantMetric(clockNames[i], units);
408 }
409 else
410 {
411 units = metricInfo.units;
412 }
413
414 if (units == "ms")
415 {
416 retValue = secondsPerIteration * kMilliSecondsPerSecond;
417 }
418 else if (units == "us")
419 {
420 retValue = secondsPerIteration * kMicroSecondsPerSecond;
421 }
422 else
423 {
424 retValue = secondsPerIteration * kNanoSecondsPerSecond;
425 }
426 mReporter->AddResult(clockNames[i], retValue);
427 }
428
429 if (gVerboseLogging)
430 {
431 double fps = static_cast<double>(mTrialNumStepsPerformed * mIterationsPerStep) /
432 elapsedTimeSeconds[0];
433 printf("Ran %0.2lf iterations per second\n", fps);
434 }
435
436 if (gCalibration)
437 {
438 mReporter->AddResult(".steps_to_run", static_cast<size_t>(mStepsToRun));
439 }
440 else
441 {
442 mReporter->AddResult(".trial_steps", static_cast<size_t>(mTrialNumStepsPerformed));
443 mReporter->AddResult(".total_steps", static_cast<size_t>(mTotalNumStepsPerformed));
444 }
445
446 // Output histogram JSON set format if enabled.
447 double secondsPerStep = elapsedTimeSeconds[0] / static_cast<double>(mTrialNumStepsPerformed);
448 double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
449 TestSuite::GetInstance()->addHistogramSample(
450 mName + mBackend, mStory, secondsPerIteration * kMilliSecondsPerSecond, "msBestFitFormat");
451 return retValue;
452 }
453
normalizedTime(size_t value) const454 double ANGLEPerfTest::normalizedTime(size_t value) const
455 {
456 return static_cast<double>(value) / static_cast<double>(mTrialNumStepsPerformed);
457 }
458
calibrateStepsToRun(RunLoopPolicy policy)459 void ANGLEPerfTest::calibrateStepsToRun(RunLoopPolicy policy)
460 {
461 doRunLoop(gCalibrationTimeSeconds, std::numeric_limits<int>::max(), policy);
462
463 double elapsedTime = mTimer.getElapsedTime();
464
465 // Scale steps down according to the time that exeeded one second.
466 double scale = gCalibrationTimeSeconds / elapsedTime;
467 mStepsToRun = static_cast<unsigned int>(static_cast<double>(mTrialNumStepsPerformed) * scale);
468 mStepsToRun = std::max(1, mStepsToRun);
469
470 if (getStepAlignment() != 1)
471 {
472 mStepsToRun = rx::roundUp(mStepsToRun, getStepAlignment());
473 }
474
475 if (gVerboseLogging)
476 {
477 printf(
478 "Running %d steps (calibration took %.2lf seconds). Expecting trial time of %.2lf "
479 "seconds.\n",
480 mStepsToRun, elapsedTime,
481 mStepsToRun * (elapsedTime / static_cast<double>(mTrialNumStepsPerformed)));
482 }
483
484 // Calibration allows the perf test runner script to save some time.
485 if (gCalibration)
486 {
487 printResults();
488 return;
489 }
490 }
491
getStepAlignment() const492 int ANGLEPerfTest::getStepAlignment() const
493 {
494 // Default: No special alignment rules.
495 return 1;
496 }
497
backend() const498 std::string RenderTestParams::backend() const
499 {
500 std::stringstream strstr;
501
502 switch (driver)
503 {
504 case angle::GLESDriverType::AngleEGL:
505 break;
506 case angle::GLESDriverType::SystemWGL:
507 case angle::GLESDriverType::SystemEGL:
508 strstr << "_native";
509 break;
510 default:
511 assert(0);
512 return "_unk";
513 }
514
515 switch (getRenderer())
516 {
517 case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
518 break;
519 case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE:
520 strstr << "_d3d11";
521 break;
522 case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE:
523 strstr << "_d3d9";
524 break;
525 case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
526 strstr << "_gl";
527 break;
528 case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
529 strstr << "_gles";
530 break;
531 case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
532 strstr << "_vulkan";
533 break;
534 default:
535 assert(0);
536 return "_unk";
537 }
538
539 switch (eglParameters.deviceType)
540 {
541 case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE:
542 strstr << "_null";
543 break;
544 case EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE:
545 strstr << "_swiftshader";
546 break;
547 default:
548 break;
549 }
550
551 return strstr.str();
552 }
553
story() const554 std::string RenderTestParams::story() const
555 {
556 switch (surfaceType)
557 {
558 case SurfaceType::Window:
559 return "";
560 case SurfaceType::WindowWithVSync:
561 return "_vsync";
562 case SurfaceType::Offscreen:
563 return "_offscreen";
564 default:
565 UNREACHABLE();
566 return "";
567 }
568 }
569
backendAndStory() const570 std::string RenderTestParams::backendAndStory() const
571 {
572 return backend() + story();
573 }
574
ANGLERenderTest(const std::string & name,const RenderTestParams & testParams,const char * units)575 ANGLERenderTest::ANGLERenderTest(const std::string &name,
576 const RenderTestParams &testParams,
577 const char *units)
578 : ANGLEPerfTest(name,
579 testParams.backend(),
580 testParams.story(),
581 OneFrame() ? 1 : testParams.iterationsPerStep,
582 units),
583 mTestParams(testParams),
584 mIsTimestampQueryAvailable(false),
585 mGLWindow(nullptr),
586 mOSWindow(nullptr),
587 mSwapEnabled(true)
588 {
589 // Force fast tests to make sure our slowest bots don't time out.
590 if (OneFrame())
591 {
592 const_cast<RenderTestParams &>(testParams).iterationsPerStep = 1;
593 }
594
595 // Try to ensure we don't trigger allocation during execution.
596 mTraceEventBuffer.reserve(kInitialTraceEventBufferSize);
597
598 switch (testParams.driver)
599 {
600 case angle::GLESDriverType::AngleEGL:
601 mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
602 mEntryPointsLib.reset(
603 angle::OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME, angle::SearchType::ModuleDir));
604 break;
605 case angle::GLESDriverType::SystemEGL:
606 #if defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
607 mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
608 mEntryPointsLib.reset(angle::OpenSharedLibraryWithExtension(
609 GetNativeEGLLibraryNameWithExtension(), SearchType::SystemDir));
610 #else
611 std::cerr << "Not implemented." << std::endl;
612 mSkipTest = true;
613 #endif // defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
614 break;
615 case angle::GLESDriverType::SystemWGL:
616 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
617 mGLWindow = WGLWindow::New(testParams.majorVersion, testParams.minorVersion);
618 mEntryPointsLib.reset(
619 angle::OpenSharedLibrary("opengl32", angle::SearchType::SystemDir));
620 #else
621 std::cout << "WGL driver not available. Skipping test." << std::endl;
622 mSkipTest = true;
623 #endif // defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
624 break;
625 default:
626 std::cerr << "Error in switch." << std::endl;
627 mSkipTest = true;
628 break;
629 }
630 }
631
~ANGLERenderTest()632 ANGLERenderTest::~ANGLERenderTest()
633 {
634 OSWindow::Delete(&mOSWindow);
635 GLWindowBase::Delete(&mGLWindow);
636 }
637
addExtensionPrerequisite(const char * extensionName)638 void ANGLERenderTest::addExtensionPrerequisite(const char *extensionName)
639 {
640 mExtensionPrerequisites.push_back(extensionName);
641 }
642
SetUp()643 void ANGLERenderTest::SetUp()
644 {
645 if (mSkipTest)
646 {
647 return;
648 }
649
650 ANGLEPerfTest::SetUp();
651
652 // Set a consistent CPU core affinity and high priority.
653 angle::StabilizeCPUForBenchmarking();
654
655 mOSWindow = OSWindow::New();
656
657 if (!mGLWindow)
658 {
659 mSkipTest = true;
660 return;
661 }
662
663 mPlatformMethods.overrideWorkaroundsD3D = OverrideWorkaroundsD3D;
664 mPlatformMethods.logError = CustomLogError;
665 mPlatformMethods.logWarning = EmptyPlatformMethod;
666 mPlatformMethods.logInfo = EmptyPlatformMethod;
667 mPlatformMethods.addTraceEvent = AddPerfTraceEvent;
668 mPlatformMethods.getTraceCategoryEnabledFlag = GetPerfTraceCategoryEnabled;
669 mPlatformMethods.updateTraceEventDuration = UpdateTraceEventDuration;
670 mPlatformMethods.monotonicallyIncreasingTime = MonotonicallyIncreasingTime;
671 mPlatformMethods.context = this;
672
673 if (!mOSWindow->initialize(mName, mTestParams.windowWidth, mTestParams.windowHeight))
674 {
675 mSkipTest = true;
676 FAIL() << "Failed initializing OSWindow";
677 // FAIL returns.
678 }
679
680 // Override platform method parameter.
681 EGLPlatformParameters withMethods = mTestParams.eglParameters;
682 withMethods.platformMethods = &mPlatformMethods;
683
684 // Request a common framebuffer config
685 mConfigParams.redBits = 8;
686 mConfigParams.greenBits = 8;
687 mConfigParams.blueBits = 8;
688 mConfigParams.alphaBits = 8;
689 mConfigParams.depthBits = 24;
690 mConfigParams.stencilBits = 8;
691
692 if (!mGLWindow->initializeGL(mOSWindow, mEntryPointsLib.get(), mTestParams.driver, withMethods,
693 mConfigParams))
694 {
695 mSkipTest = true;
696 FAIL() << "Failed initializing GL Window";
697 // FAIL returns.
698 }
699
700 // Disable vsync.
701 if (mTestParams.surfaceType != SurfaceType::WindowWithVSync)
702 {
703 if (!mGLWindow->setSwapInterval(0))
704 {
705 mSkipTest = true;
706 FAIL() << "Failed setting swap interval";
707 // FAIL returns.
708 }
709 }
710
711 mIsTimestampQueryAvailable = IsGLExtensionEnabled("GL_EXT_disjoint_timer_query");
712
713 if (!areExtensionPrerequisitesFulfilled())
714 {
715 mSkipTest = true;
716 }
717
718 if (mSkipTest)
719 {
720 return;
721 }
722
723 #if defined(ANGLE_ENABLE_ASSERTS)
724 if (IsGLExtensionEnabled("GL_KHR_debug"))
725 {
726 EnableDebugCallback(&PerfTestDebugCallback, this);
727 }
728 #endif
729
730 initializeBenchmark();
731
732 if (mTestParams.iterationsPerStep == 0)
733 {
734 mSkipTest = true;
735 FAIL() << "Please initialize 'iterationsPerStep'.";
736 // FAIL returns.
737 }
738
739 if (gVerboseLogging)
740 {
741 printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER));
742 printf("GL_VERSION: %s\n", glGetString(GL_VERSION));
743 }
744
745 mTestTrialResults.reserve(gTestTrials);
746
747 for (int loopIndex = 0; loopIndex < gWarmupLoops; ++loopIndex)
748 {
749 doRunLoop(gCalibrationTimeSeconds, std::numeric_limits<int>::max(),
750 RunLoopPolicy::FinishEveryStep);
751 if (gVerboseLogging)
752 {
753 printf("Warm-up loop took %.2lf seconds.\n", mTimer.getElapsedTime());
754 }
755 }
756
757 if (mStepsToRun <= 0)
758 {
759 // Ensure we always call Finish when calibrating Render tests. This completes our work
760 // beween calibration measurements.
761 calibrateStepsToRun(RunLoopPolicy::FinishEveryStep);
762 }
763 }
764
TearDown()765 void ANGLERenderTest::TearDown()
766 {
767 if (!mSkipTest)
768 {
769 destroyBenchmark();
770 }
771
772 if (mGLWindow)
773 {
774 mGLWindow->destroyGL();
775 mGLWindow = nullptr;
776 }
777
778 if (mOSWindow)
779 {
780 mOSWindow->destroy();
781 mOSWindow = nullptr;
782 }
783
784 // Dump trace events to json file.
785 if (gEnableTrace)
786 {
787 DumpTraceEventsToJSONFile(mTraceEventBuffer, gTraceFile);
788 }
789
790 ANGLEPerfTest::TearDown();
791 }
792
beginInternalTraceEvent(const char * name)793 void ANGLERenderTest::beginInternalTraceEvent(const char *name)
794 {
795 if (gEnableTrace)
796 {
797 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[0].name, name,
798 MonotonicallyIncreasingTime(&mPlatformMethods),
799 getCurrentThreadSerial());
800 }
801 }
802
endInternalTraceEvent(const char * name)803 void ANGLERenderTest::endInternalTraceEvent(const char *name)
804 {
805 if (gEnableTrace)
806 {
807 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[0].name, name,
808 MonotonicallyIncreasingTime(&mPlatformMethods),
809 getCurrentThreadSerial());
810 }
811 }
812
beginGLTraceEvent(const char * name,double hostTimeSec)813 void ANGLERenderTest::beginGLTraceEvent(const char *name, double hostTimeSec)
814 {
815 if (gEnableTrace)
816 {
817 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[1].name, name,
818 hostTimeSec, getCurrentThreadSerial());
819 }
820 }
821
endGLTraceEvent(const char * name,double hostTimeSec)822 void ANGLERenderTest::endGLTraceEvent(const char *name, double hostTimeSec)
823 {
824 if (gEnableTrace)
825 {
826 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[1].name, name,
827 hostTimeSec, getCurrentThreadSerial());
828 }
829 }
830
step()831 void ANGLERenderTest::step()
832 {
833 beginInternalTraceEvent("step");
834
835 // Clear events that the application did not process from this frame
836 Event event;
837 bool closed = false;
838 while (popEvent(&event))
839 {
840 // If the application did not catch a close event, close now
841 if (event.Type == Event::EVENT_CLOSED)
842 {
843 closed = true;
844 }
845 }
846
847 if (closed)
848 {
849 abortTest();
850 }
851 else
852 {
853 drawBenchmark();
854
855 // Swap is needed so that the GPU driver will occasionally flush its
856 // internal command queue to the GPU. This is enabled for null back-end
857 // devices because some back-ends (e.g. Vulkan) also accumulate internal
858 // command queues.
859 if (mSwapEnabled)
860 {
861 mGLWindow->swap();
862 }
863 mOSWindow->messageLoop();
864
865 #if defined(ANGLE_ENABLE_ASSERTS)
866 if (!gRetraceMode)
867 {
868 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
869 }
870 #endif // defined(ANGLE_ENABLE_ASSERTS)
871 }
872
873 endInternalTraceEvent("step");
874 }
875
startGpuTimer()876 void ANGLERenderTest::startGpuTimer()
877 {
878 if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
879 {
880 glGenQueriesEXT(1, &mCurrentTimestampBeginQuery);
881 glQueryCounterEXT(mCurrentTimestampBeginQuery, GL_TIMESTAMP_EXT);
882 }
883 }
884
stopGpuTimer()885 void ANGLERenderTest::stopGpuTimer()
886 {
887 if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
888 {
889 GLuint endQuery = 0;
890 glGenQueriesEXT(1, &endQuery);
891 glQueryCounterEXT(endQuery, GL_TIMESTAMP_EXT);
892 mTimestampQueries.push_back({mCurrentTimestampBeginQuery, endQuery});
893 }
894 }
895
computeGPUTime()896 void ANGLERenderTest::computeGPUTime()
897 {
898 if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
899 {
900 for (const TimestampSample &sample : mTimestampQueries)
901 {
902 uint64_t beginGLTimeNs = 0;
903 uint64_t endGLTimeNs = 0;
904 glGetQueryObjectui64vEXT(sample.beginQuery, GL_QUERY_RESULT_EXT, &beginGLTimeNs);
905 glGetQueryObjectui64vEXT(sample.endQuery, GL_QUERY_RESULT_EXT, &endGLTimeNs);
906 glDeleteQueriesEXT(1, &sample.beginQuery);
907 glDeleteQueriesEXT(1, &sample.endQuery);
908 mGPUTimeNs += endGLTimeNs - beginGLTimeNs;
909 }
910
911 mTimestampQueries.clear();
912 }
913 }
914
startTest()915 void ANGLERenderTest::startTest() {}
916
finishTest()917 void ANGLERenderTest::finishTest()
918 {
919 if (mTestParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE &&
920 !gNoFinish && !gRetraceMode)
921 {
922 glFinish();
923 }
924 }
925
popEvent(Event * event)926 bool ANGLERenderTest::popEvent(Event *event)
927 {
928 return mOSWindow->popEvent(event);
929 }
930
getWindow()931 OSWindow *ANGLERenderTest::getWindow()
932 {
933 return mOSWindow;
934 }
935
getGLWindow()936 GLWindowBase *ANGLERenderTest::getGLWindow()
937 {
938 return mGLWindow;
939 }
940
areExtensionPrerequisitesFulfilled() const941 bool ANGLERenderTest::areExtensionPrerequisitesFulfilled() const
942 {
943 for (const char *extension : mExtensionPrerequisites)
944 {
945 if (!CheckExtensionExists(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
946 extension))
947 {
948 std::cout << "Test skipped due to missing extension: " << extension << std::endl;
949 return false;
950 }
951 }
952 return true;
953 }
954
setWebGLCompatibilityEnabled(bool webglCompatibility)955 void ANGLERenderTest::setWebGLCompatibilityEnabled(bool webglCompatibility)
956 {
957 mConfigParams.webGLCompatibility = webglCompatibility;
958 }
959
setRobustResourceInit(bool enabled)960 void ANGLERenderTest::setRobustResourceInit(bool enabled)
961 {
962 mConfigParams.robustResourceInit = enabled;
963 }
964
getTraceEventBuffer()965 std::vector<TraceEvent> &ANGLERenderTest::getTraceEventBuffer()
966 {
967 return mTraceEventBuffer;
968 }
969
onErrorMessage(const char * errorMessage)970 void ANGLERenderTest::onErrorMessage(const char *errorMessage)
971 {
972 abortTest();
973 FAIL() << "Failing test because of unexpected error:\n" << errorMessage << "\n";
974 }
975
getCurrentThreadSerial()976 uint32_t ANGLERenderTest::getCurrentThreadSerial()
977 {
978 std::thread::id id = std::this_thread::get_id();
979
980 for (uint32_t serial = 0; serial < static_cast<uint32_t>(mThreadIDs.size()); ++serial)
981 {
982 if (mThreadIDs[serial] == id)
983 {
984 return serial + 1;
985 }
986 }
987
988 mThreadIDs.push_back(id);
989 return static_cast<uint32_t>(mThreadIDs.size());
990 }
991
992 namespace angle
993 {
GetHostTimeSeconds()994 double GetHostTimeSeconds()
995 {
996 // Move the time origin to the first call to this function, to avoid generating unnecessarily
997 // large timestamps.
998 static double origin = angle::GetCurrentTime();
999 return angle::GetCurrentTime() - origin;
1000 }
1001 } // namespace angle
1002