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 "third_party/perf/perf_test.h"
18 #include "third_party/trace_event/trace_event.h"
19 #include "util/shader_utils.h"
20 #include "util/test_utils.h"
21
22 #include <cassert>
23 #include <cmath>
24 #include <fstream>
25 #include <iostream>
26 #include <sstream>
27
28 #include <json/json.h>
29
30 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
31 # include "util/windows/WGLWindow.h"
32 #endif // defined(ANGLE_USE_UTIL_LOADER) &&defined(ANGLE_PLATFORM_WINDOWS)
33
34 using namespace angle;
35
36 namespace
37 {
38 constexpr size_t kInitialTraceEventBufferSize = 50000;
39 constexpr double kMicroSecondsPerSecond = 1e6;
40 constexpr double kNanoSecondsPerSecond = 1e9;
41 constexpr double kCalibrationRunTimeSeconds = 1.0;
42 constexpr double kMaximumRunTimeSeconds = 10.0;
43 constexpr unsigned int kNumTrials = 3;
44
45 struct TraceCategory
46 {
47 unsigned char enabled;
48 const char *name;
49 };
50
51 constexpr TraceCategory gTraceCategories[2] = {
52 {1, "gpu.angle"},
53 {1, "gpu.angle.gpu"},
54 };
55
EmptyPlatformMethod(angle::PlatformMethods *,const char *)56 void EmptyPlatformMethod(angle::PlatformMethods *, const char *) {}
57
OverrideWorkaroundsD3D(angle::PlatformMethods * platform,angle::FeaturesD3D * featuresD3D)58 void OverrideWorkaroundsD3D(angle::PlatformMethods *platform, angle::FeaturesD3D *featuresD3D)
59 {
60 auto *angleRenderTest = static_cast<ANGLERenderTest *>(platform->context);
61 angleRenderTest->overrideWorkaroundsD3D(featuresD3D);
62 }
63
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)64 angle::TraceEventHandle AddPerfTraceEvent(angle::PlatformMethods *platform,
65 char phase,
66 const unsigned char *categoryEnabledFlag,
67 const char *name,
68 unsigned long long id,
69 double timestamp,
70 int numArgs,
71 const char **argNames,
72 const unsigned char *argTypes,
73 const unsigned long long *argValues,
74 unsigned char flags)
75 {
76 if (!gEnableTrace)
77 return 0;
78
79 // Discover the category name based on categoryEnabledFlag. This flag comes from the first
80 // parameter of TraceCategory, and corresponds to one of the entries in gTraceCategories.
81 static_assert(offsetof(TraceCategory, enabled) == 0,
82 "|enabled| must be the first field of the TraceCategory class.");
83 const TraceCategory *category = reinterpret_cast<const TraceCategory *>(categoryEnabledFlag);
84
85 ANGLERenderTest *renderTest = static_cast<ANGLERenderTest *>(platform->context);
86 std::vector<TraceEvent> &buffer = renderTest->getTraceEventBuffer();
87 buffer.emplace_back(phase, category->name, name, timestamp);
88 return buffer.size();
89 }
90
GetPerfTraceCategoryEnabled(angle::PlatformMethods * platform,const char * categoryName)91 const unsigned char *GetPerfTraceCategoryEnabled(angle::PlatformMethods *platform,
92 const char *categoryName)
93 {
94 if (gEnableTrace)
95 {
96 for (const TraceCategory &category : gTraceCategories)
97 {
98 if (strcmp(category.name, categoryName) == 0)
99 {
100 return &category.enabled;
101 }
102 }
103 }
104
105 constexpr static unsigned char kZero = 0;
106 return &kZero;
107 }
108
UpdateTraceEventDuration(angle::PlatformMethods * platform,const unsigned char * categoryEnabledFlag,const char * name,angle::TraceEventHandle eventHandle)109 void UpdateTraceEventDuration(angle::PlatformMethods *platform,
110 const unsigned char *categoryEnabledFlag,
111 const char *name,
112 angle::TraceEventHandle eventHandle)
113 {
114 // Not implemented.
115 }
116
MonotonicallyIncreasingTime(angle::PlatformMethods * platform)117 double MonotonicallyIncreasingTime(angle::PlatformMethods *platform)
118 {
119 return GetHostTimeSeconds();
120 }
121
DumpTraceEventsToJSONFile(const std::vector<TraceEvent> & traceEvents,const char * outputFileName)122 void DumpTraceEventsToJSONFile(const std::vector<TraceEvent> &traceEvents,
123 const char *outputFileName)
124 {
125 Json::Value eventsValue(Json::arrayValue);
126
127 for (const TraceEvent &traceEvent : traceEvents)
128 {
129 Json::Value value(Json::objectValue);
130
131 std::stringstream phaseName;
132 phaseName << traceEvent.phase;
133
134 const auto microseconds =
135 static_cast<Json::LargestInt>(traceEvent.timestamp * 1000.0 * 1000.0);
136
137 value["name"] = traceEvent.name;
138 value["cat"] = traceEvent.categoryName;
139 value["ph"] = phaseName.str();
140 value["ts"] = microseconds;
141 value["pid"] = strcmp(traceEvent.categoryName, "gpu.angle.gpu") == 0 ? "GPU" : "ANGLE";
142 value["tid"] = 1;
143
144 eventsValue.append(value);
145 }
146
147 Json::Value root(Json::objectValue);
148 root["traceEvents"] = eventsValue;
149
150 std::ofstream outFile;
151 outFile.open(outputFileName);
152
153 Json::StyledWriter styledWrite;
154 outFile << styledWrite.write(root);
155
156 outFile.close();
157 }
158
DebugMessageCallback(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar * message,const void * userParam)159 ANGLE_MAYBE_UNUSED void KHRONOS_APIENTRY DebugMessageCallback(GLenum source,
160 GLenum type,
161 GLuint id,
162 GLenum severity,
163 GLsizei length,
164 const GLchar *message,
165 const void *userParam)
166 {
167 std::string sourceText = gl::GetDebugMessageSourceString(source);
168 std::string typeText = gl::GetDebugMessageTypeString(type);
169 std::string severityText = gl::GetDebugMessageSeverityString(severity);
170 std::cerr << sourceText << ", " << typeText << ", " << severityText << ": " << message << "\n";
171 }
172 } // anonymous namespace
173
TraceEvent(char phaseIn,const char * categoryNameIn,const char * nameIn,double timestampIn)174 TraceEvent::TraceEvent(char phaseIn,
175 const char *categoryNameIn,
176 const char *nameIn,
177 double timestampIn)
178 : phase(phaseIn), categoryName(categoryNameIn), name{}, timestamp(timestampIn), tid(1)
179 {
180 ASSERT(strlen(nameIn) < kMaxNameLen);
181 strcpy(name, nameIn);
182 }
183
ANGLEPerfTest(const std::string & name,const std::string & backend,const std::string & story,unsigned int iterationsPerStep)184 ANGLEPerfTest::ANGLEPerfTest(const std::string &name,
185 const std::string &backend,
186 const std::string &story,
187 unsigned int iterationsPerStep)
188 : mName(name),
189 mBackend(backend),
190 mStory(story),
191 mGPUTimeNs(0),
192 mSkipTest(false),
193 mStepsToRun(std::numeric_limits<unsigned int>::max()),
194 mNumStepsPerformed(0),
195 mIterationsPerStep(iterationsPerStep),
196 mRunning(true)
197 {
198 if (mStory == "")
199 {
200 mStory = "baseline_story";
201 }
202 if (mStory[0] == '_')
203 {
204 mStory = mStory.substr(1);
205 }
206 mReporter = std::make_unique<perf_test::PerfResultReporter>(mName + mBackend, mStory);
207 mReporter->RegisterImportantMetric(".wall_time", "ns");
208 mReporter->RegisterImportantMetric(".gpu_time", "ns");
209 mReporter->RegisterFyiMetric(".steps", "count");
210 }
211
~ANGLEPerfTest()212 ANGLEPerfTest::~ANGLEPerfTest() {}
213
run()214 void ANGLEPerfTest::run()
215 {
216 if (mSkipTest)
217 {
218 return;
219 }
220
221 // Calibrate to a fixed number of steps during an initial set time.
222 if (gStepsToRunOverride <= 0)
223 {
224 doRunLoop(kCalibrationRunTimeSeconds);
225
226 // Scale steps down according to the time that exeeded one second.
227 double scale = kCalibrationRunTimeSeconds / mTimer.getElapsedTime();
228 mStepsToRun = static_cast<unsigned int>(static_cast<double>(mNumStepsPerformed) * scale);
229
230 // Calibration allows the perf test runner script to save some time.
231 if (gCalibration)
232 {
233 mReporter->AddResult(".steps", static_cast<size_t>(mStepsToRun));
234 return;
235 }
236 }
237 else
238 {
239 mStepsToRun = gStepsToRunOverride;
240 }
241
242 // Check again for early exit.
243 if (mSkipTest)
244 {
245 return;
246 }
247
248 // Do another warmup run. Seems to consistently improve results.
249 doRunLoop(kMaximumRunTimeSeconds);
250
251 double totalTime = 0.0;
252 for (unsigned int trial = 0; trial < kNumTrials; ++trial)
253 {
254 doRunLoop(kMaximumRunTimeSeconds);
255 totalTime += printResults();
256 }
257 }
258
doRunLoop(double maxRunTime)259 void ANGLEPerfTest::doRunLoop(double maxRunTime)
260 {
261 mNumStepsPerformed = 0;
262 mRunning = true;
263 mTimer.start();
264 startTest();
265
266 while (mRunning)
267 {
268 step();
269 if (mRunning)
270 {
271 ++mNumStepsPerformed;
272 if (mTimer.getElapsedTime() > maxRunTime)
273 {
274 mRunning = false;
275 }
276 else if (mNumStepsPerformed >= mStepsToRun)
277 {
278 mRunning = false;
279 }
280 }
281 }
282 finishTest();
283 mTimer.stop();
284 }
285
SetUp()286 void ANGLEPerfTest::SetUp() {}
287
TearDown()288 void ANGLEPerfTest::TearDown() {}
289
printResults()290 double ANGLEPerfTest::printResults()
291 {
292 double elapsedTimeSeconds[2] = {
293 mTimer.getElapsedTime(),
294 mGPUTimeNs * 1e-9,
295 };
296
297 const char *clockNames[2] = {
298 ".wall_time",
299 ".gpu_time",
300 };
301
302 // If measured gpu time is non-zero, print that too.
303 size_t clocksToOutput = mGPUTimeNs > 0 ? 2 : 1;
304
305 double retValue = 0.0;
306 for (size_t i = 0; i < clocksToOutput; ++i)
307 {
308 double secondsPerStep = elapsedTimeSeconds[i] / static_cast<double>(mNumStepsPerformed);
309 double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
310
311 perf_test::MetricInfo metricInfo;
312 std::string units;
313 // Lazily register the metric, re-using the existing units if it is
314 // already registered.
315 if (!mReporter->GetMetricInfo(clockNames[i], &metricInfo))
316 {
317 units = secondsPerIteration > 1e-3 ? "us" : "ns";
318 mReporter->RegisterImportantMetric(clockNames[i], units);
319 }
320 else
321 {
322 units = metricInfo.units;
323 }
324
325 if (units == "us")
326 {
327 retValue = secondsPerIteration * kMicroSecondsPerSecond;
328 }
329 else
330 {
331 retValue = secondsPerIteration * kNanoSecondsPerSecond;
332 }
333 mReporter->AddResult(clockNames[i], retValue);
334 }
335 return retValue;
336 }
337
normalizedTime(size_t value) const338 double ANGLEPerfTest::normalizedTime(size_t value) const
339 {
340 return static_cast<double>(value) / static_cast<double>(mNumStepsPerformed);
341 }
342
backend() const343 std::string RenderTestParams::backend() const
344 {
345 std::stringstream strstr;
346
347 switch (driver)
348 {
349 case angle::GLESDriverType::AngleEGL:
350 break;
351 case angle::GLESDriverType::SystemEGL:
352 strstr << "_native";
353 break;
354 case angle::GLESDriverType::SystemWGL:
355 strstr << "_wgl";
356 break;
357 default:
358 assert(0);
359 return "_unk";
360 }
361
362 switch (getRenderer())
363 {
364 case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
365 break;
366 case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE:
367 strstr << "_d3d11";
368 break;
369 case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE:
370 strstr << "_d3d9";
371 break;
372 case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
373 strstr << "_gl";
374 break;
375 case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
376 strstr << "_gles";
377 break;
378 case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
379 strstr << "_vulkan";
380 break;
381 default:
382 assert(0);
383 return "_unk";
384 }
385
386 if (eglParameters.deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE)
387 {
388 strstr << "_null";
389 }
390
391 return strstr.str();
392 }
393
story() const394 std::string RenderTestParams::story() const
395 {
396 return "";
397 }
398
backendAndStory() const399 std::string RenderTestParams::backendAndStory() const
400 {
401 return backend() + story();
402 }
403
ANGLERenderTest(const std::string & name,const RenderTestParams & testParams)404 ANGLERenderTest::ANGLERenderTest(const std::string &name, const RenderTestParams &testParams)
405 : ANGLEPerfTest(name,
406 testParams.backend(),
407 testParams.story(),
408 OneFrame() ? 1 : testParams.iterationsPerStep),
409 mTestParams(testParams),
410 mIsTimestampQueryAvailable(false),
411 mGLWindow(nullptr),
412 mOSWindow(nullptr),
413 mSwapEnabled(true)
414 {
415 // Force fast tests to make sure our slowest bots don't time out.
416 if (OneFrame())
417 {
418 const_cast<RenderTestParams &>(testParams).iterationsPerStep = 1;
419 }
420
421 // Try to ensure we don't trigger allocation during execution.
422 mTraceEventBuffer.reserve(kInitialTraceEventBufferSize);
423
424 switch (testParams.driver)
425 {
426 case angle::GLESDriverType::AngleEGL:
427 mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
428 mEntryPointsLib.reset(angle::OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME,
429 angle::SearchType::ApplicationDir));
430 break;
431 case angle::GLESDriverType::SystemEGL:
432 #if defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
433 mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
434 mEntryPointsLib.reset(
435 angle::OpenSharedLibraryWithExtension(GetNativeEGLLibraryNameWithExtension()));
436 #else
437 std::cerr << "Not implemented." << std::endl;
438 mSkipTest = true;
439 #endif // defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
440 break;
441 case angle::GLESDriverType::SystemWGL:
442 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
443 mGLWindow = WGLWindow::New(testParams.majorVersion, testParams.minorVersion);
444 mEntryPointsLib.reset(
445 angle::OpenSharedLibrary("opengl32", angle::SearchType::SystemDir));
446 #else
447 std::cout << "WGL driver not available. Skipping test." << std::endl;
448 mSkipTest = true;
449 #endif // defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
450 break;
451 default:
452 std::cerr << "Error in switch." << std::endl;
453 mSkipTest = true;
454 break;
455 }
456 }
457
~ANGLERenderTest()458 ANGLERenderTest::~ANGLERenderTest()
459 {
460 OSWindow::Delete(&mOSWindow);
461 GLWindowBase::Delete(&mGLWindow);
462 }
463
addExtensionPrerequisite(const char * extensionName)464 void ANGLERenderTest::addExtensionPrerequisite(const char *extensionName)
465 {
466 mExtensionPrerequisites.push_back(extensionName);
467 }
468
SetUp()469 void ANGLERenderTest::SetUp()
470 {
471 if (mSkipTest)
472 {
473 return;
474 }
475
476 ANGLEPerfTest::SetUp();
477
478 // Set a consistent CPU core affinity and high priority.
479 angle::StabilizeCPUForBenchmarking();
480
481 mOSWindow = OSWindow::New();
482
483 if (!mGLWindow)
484 {
485 mSkipTest = true;
486 return;
487 }
488
489 mPlatformMethods.overrideWorkaroundsD3D = OverrideWorkaroundsD3D;
490 mPlatformMethods.logError = EmptyPlatformMethod;
491 mPlatformMethods.logWarning = EmptyPlatformMethod;
492 mPlatformMethods.logInfo = EmptyPlatformMethod;
493 mPlatformMethods.addTraceEvent = AddPerfTraceEvent;
494 mPlatformMethods.getTraceCategoryEnabledFlag = GetPerfTraceCategoryEnabled;
495 mPlatformMethods.updateTraceEventDuration = UpdateTraceEventDuration;
496 mPlatformMethods.monotonicallyIncreasingTime = MonotonicallyIncreasingTime;
497 mPlatformMethods.context = this;
498
499 if (!mOSWindow->initialize(mName, mTestParams.windowWidth, mTestParams.windowHeight))
500 {
501 mSkipTest = true;
502 FAIL() << "Failed initializing OSWindow";
503 // FAIL returns.
504 }
505
506 // Override platform method parameter.
507 EGLPlatformParameters withMethods = mTestParams.eglParameters;
508 withMethods.platformMethods = &mPlatformMethods;
509
510 // Request a common framebuffer config
511 mConfigParams.redBits = 8;
512 mConfigParams.greenBits = 8;
513 mConfigParams.blueBits = 8;
514 mConfigParams.alphaBits = 8;
515 mConfigParams.depthBits = 24;
516 mConfigParams.stencilBits = 8;
517
518 if (!mGLWindow->initializeGL(mOSWindow, mEntryPointsLib.get(), mTestParams.driver, withMethods,
519 mConfigParams))
520 {
521 mSkipTest = true;
522 FAIL() << "Failed initializing GL Window";
523 // FAIL returns.
524 }
525
526 // Disable vsync.
527 if (!mGLWindow->setSwapInterval(0))
528 {
529 mSkipTest = true;
530 FAIL() << "Failed setting swap interval";
531 // FAIL returns.
532 }
533
534 mIsTimestampQueryAvailable = IsGLExtensionEnabled("GL_EXT_disjoint_timer_query");
535
536 if (!areExtensionPrerequisitesFulfilled())
537 {
538 mSkipTest = true;
539 }
540
541 if (mSkipTest)
542 {
543 return;
544 }
545
546 #if defined(ANGLE_ENABLE_ASSERTS)
547 if (IsGLExtensionEnabled("GL_KHR_debug"))
548 {
549 glEnable(GL_DEBUG_OUTPUT);
550 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
551 // Enable medium and high priority messages.
552 glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_HIGH, 0, nullptr,
553 GL_TRUE);
554 glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_MEDIUM, 0, nullptr,
555 GL_TRUE);
556 // Disable low and notification priority messages.
557 glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_LOW, 0, nullptr,
558 GL_FALSE);
559 glDebugMessageControlKHR(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, 0,
560 nullptr, GL_FALSE);
561 // Disable medium priority performance messages to reduce spam.
562 glDebugMessageControlKHR(GL_DONT_CARE, GL_DEBUG_TYPE_PERFORMANCE, GL_DEBUG_SEVERITY_MEDIUM,
563 0, nullptr, GL_FALSE);
564 glDebugMessageCallbackKHR(DebugMessageCallback, this);
565 }
566 #endif
567
568 initializeBenchmark();
569
570 if (mTestParams.iterationsPerStep == 0)
571 {
572 mSkipTest = true;
573 FAIL() << "Please initialize 'iterationsPerStep'.";
574 // FAIL returns.
575 }
576
577 // Capture a screenshot if enabled.
578 if (gScreenShotDir != nullptr)
579 {
580 std::stringstream screenshotNameStr;
581 screenshotNameStr << gScreenShotDir << GetPathSeparator() << "angle" << mBackend << "_"
582 << mStory << ".png";
583 std::string screenshotName = screenshotNameStr.str();
584 saveScreenshot(screenshotName);
585 }
586 }
587
TearDown()588 void ANGLERenderTest::TearDown()
589 {
590 if (!mSkipTest)
591 {
592 destroyBenchmark();
593 }
594
595 if (mGLWindow)
596 {
597 mGLWindow->destroyGL();
598 mGLWindow = nullptr;
599 }
600
601 if (mOSWindow)
602 {
603 mOSWindow->destroy();
604 mOSWindow = nullptr;
605 }
606
607 // Dump trace events to json file.
608 if (gEnableTrace)
609 {
610 DumpTraceEventsToJSONFile(mTraceEventBuffer, gTraceFile);
611 }
612
613 ANGLEPerfTest::TearDown();
614 }
615
beginInternalTraceEvent(const char * name)616 void ANGLERenderTest::beginInternalTraceEvent(const char *name)
617 {
618 if (gEnableTrace)
619 {
620 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[0].name, name,
621 MonotonicallyIncreasingTime(&mPlatformMethods));
622 }
623 }
624
endInternalTraceEvent(const char * name)625 void ANGLERenderTest::endInternalTraceEvent(const char *name)
626 {
627 if (gEnableTrace)
628 {
629 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[0].name, name,
630 MonotonicallyIncreasingTime(&mPlatformMethods));
631 }
632 }
633
beginGLTraceEvent(const char * name,double hostTimeSec)634 void ANGLERenderTest::beginGLTraceEvent(const char *name, double hostTimeSec)
635 {
636 if (gEnableTrace)
637 {
638 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[1].name, name,
639 hostTimeSec);
640 }
641 }
642
endGLTraceEvent(const char * name,double hostTimeSec)643 void ANGLERenderTest::endGLTraceEvent(const char *name, double hostTimeSec)
644 {
645 if (gEnableTrace)
646 {
647 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[1].name, name,
648 hostTimeSec);
649 }
650 }
651
step()652 void ANGLERenderTest::step()
653 {
654 beginInternalTraceEvent("step");
655
656 // Clear events that the application did not process from this frame
657 Event event;
658 bool closed = false;
659 while (popEvent(&event))
660 {
661 // If the application did not catch a close event, close now
662 if (event.Type == Event::EVENT_CLOSED)
663 {
664 closed = true;
665 }
666 }
667
668 if (closed)
669 {
670 abortTest();
671 }
672 else
673 {
674 drawBenchmark();
675
676 // Swap is needed so that the GPU driver will occasionally flush its
677 // internal command queue to the GPU. This is enabled for null back-end
678 // devices because some back-ends (e.g. Vulkan) also accumulate internal
679 // command queues.
680 if (mSwapEnabled)
681 {
682 mGLWindow->swap();
683 }
684 mOSWindow->messageLoop();
685
686 #if defined(ANGLE_ENABLE_ASSERTS)
687 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
688 #endif // defined(ANGLE_ENABLE_ASSERTS)
689 }
690
691 endInternalTraceEvent("step");
692 }
693
startGpuTimer()694 void ANGLERenderTest::startGpuTimer()
695 {
696 if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
697 {
698 glBeginQueryEXT(GL_TIME_ELAPSED_EXT, mTimestampQuery);
699 }
700 }
701
stopGpuTimer()702 void ANGLERenderTest::stopGpuTimer()
703 {
704 if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
705 {
706 glEndQueryEXT(GL_TIME_ELAPSED_EXT);
707 uint64_t gpuTimeNs = 0;
708 glGetQueryObjectui64vEXT(mTimestampQuery, GL_QUERY_RESULT_EXT, &gpuTimeNs);
709
710 mGPUTimeNs += gpuTimeNs;
711 }
712 }
713
startTest()714 void ANGLERenderTest::startTest()
715 {
716 if (mTestParams.trackGpuTime)
717 {
718 glGenQueriesEXT(1, &mTimestampQuery);
719 mGPUTimeNs = 0;
720 }
721 }
722
finishTest()723 void ANGLERenderTest::finishTest()
724 {
725 if (mTestParams.trackGpuTime)
726 {
727 glDeleteQueriesEXT(1, &mTimestampQuery);
728 }
729 if (mTestParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE)
730 {
731 glFinish();
732 }
733 }
734
popEvent(Event * event)735 bool ANGLERenderTest::popEvent(Event *event)
736 {
737 return mOSWindow->popEvent(event);
738 }
739
getWindow()740 OSWindow *ANGLERenderTest::getWindow()
741 {
742 return mOSWindow;
743 }
744
getGLWindow()745 GLWindowBase *ANGLERenderTest::getGLWindow()
746 {
747 return mGLWindow;
748 }
749
areExtensionPrerequisitesFulfilled() const750 bool ANGLERenderTest::areExtensionPrerequisitesFulfilled() const
751 {
752 for (const char *extension : mExtensionPrerequisites)
753 {
754 if (!CheckExtensionExists(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
755 extension))
756 {
757 std::cout << "Test skipped due to missing extension: " << extension << std::endl;
758 return false;
759 }
760 }
761 return true;
762 }
763
setWebGLCompatibilityEnabled(bool webglCompatibility)764 void ANGLERenderTest::setWebGLCompatibilityEnabled(bool webglCompatibility)
765 {
766 mConfigParams.webGLCompatibility = webglCompatibility;
767 }
768
setRobustResourceInit(bool enabled)769 void ANGLERenderTest::setRobustResourceInit(bool enabled)
770 {
771 mConfigParams.robustResourceInit = enabled;
772 }
773
getTraceEventBuffer()774 std::vector<TraceEvent> &ANGLERenderTest::getTraceEventBuffer()
775 {
776 return mTraceEventBuffer;
777 }
778
779 namespace angle
780 {
GetHostTimeSeconds()781 double GetHostTimeSeconds()
782 {
783 // Move the time origin to the first call to this function, to avoid generating unnecessarily
784 // large timestamps.
785 static double origin = angle::GetCurrentTime();
786 return angle::GetCurrentTime() - origin;
787 }
788 } // namespace angle
789