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 #if defined(ANGLE_PLATFORM_ANDROID)
13 # include <android/log.h>
14 # include <dlfcn.h>
15 #endif
16 #include "ANGLEPerfTestArgs.h"
17 #include "common/base/anglebase/trace_event/trace_event.h"
18 #include "common/debug.h"
19 #include "common/gl_enum_utils.h"
20 #include "common/mathutil.h"
21 #include "common/platform.h"
22 #include "common/string_utils.h"
23 #include "common/system_utils.h"
24 #include "common/utilities.h"
25 #include "test_utils/runner/TestSuite.h"
26 #include "third_party/perf/perf_test.h"
27 #include "util/shader_utils.h"
28 #include "util/test_utils.h"
29
30 #if defined(ANGLE_PLATFORM_ANDROID)
31 # include "util/android/AndroidWindow.h"
32 #endif
33
34 #include <cassert>
35 #include <cmath>
36 #include <fstream>
37 #include <iostream>
38 #include <numeric>
39 #include <sstream>
40 #include <string>
41
42 #include <rapidjson/document.h>
43 #include <rapidjson/filewritestream.h>
44 #include <rapidjson/istreamwrapper.h>
45 #include <rapidjson/prettywriter.h>
46
47 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
48 # include "util/windows/WGLWindow.h"
49 #endif // defined(ANGLE_USE_UTIL_LOADER) &&defined(ANGLE_PLATFORM_WINDOWS)
50
51 using namespace angle;
52 namespace js = rapidjson;
53
54 namespace
55 {
56 constexpr size_t kInitialTraceEventBufferSize = 50000;
57 constexpr double kMilliSecondsPerSecond = 1e3;
58 constexpr double kMicroSecondsPerSecond = 1e6;
59 constexpr double kNanoSecondsPerSecond = 1e9;
60 constexpr size_t kNumberOfStepsPerformedToComputeGPUTime = 16;
61 constexpr char kPeakMemoryMetric[] = ".memory_max";
62 constexpr char kMedianMemoryMetric[] = ".memory_median";
63
64 struct TraceCategory
65 {
66 unsigned char enabled;
67 const char *name;
68 };
69
70 constexpr TraceCategory gTraceCategories[2] = {
71 {1, "gpu.angle"},
72 {1, "gpu.angle.gpu"},
73 };
74
EmptyPlatformMethod(PlatformMethods *,const char *)75 void EmptyPlatformMethod(PlatformMethods *, const char *) {}
76
CustomLogError(PlatformMethods * platform,const char * errorMessage)77 void CustomLogError(PlatformMethods *platform, const char *errorMessage)
78 {
79 auto *angleRenderTest = static_cast<ANGLERenderTest *>(platform->context);
80 angleRenderTest->onErrorMessage(errorMessage);
81 }
82
AddPerfTraceEvent(PlatformMethods * platform,char phase,const unsigned char * categoryEnabledFlag,const char * name,unsigned long long id,double,int numArgs,const char ** argNames,const unsigned char * argTypes,const unsigned long long * argValues,unsigned char flags)83 TraceEventHandle AddPerfTraceEvent(PlatformMethods *platform,
84 char phase,
85 const unsigned char *categoryEnabledFlag,
86 const char *name,
87 unsigned long long id,
88 double /*timestamp*/,
89 int numArgs,
90 const char **argNames,
91 const unsigned char *argTypes,
92 const unsigned long long *argValues,
93 unsigned char flags)
94 {
95 if (!gEnableTrace)
96 return 0;
97
98 // Discover the category name based on categoryEnabledFlag. This flag comes from the first
99 // parameter of TraceCategory, and corresponds to one of the entries in gTraceCategories.
100 static_assert(offsetof(TraceCategory, enabled) == 0,
101 "|enabled| must be the first field of the TraceCategory class.");
102 const TraceCategory *category = reinterpret_cast<const TraceCategory *>(categoryEnabledFlag);
103
104 ANGLERenderTest *renderTest = static_cast<ANGLERenderTest *>(platform->context);
105
106 std::lock_guard<std::mutex> lock(renderTest->getTraceEventMutex());
107
108 uint32_t tid = renderTest->getCurrentThreadSerial();
109
110 std::vector<TraceEvent> &buffer = renderTest->getTraceEventBuffer();
111 buffer.emplace_back(phase, category->name, name,
112 platform->monotonicallyIncreasingTime(platform), tid);
113 return buffer.size();
114 }
115
GetPerfTraceCategoryEnabled(PlatformMethods * platform,const char * categoryName)116 const unsigned char *GetPerfTraceCategoryEnabled(PlatformMethods *platform,
117 const char *categoryName)
118 {
119 if (gEnableTrace)
120 {
121 for (const TraceCategory &category : gTraceCategories)
122 {
123 if (strcmp(category.name, categoryName) == 0)
124 {
125 return &category.enabled;
126 }
127 }
128 }
129
130 constexpr static unsigned char kZero = 0;
131 return &kZero;
132 }
133
UpdateTraceEventDuration(PlatformMethods * platform,const unsigned char * categoryEnabledFlag,const char * name,TraceEventHandle eventHandle)134 void UpdateTraceEventDuration(PlatformMethods *platform,
135 const unsigned char *categoryEnabledFlag,
136 const char *name,
137 TraceEventHandle eventHandle)
138 {
139 // Not implemented.
140 }
141
MonotonicallyIncreasingTime(PlatformMethods * platform)142 double MonotonicallyIncreasingTime(PlatformMethods *platform)
143 {
144 return GetHostTimeSeconds();
145 }
146
WriteJsonFile(const std::string & outputFile,js::Document * doc)147 bool WriteJsonFile(const std::string &outputFile, js::Document *doc)
148 {
149 FILE *fp = fopen(outputFile.c_str(), "w");
150 if (!fp)
151 {
152 return false;
153 }
154
155 constexpr size_t kBufferSize = 0xFFFF;
156 std::vector<char> writeBuffer(kBufferSize);
157 js::FileWriteStream os(fp, writeBuffer.data(), kBufferSize);
158 js::PrettyWriter<js::FileWriteStream> writer(os);
159 if (!doc->Accept(writer))
160 {
161 fclose(fp);
162 return false;
163 }
164 fclose(fp);
165 return true;
166 }
167
DumpTraceEventsToJSONFile(const std::vector<TraceEvent> & traceEvents,const char * outputFileName)168 void DumpTraceEventsToJSONFile(const std::vector<TraceEvent> &traceEvents,
169 const char *outputFileName)
170 {
171 js::Document doc(js::kObjectType);
172 js::Document::AllocatorType &allocator = doc.GetAllocator();
173
174 js::Value events(js::kArrayType);
175
176 for (const TraceEvent &traceEvent : traceEvents)
177 {
178 js::Value value(js::kObjectType);
179
180 const uint64_t microseconds = static_cast<uint64_t>(traceEvent.timestamp * 1000.0 * 1000.0);
181
182 js::Document::StringRefType eventName(traceEvent.name);
183 js::Document::StringRefType categoryName(traceEvent.categoryName);
184 js::Document::StringRefType pidName(
185 strcmp(traceEvent.categoryName, "gpu.angle.gpu") == 0 ? "GPU" : "ANGLE");
186
187 value.AddMember("name", eventName, allocator);
188 value.AddMember("cat", categoryName, allocator);
189 value.AddMember("ph", std::string(1, traceEvent.phase), allocator);
190 value.AddMember("ts", microseconds, allocator);
191 value.AddMember("pid", pidName, allocator);
192 value.AddMember("tid", traceEvent.tid, allocator);
193
194 events.PushBack(value, allocator);
195 }
196
197 doc.AddMember("traceEvents", events, allocator);
198
199 if (WriteJsonFile(outputFileName, &doc))
200 {
201 printf("Wrote trace file to %s\n", outputFileName);
202 }
203 else
204 {
205 printf("Error writing trace file to %s\n", outputFileName);
206 }
207 }
208
PerfTestDebugCallback(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar * message,const void * userParam)209 [[maybe_unused]] void KHRONOS_APIENTRY PerfTestDebugCallback(GLenum source,
210 GLenum type,
211 GLuint id,
212 GLenum severity,
213 GLsizei length,
214 const GLchar *message,
215 const void *userParam)
216 {
217 // Early exit on non-errors.
218 if (type != GL_DEBUG_TYPE_ERROR || !userParam)
219 {
220 return;
221 }
222
223 ANGLERenderTest *renderTest =
224 const_cast<ANGLERenderTest *>(reinterpret_cast<const ANGLERenderTest *>(userParam));
225 renderTest->onErrorMessage(message);
226 }
227
ComputeMean(const std::vector<double> & values)228 double ComputeMean(const std::vector<double> &values)
229 {
230 double sum = std::accumulate(values.begin(), values.end(), 0.0);
231
232 double mean = sum / static_cast<double>(values.size());
233 return mean;
234 }
235
FinishAndCheckForContextLoss()236 void FinishAndCheckForContextLoss()
237 {
238 glFinish();
239 if (glGetError() == GL_CONTEXT_LOST)
240 {
241 FAIL() << "Context lost";
242 }
243 }
244
DumpFpsValues(const char * test,double mean_time)245 void DumpFpsValues(const char *test, double mean_time)
246 {
247 #if defined(ANGLE_PLATFORM_ANDROID)
248 std::ofstream fp(AndroidWindow::GetExternalStorageDirectory() + "/traces_fps.txt",
249 std::ios::app);
250 #else
251 std::ofstream fp("traces_fps.txt", std::ios::app);
252 #endif
253 double fps_value = 1000 / mean_time;
254 fp << test << " " << fps_value << std::endl;
255 fp.close();
256 }
257
258 #if defined(ANGLE_PLATFORM_ANDROID)
259 constexpr bool kHasATrace = true;
260
261 void *gLibAndroid = nullptr;
262 bool (*gATraceIsEnabled)(void);
263 bool (*gATraceSetCounter)(const char *counterName, int64_t counterValue);
264
SetupATrace()265 void SetupATrace()
266 {
267 if (gLibAndroid == nullptr)
268 {
269 gLibAndroid = dlopen("libandroid.so", RTLD_NOW | RTLD_LOCAL);
270 gATraceIsEnabled = (decltype(gATraceIsEnabled))dlsym(gLibAndroid, "ATrace_isEnabled");
271 gATraceSetCounter = (decltype(gATraceSetCounter))dlsym(gLibAndroid, "ATrace_setCounter");
272 }
273 }
274
ATraceEnabled()275 bool ATraceEnabled()
276 {
277 return gATraceIsEnabled();
278 }
279 #else
280 constexpr bool kHasATrace = false;
SetupATrace()281 void SetupATrace() {}
ATraceEnabled()282 bool ATraceEnabled()
283 {
284 return false;
285 }
286 #endif
287 } // anonymous namespace
288
TraceEvent(char phaseIn,const char * categoryNameIn,const char * nameIn,double timestampIn,uint32_t tidIn)289 TraceEvent::TraceEvent(char phaseIn,
290 const char *categoryNameIn,
291 const char *nameIn,
292 double timestampIn,
293 uint32_t tidIn)
294 : phase(phaseIn), categoryName(categoryNameIn), name{}, timestamp(timestampIn), tid(tidIn)
295 {
296 ASSERT(strlen(nameIn) < kMaxNameLen);
297 strcpy(name, nameIn);
298 }
299
ANGLEPerfTest(const std::string & name,const std::string & backend,const std::string & story,unsigned int iterationsPerStep,const char * units)300 ANGLEPerfTest::ANGLEPerfTest(const std::string &name,
301 const std::string &backend,
302 const std::string &story,
303 unsigned int iterationsPerStep,
304 const char *units)
305 : mName(name),
306 mBackend(backend),
307 mStory(story),
308 mGPUTimeNs(0),
309 mSkipTest(false),
310 mStepsToRun(std::max(gStepsPerTrial, gMaxStepsPerformed)),
311 mTrialNumStepsPerformed(0),
312 mTotalNumStepsPerformed(0),
313 mIterationsPerStep(iterationsPerStep),
314 mRunning(true),
315 mPerfMonitor(0)
316 {
317 if (mStory == "")
318 {
319 mStory = "baseline_story";
320 }
321 if (mStory[0] == '_')
322 {
323 mStory = mStory.substr(1);
324 }
325 mReporter = std::make_unique<perf_test::PerfResultReporter>(mName + mBackend, mStory);
326 mReporter->RegisterImportantMetric(".wall_time", units);
327 mReporter->RegisterImportantMetric(".cpu_time", units);
328 mReporter->RegisterImportantMetric(".gpu_time", units);
329 mReporter->RegisterFyiMetric(".trial_steps", "count");
330 mReporter->RegisterFyiMetric(".total_steps", "count");
331
332 if (kHasATrace)
333 {
334 SetupATrace();
335 }
336 }
337
~ANGLEPerfTest()338 ANGLEPerfTest::~ANGLEPerfTest() {}
339
run()340 void ANGLEPerfTest::run()
341 {
342 printf("running test name: \"%s\", backend: \"%s\", story: \"%s\"\n", mName.c_str(),
343 mBackend.c_str(), mStory.c_str());
344 #if defined(ANGLE_PLATFORM_ANDROID)
345 __android_log_print(ANDROID_LOG_INFO, "ANGLE",
346 "running test name: \"%s\", backend: \"%s\", story: \"%s\"", mName.c_str(),
347 mBackend.c_str(), mStory.c_str());
348 #endif
349 if (mSkipTest)
350 {
351 GTEST_SKIP() << mSkipTestReason;
352 // GTEST_SKIP returns.
353 }
354
355 uint32_t numTrials = OneFrame() ? 1 : gTestTrials;
356 if (gVerboseLogging)
357 {
358 printf("Test Trials: %d\n", static_cast<int>(numTrials));
359 }
360
361 atraceCounter("TraceStage", 3);
362
363 for (uint32_t trial = 0; trial < numTrials; ++trial)
364 {
365 runTrial(gTrialTimeSeconds, mStepsToRun, RunTrialPolicy::RunContinuously);
366 processResults();
367 if (gVerboseLogging)
368 {
369 double trialTime = mTrialTimer.getElapsedWallClockTime();
370 printf("Trial %d time: %.2lf seconds.\n", trial + 1, trialTime);
371
372 double secondsPerStep = trialTime / static_cast<double>(mTrialNumStepsPerformed);
373 double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
374 mTestTrialResults.push_back(secondsPerIteration * 1000.0);
375 }
376 }
377
378 atraceCounter("TraceStage", 0);
379
380 if (gVerboseLogging && !mTestTrialResults.empty())
381 {
382 double numResults = static_cast<double>(mTestTrialResults.size());
383 double mean = ComputeMean(mTestTrialResults);
384
385 double variance = 0;
386 for (double trialResult : mTestTrialResults)
387 {
388 double difference = trialResult - mean;
389 variance += difference * difference;
390 }
391 variance /= numResults;
392
393 double standardDeviation = std::sqrt(variance);
394 double coefficientOfVariation = standardDeviation / mean;
395
396 if (mean < 0.001)
397 {
398 printf("Mean result time: %.4lf ns.\n", mean * 1000.0);
399 }
400 else
401 {
402 printf("Mean result time: %.4lf ms.\n", mean);
403 }
404
405 if (kStandaloneBenchmark)
406 {
407 DumpFpsValues(mStory.c_str(), mean);
408 }
409
410 printf("Coefficient of variation: %.2lf%%\n", coefficientOfVariation * 100.0);
411 }
412 }
413
runTrial(double maxRunTime,int maxStepsToRun,RunTrialPolicy runPolicy)414 void ANGLEPerfTest::runTrial(double maxRunTime, int maxStepsToRun, RunTrialPolicy runPolicy)
415 {
416 mTrialNumStepsPerformed = 0;
417 mRunning = true;
418 mGPUTimeNs = 0;
419 int stepAlignment = getStepAlignment();
420 mTrialTimer.start();
421 startTest();
422
423 int loopStepsPerformed = 0;
424 double lastLoopWallTime = 0;
425 while (mRunning)
426 {
427 // When ATrace enabled, track average frame time before the first frame of each trace loop.
428 if (ATraceEnabled() && stepAlignment > 1 && runPolicy == RunTrialPolicy::RunContinuously &&
429 mTrialNumStepsPerformed % stepAlignment == 0)
430 {
431 double wallTime = mTrialTimer.getElapsedWallClockTime();
432 if (loopStepsPerformed > 0) // 0 at the first frame of the first loop
433 {
434 int frameTimeAvgUs = int(1e6 * (wallTime - lastLoopWallTime) / loopStepsPerformed);
435 atraceCounter("TraceLoopFrameTimeAvgUs", frameTimeAvgUs);
436 loopStepsPerformed = 0;
437 }
438 lastLoopWallTime = wallTime;
439 }
440
441 // Only stop on aligned steps or in a few special case modes
442 if (mTrialNumStepsPerformed % stepAlignment == 0 || gStepsPerTrial == 1 || gRunToKeyFrame ||
443 gMaxStepsPerformed != kDefaultMaxStepsPerformed)
444 {
445 if (gMaxStepsPerformed > 0 && mTotalNumStepsPerformed >= gMaxStepsPerformed)
446 {
447 if (gVerboseLogging)
448 {
449 printf("Stopping test after %d total steps.\n", mTotalNumStepsPerformed);
450 }
451 mRunning = false;
452 break;
453 }
454 if (mTrialTimer.getElapsedWallClockTime() > maxRunTime)
455 {
456 if (gVerboseLogging)
457 {
458 printf("Stopping test after %.2lf seconds.\n",
459 mTrialTimer.getElapsedWallClockTime());
460 }
461 mRunning = false;
462 break;
463 }
464 if (mTrialNumStepsPerformed >= maxStepsToRun)
465 {
466 if (gVerboseLogging)
467 {
468 printf("Stopping test after %d trial steps.\n", mTrialNumStepsPerformed);
469 }
470 mRunning = false;
471 break;
472 }
473 }
474
475 if (gFpsLimit)
476 {
477 double wantTime = mTrialNumStepsPerformed / double(gFpsLimit);
478 double currentTime = mTrialTimer.getElapsedWallClockTime();
479 if (currentTime < wantTime)
480 {
481 std::this_thread::sleep_for(std::chrono::duration<double>(wantTime - currentTime));
482 }
483 }
484 step();
485
486 if (runPolicy == RunTrialPolicy::FinishEveryStep)
487 {
488 FinishAndCheckForContextLoss();
489 }
490
491 if (mRunning)
492 {
493 mTrialNumStepsPerformed++;
494 mTotalNumStepsPerformed++;
495 loopStepsPerformed++;
496 }
497
498 if ((mTotalNumStepsPerformed % kNumberOfStepsPerformedToComputeGPUTime) == 0)
499 {
500 computeGPUTime();
501 }
502 }
503
504 if (runPolicy == RunTrialPolicy::RunContinuously)
505 {
506 atraceCounter("TraceLoopFrameTimeAvgUs", 0);
507 }
508 finishTest();
509 mTrialTimer.stop();
510 computeGPUTime();
511 }
512
SetUp()513 void ANGLEPerfTest::SetUp()
514 {
515 if (gWarmup)
516 {
517 atraceCounter("TraceStage", 1);
518
519 // Trace tests run with glFinish for a loop (getStepAlignment == frameCount).
520 int warmupSteps = getStepAlignment();
521 if (gVerboseLogging)
522 {
523 printf("Warmup: %d steps\n", warmupSteps);
524 }
525
526 Timer warmupTimer;
527 warmupTimer.start();
528
529 runTrial(gTrialTimeSeconds, warmupSteps, RunTrialPolicy::FinishEveryStep);
530
531 if (warmupSteps > 1) // trace tests only: getStepAlignment() is 1 otherwise
532 {
533 atraceCounter("TraceStage", 2);
534
535 // Short traces (e.g. 10 frames) have some spikes after the first loop b/308975999
536 const double kMinWarmupTime = 1.5;
537 double remainingTime = kMinWarmupTime - warmupTimer.getElapsedWallClockTime();
538 if (remainingTime > 0)
539 {
540 printf("Warmup: Looping for remaining warmup time (%.2f seconds).\n",
541 remainingTime);
542 runTrial(remainingTime, std::numeric_limits<int>::max(),
543 RunTrialPolicy::RunContinuouslyWarmup);
544 }
545 }
546
547 if (gVerboseLogging)
548 {
549 printf("Warmup took %.2lf seconds.\n", warmupTimer.getElapsedWallClockTime());
550 }
551 }
552 }
553
TearDown()554 void ANGLEPerfTest::TearDown() {}
555
recordIntegerMetric(const char * metric,size_t value,const std::string & units)556 void ANGLEPerfTest::recordIntegerMetric(const char *metric, size_t value, const std::string &units)
557 {
558 // Prints "RESULT ..." to stdout
559 mReporter->AddResult(metric, value);
560
561 // Saves results to file if enabled
562 TestSuite::GetMetricWriter().writeInfo(mName, mBackend, mStory, metric, units);
563 TestSuite::GetMetricWriter().writeIntegerValue(value);
564 }
565
recordDoubleMetric(const char * metric,double value,const std::string & units)566 void ANGLEPerfTest::recordDoubleMetric(const char *metric, double value, const std::string &units)
567 {
568 // Prints "RESULT ..." to stdout
569 mReporter->AddResult(metric, value);
570
571 // Saves results to file if enabled
572 TestSuite::GetMetricWriter().writeInfo(mName, mBackend, mStory, metric, units);
573 TestSuite::GetMetricWriter().writeDoubleValue(value);
574 }
575
addHistogramSample(const char * metric,double value,const std::string & units)576 void ANGLEPerfTest::addHistogramSample(const char *metric, double value, const std::string &units)
577 {
578 std::string measurement = mName + mBackend + metric;
579 // Output histogram JSON set format if enabled.
580 TestSuite::GetInstance()->addHistogramSample(measurement, mStory, value, units);
581 }
582
processResults()583 void ANGLEPerfTest::processResults()
584 {
585 processClockResult(".cpu_time", mTrialTimer.getElapsedCpuTime());
586 processClockResult(".wall_time", mTrialTimer.getElapsedWallClockTime());
587
588 if (mGPUTimeNs > 0)
589 {
590 processClockResult(".gpu_time", mGPUTimeNs * 1e-9);
591 }
592
593 if (gVerboseLogging)
594 {
595 double fps = static_cast<double>(mTrialNumStepsPerformed * mIterationsPerStep) /
596 mTrialTimer.getElapsedWallClockTime();
597 printf("Ran %0.2lf iterations per second\n", fps);
598 }
599
600 mReporter->AddResult(".trial_steps", static_cast<size_t>(mTrialNumStepsPerformed));
601 mReporter->AddResult(".total_steps", static_cast<size_t>(mTotalNumStepsPerformed));
602
603 if (!mProcessMemoryUsageKBSamples.empty())
604 {
605 std::sort(mProcessMemoryUsageKBSamples.begin(), mProcessMemoryUsageKBSamples.end());
606
607 // Compute median.
608 size_t medianIndex = mProcessMemoryUsageKBSamples.size() / 2;
609 uint64_t medianMemoryKB = mProcessMemoryUsageKBSamples[medianIndex];
610 auto peakMemoryIterator = std::max_element(mProcessMemoryUsageKBSamples.begin(),
611 mProcessMemoryUsageKBSamples.end());
612 uint64_t peakMemoryKB = *peakMemoryIterator;
613
614 processMemoryResult(kMedianMemoryMetric, medianMemoryKB);
615 processMemoryResult(kPeakMemoryMetric, peakMemoryKB);
616 }
617
618 for (const auto &iter : mPerfCounterInfo)
619 {
620 const std::string &counterName = iter.second.name;
621 std::vector<GLuint64> samples = iter.second.samples;
622
623 // Median
624 {
625 size_t midpoint = samples.size() / 2;
626 std::nth_element(samples.begin(), samples.begin() + midpoint, samples.end());
627
628 std::string medianName = "." + counterName + "_median";
629 recordIntegerMetric(medianName.c_str(), static_cast<size_t>(samples[midpoint]),
630 "count");
631 addHistogramSample(medianName.c_str(), static_cast<double>(samples[midpoint]), "count");
632 }
633
634 // Maximum
635 {
636 const auto &maxIt = std::max_element(samples.begin(), samples.end());
637
638 std::string maxName = "." + counterName + "_max";
639 recordIntegerMetric(maxName.c_str(), static_cast<size_t>(*maxIt), "count");
640 addHistogramSample(maxName.c_str(), static_cast<double>(*maxIt), "count");
641 }
642
643 // Sum
644 {
645 GLuint64 sum =
646 std::accumulate(samples.begin(), samples.end(), static_cast<GLuint64>(0));
647
648 std::string sumName = "." + counterName + "_max";
649 recordIntegerMetric(sumName.c_str(), static_cast<size_t>(sum), "count");
650 addHistogramSample(sumName.c_str(), static_cast<double>(sum), "count");
651 }
652 }
653 }
654
processClockResult(const char * metric,double resultSeconds)655 void ANGLEPerfTest::processClockResult(const char *metric, double resultSeconds)
656 {
657 double secondsPerStep = resultSeconds / static_cast<double>(mTrialNumStepsPerformed);
658 double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);
659
660 perf_test::MetricInfo metricInfo;
661 std::string units;
662 bool foundMetric = mReporter->GetMetricInfo(metric, &metricInfo);
663 if (!foundMetric)
664 {
665 fprintf(stderr, "Error getting metric info for %s.\n", metric);
666 return;
667 }
668 units = metricInfo.units;
669
670 double result;
671
672 if (units == "ms")
673 {
674 result = secondsPerIteration * kMilliSecondsPerSecond;
675 }
676 else if (units == "us")
677 {
678 result = secondsPerIteration * kMicroSecondsPerSecond;
679 }
680 else
681 {
682 result = secondsPerIteration * kNanoSecondsPerSecond;
683 }
684 recordDoubleMetric(metric, result, units);
685 addHistogramSample(metric, secondsPerIteration * kMilliSecondsPerSecond,
686 "msBestFitFormat_smallerIsBetter");
687 }
688
processMemoryResult(const char * metric,uint64_t resultKB)689 void ANGLEPerfTest::processMemoryResult(const char *metric, uint64_t resultKB)
690 {
691 perf_test::MetricInfo metricInfo;
692 if (!mReporter->GetMetricInfo(metric, &metricInfo))
693 {
694 mReporter->RegisterImportantMetric(metric, "sizeInBytes");
695 }
696
697 recordIntegerMetric(metric, static_cast<size_t>(resultKB * 1000), "sizeInBytes");
698 addHistogramSample(metric, static_cast<double>(resultKB) * 1000.0,
699 "sizeInBytes_smallerIsBetter");
700 }
701
normalizedTime(size_t value) const702 double ANGLEPerfTest::normalizedTime(size_t value) const
703 {
704 return static_cast<double>(value) / static_cast<double>(mTrialNumStepsPerformed);
705 }
706
getStepAlignment() const707 int ANGLEPerfTest::getStepAlignment() const
708 {
709 // Default: No special alignment rules.
710 return 1;
711 }
712
atraceCounter(const char * counterName,int64_t counterValue)713 void ANGLEPerfTest::atraceCounter(const char *counterName, int64_t counterValue)
714 {
715 #if defined(ANGLE_PLATFORM_ANDROID)
716 if (ATraceEnabled())
717 {
718 gATraceSetCounter(counterName, counterValue);
719 }
720 #endif
721 }
722
RenderTestParams()723 RenderTestParams::RenderTestParams()
724 {
725 #if defined(ANGLE_DEBUG_LAYERS_ENABLED)
726 eglParameters.debugLayersEnabled = true;
727 #else
728 eglParameters.debugLayersEnabled = false;
729 #endif
730 }
731
backend() const732 std::string RenderTestParams::backend() const
733 {
734 std::stringstream strstr;
735
736 switch (driver)
737 {
738 case GLESDriverType::AngleEGL:
739 break;
740 case GLESDriverType::AngleVulkanSecondariesEGL:
741 strstr << "_vulkan_secondaries";
742 break;
743 case GLESDriverType::SystemWGL:
744 case GLESDriverType::SystemEGL:
745 strstr << "_native";
746 break;
747 case GLESDriverType::ZinkEGL:
748 strstr << "_zink";
749 break;
750 default:
751 assert(0);
752 return "_unk";
753 }
754
755 switch (getRenderer())
756 {
757 case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
758 break;
759 case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE:
760 strstr << "_d3d11";
761 break;
762 case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE:
763 strstr << "_d3d9";
764 break;
765 case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
766 strstr << "_gl";
767 break;
768 case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
769 strstr << "_gles";
770 break;
771 case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:
772 strstr << "_vulkan";
773 break;
774 case EGL_PLATFORM_ANGLE_TYPE_METAL_ANGLE:
775 strstr << "_metal";
776 break;
777 default:
778 assert(0);
779 return "_unk";
780 }
781
782 switch (eglParameters.deviceType)
783 {
784 case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE:
785 strstr << "_null";
786 break;
787 case EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE:
788 strstr << "_swiftshader";
789 break;
790 default:
791 break;
792 }
793
794 return strstr.str();
795 }
796
story() const797 std::string RenderTestParams::story() const
798 {
799 std::stringstream strstr;
800
801 switch (surfaceType)
802 {
803 case SurfaceType::Window:
804 break;
805 case SurfaceType::WindowWithVSync:
806 strstr << "_vsync";
807 break;
808 case SurfaceType::Offscreen:
809 strstr << "_offscreen";
810 break;
811 default:
812 UNREACHABLE();
813 return "";
814 }
815
816 if (multisample)
817 {
818 strstr << "_" << samples << "_samples";
819 }
820
821 return strstr.str();
822 }
823
backendAndStory() const824 std::string RenderTestParams::backendAndStory() const
825 {
826 return backend() + story();
827 }
828
ANGLERenderTest(const std::string & name,const RenderTestParams & testParams,const char * units)829 ANGLERenderTest::ANGLERenderTest(const std::string &name,
830 const RenderTestParams &testParams,
831 const char *units)
832 : ANGLEPerfTest(name,
833 testParams.backend(),
834 testParams.story(),
835 OneFrame() ? 1 : testParams.iterationsPerStep,
836 units),
837 mTestParams(testParams),
838 mIsTimestampQueryAvailable(false),
839 mGLWindow(nullptr),
840 mOSWindow(nullptr),
841 mSwapEnabled(true)
842 {
843 // Force fast tests to make sure our slowest bots don't time out.
844 if (OneFrame())
845 {
846 const_cast<RenderTestParams &>(testParams).iterationsPerStep = 1;
847 }
848
849 // Try to ensure we don't trigger allocation during execution.
850 mTraceEventBuffer.reserve(kInitialTraceEventBufferSize);
851
852 switch (testParams.driver)
853 {
854 case GLESDriverType::AngleEGL:
855 mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
856 mEntryPointsLib.reset(OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME, SearchType::ModuleDir));
857 break;
858 case GLESDriverType::AngleVulkanSecondariesEGL:
859 mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
860 mEntryPointsLib.reset(OpenSharedLibrary(ANGLE_VULKAN_SECONDARIES_EGL_LIBRARY_NAME,
861 SearchType::ModuleDir));
862 break;
863 case GLESDriverType::SystemEGL:
864 #if defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
865 mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
866 mEntryPointsLib.reset(OpenSharedLibraryWithExtension(
867 GetNativeEGLLibraryNameWithExtension(), SearchType::SystemDir));
868 #else
869 skipTest("Not implemented.");
870 #endif // defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)
871 break;
872 case GLESDriverType::SystemWGL:
873 #if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
874 mGLWindow = WGLWindow::New(testParams.majorVersion, testParams.minorVersion);
875 mEntryPointsLib.reset(OpenSharedLibrary("opengl32", SearchType::SystemDir));
876 #else
877 skipTest("WGL driver not available.");
878 #endif // defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)
879 break;
880 case GLESDriverType::ZinkEGL:
881 mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);
882 mEntryPointsLib.reset(
883 OpenSharedLibrary(ANGLE_MESA_EGL_LIBRARY_NAME, SearchType::ModuleDir));
884 break;
885 default:
886 skipTest("Error in switch.");
887 break;
888 }
889 }
890
~ANGLERenderTest()891 ANGLERenderTest::~ANGLERenderTest()
892 {
893 OSWindow::Delete(&mOSWindow);
894 GLWindowBase::Delete(&mGLWindow);
895 }
896
addExtensionPrerequisite(std::string extensionName)897 void ANGLERenderTest::addExtensionPrerequisite(std::string extensionName)
898 {
899 mExtensionPrerequisites.push_back(extensionName);
900 }
901
addIntegerPrerequisite(GLenum target,int min)902 void ANGLERenderTest::addIntegerPrerequisite(GLenum target, int min)
903 {
904 mIntegerPrerequisites.push_back({target, min});
905 }
906
SetUp()907 void ANGLERenderTest::SetUp()
908 {
909 if (mSkipTest)
910 {
911 return;
912 }
913
914 // Set a consistent CPU core affinity and high priority.
915 StabilizeCPUForBenchmarking();
916
917 mOSWindow = OSWindow::New();
918
919 if (!mGLWindow)
920 {
921 skipTest("!mGLWindow");
922 return;
923 }
924
925 mPlatformMethods.logError = CustomLogError;
926 mPlatformMethods.logWarning = EmptyPlatformMethod;
927 mPlatformMethods.logInfo = EmptyPlatformMethod;
928 mPlatformMethods.addTraceEvent = AddPerfTraceEvent;
929 mPlatformMethods.getTraceCategoryEnabledFlag = GetPerfTraceCategoryEnabled;
930 mPlatformMethods.updateTraceEventDuration = UpdateTraceEventDuration;
931 mPlatformMethods.monotonicallyIncreasingTime = MonotonicallyIncreasingTime;
932 mPlatformMethods.context = this;
933
934 if (!mOSWindow->initialize(mName, mTestParams.windowWidth, mTestParams.windowHeight))
935 {
936 failTest("Failed initializing OSWindow");
937 return;
938 }
939
940 // Override platform method parameter.
941 EGLPlatformParameters withMethods = mTestParams.eglParameters;
942 withMethods.platformMethods = &mPlatformMethods;
943
944 // Request a common framebuffer config
945 mConfigParams.redBits = 8;
946 mConfigParams.greenBits = 8;
947 mConfigParams.blueBits = 8;
948 mConfigParams.alphaBits = 8;
949 mConfigParams.depthBits = 24;
950 mConfigParams.stencilBits = 8;
951 mConfigParams.colorSpace = mTestParams.colorSpace;
952 mConfigParams.multisample = mTestParams.multisample;
953 mConfigParams.samples = mTestParams.samples;
954 if (mTestParams.surfaceType != SurfaceType::WindowWithVSync)
955 {
956 mConfigParams.swapInterval = 0;
957 }
958
959 if (gPrintExtensionsToFile != nullptr || gRequestedExtensions != nullptr)
960 {
961 mConfigParams.extensionsEnabled = false;
962 }
963
964 GLWindowResult res = mGLWindow->initializeGLWithResult(
965 mOSWindow, mEntryPointsLib.get(), mTestParams.driver, withMethods, mConfigParams);
966 switch (res)
967 {
968 case GLWindowResult::NoColorspaceSupport:
969 skipTest("Missing support for color spaces.");
970 return;
971 case GLWindowResult::Error:
972 failTest("Failed initializing GL Window");
973 return;
974 default:
975 break;
976 }
977
978 if (gPrintExtensionsToFile)
979 {
980 std::ofstream fout(gPrintExtensionsToFile);
981 if (fout.is_open())
982 {
983 int numExtensions = 0;
984 glGetIntegerv(GL_NUM_REQUESTABLE_EXTENSIONS_ANGLE, &numExtensions);
985 for (int ext = 0; ext < numExtensions; ext++)
986 {
987 fout << glGetStringi(GL_REQUESTABLE_EXTENSIONS_ANGLE, ext) << std::endl;
988 }
989 fout.close();
990 std::stringstream statusString;
991 statusString << "Wrote out to file: " << gPrintExtensionsToFile;
992 skipTest(statusString.str());
993 }
994 else
995 {
996 std::stringstream failStr;
997 failStr << "Failed to open file: " << gPrintExtensionsToFile;
998 failTest(failStr.str());
999 }
1000 return;
1001 }
1002
1003 if (gRequestedExtensions != nullptr)
1004 {
1005 std::istringstream ss{gRequestedExtensions};
1006 std::string ext;
1007 while (std::getline(ss, ext, ' '))
1008 {
1009 glRequestExtensionANGLE(ext.c_str());
1010 }
1011 }
1012
1013 // Disable vsync (if not done by the window init).
1014 if (mTestParams.surfaceType != SurfaceType::WindowWithVSync)
1015 {
1016 if (!mGLWindow->setSwapInterval(0))
1017 {
1018 failTest("Failed setting swap interval");
1019 return;
1020 }
1021 }
1022
1023 if (mTestParams.trackGpuTime)
1024 {
1025 mIsTimestampQueryAvailable = EnsureGLExtensionEnabled("GL_EXT_disjoint_timer_query");
1026 }
1027
1028 skipTestIfMissingExtensionPrerequisites();
1029 skipTestIfFailsIntegerPrerequisite();
1030
1031 if (mSkipTest)
1032 {
1033 GTEST_SKIP() << mSkipTestReason;
1034 // GTEST_SKIP returns.
1035 }
1036
1037 #if defined(ANGLE_ENABLE_ASSERTS)
1038 if (IsGLExtensionEnabled("GL_KHR_debug") && mEnableDebugCallback)
1039 {
1040 EnableDebugCallback(&PerfTestDebugCallback, this);
1041 }
1042 #endif
1043
1044 initializeBenchmark();
1045
1046 if (mSkipTest)
1047 {
1048 GTEST_SKIP() << mSkipTestReason;
1049 // GTEST_SKIP returns.
1050 }
1051
1052 if (mTestParams.iterationsPerStep == 0)
1053 {
1054 failTest("Please initialize 'iterationsPerStep'.");
1055 return;
1056 }
1057
1058 if (gVerboseLogging)
1059 {
1060 printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER));
1061 printf("GL_VERSION: %s\n", glGetString(GL_VERSION));
1062 }
1063
1064 mTestTrialResults.reserve(gTestTrials);
1065
1066 // Runs warmup if enabled
1067 ANGLEPerfTest::SetUp();
1068
1069 initPerfCounters();
1070 }
1071
TearDown()1072 void ANGLERenderTest::TearDown()
1073 {
1074 ASSERT(mTimestampQueries.empty());
1075
1076 if (!mPerfCounterInfo.empty())
1077 {
1078 glDeletePerfMonitorsAMD(1, &mPerfMonitor);
1079 mPerfMonitor = 0;
1080 }
1081
1082 if (!mSkipTest)
1083 {
1084 destroyBenchmark();
1085 }
1086
1087 if (mGLWindow)
1088 {
1089 mGLWindow->destroyGL();
1090 mGLWindow = nullptr;
1091 }
1092
1093 if (mOSWindow)
1094 {
1095 mOSWindow->destroy();
1096 mOSWindow = nullptr;
1097 }
1098
1099 // Dump trace events to json file.
1100 if (gEnableTrace)
1101 {
1102 DumpTraceEventsToJSONFile(mTraceEventBuffer, gTraceFile);
1103 }
1104
1105 ANGLEPerfTest::TearDown();
1106 }
1107
initPerfCounters()1108 void ANGLERenderTest::initPerfCounters()
1109 {
1110 if (!gPerfCounters)
1111 {
1112 return;
1113 }
1114
1115 if (!IsGLExtensionEnabled(kPerfMonitorExtensionName))
1116 {
1117 fprintf(stderr, "Cannot report perf metrics because %s is not available.\n",
1118 kPerfMonitorExtensionName);
1119 return;
1120 }
1121
1122 CounterNameToIndexMap indexMap = BuildCounterNameToIndexMap();
1123
1124 std::vector<std::string> counters =
1125 angle::SplitString(gPerfCounters, ":", angle::WhitespaceHandling::TRIM_WHITESPACE,
1126 angle::SplitResult::SPLIT_WANT_NONEMPTY);
1127 for (const std::string &counter : counters)
1128 {
1129 bool found = false;
1130
1131 for (const auto &indexMapIter : indexMap)
1132 {
1133 const std::string &indexMapName = indexMapIter.first;
1134 if (NamesMatchWithWildcard(counter.c_str(), indexMapName.c_str()))
1135 {
1136 {
1137 std::stringstream medianStr;
1138 medianStr << '.' << indexMapName << "_median";
1139 std::string medianName = medianStr.str();
1140 mReporter->RegisterImportantMetric(medianName, "count");
1141 }
1142
1143 {
1144 std::stringstream maxStr;
1145 maxStr << '.' << indexMapName << "_max";
1146 std::string maxName = maxStr.str();
1147 mReporter->RegisterImportantMetric(maxName, "count");
1148 }
1149
1150 {
1151 std::stringstream sumStr;
1152 sumStr << '.' << indexMapName << "_sum";
1153 std::string sumName = sumStr.str();
1154 mReporter->RegisterImportantMetric(sumName, "count");
1155 }
1156
1157 GLuint index = indexMapIter.second;
1158 mPerfCounterInfo[index] = {indexMapName, {}};
1159
1160 found = true;
1161 }
1162 }
1163
1164 if (!found)
1165 {
1166 fprintf(stderr, "'%s' does not match any available perf counters.\n", counter.c_str());
1167 }
1168 }
1169
1170 if (!mPerfCounterInfo.empty())
1171 {
1172 glGenPerfMonitorsAMD(1, &mPerfMonitor);
1173 // Note: technically, glSelectPerfMonitorCountersAMD should be used to select the counters,
1174 // but currently ANGLE always captures all counters.
1175 }
1176 }
1177
updatePerfCounters()1178 void ANGLERenderTest::updatePerfCounters()
1179 {
1180 if (mPerfCounterInfo.empty())
1181 {
1182 return;
1183 }
1184
1185 std::vector<PerfMonitorTriplet> perfData = GetPerfMonitorTriplets();
1186 ASSERT(!perfData.empty());
1187
1188 for (auto &iter : mPerfCounterInfo)
1189 {
1190 uint32_t counter = iter.first;
1191 std::vector<GLuint64> &samples = iter.second.samples;
1192 samples.push_back(perfData[counter].value);
1193 }
1194 }
1195
beginInternalTraceEvent(const char * name)1196 void ANGLERenderTest::beginInternalTraceEvent(const char *name)
1197 {
1198 if (gEnableTrace)
1199 {
1200 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[0].name, name,
1201 MonotonicallyIncreasingTime(&mPlatformMethods),
1202 getCurrentThreadSerial());
1203 }
1204 }
1205
endInternalTraceEvent(const char * name)1206 void ANGLERenderTest::endInternalTraceEvent(const char *name)
1207 {
1208 if (gEnableTrace)
1209 {
1210 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[0].name, name,
1211 MonotonicallyIncreasingTime(&mPlatformMethods),
1212 getCurrentThreadSerial());
1213 }
1214 }
1215
beginGLTraceEvent(const char * name,double hostTimeSec)1216 void ANGLERenderTest::beginGLTraceEvent(const char *name, double hostTimeSec)
1217 {
1218 if (gEnableTrace)
1219 {
1220 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[1].name, name,
1221 hostTimeSec, getCurrentThreadSerial());
1222 }
1223 }
1224
endGLTraceEvent(const char * name,double hostTimeSec)1225 void ANGLERenderTest::endGLTraceEvent(const char *name, double hostTimeSec)
1226 {
1227 if (gEnableTrace)
1228 {
1229 mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[1].name, name,
1230 hostTimeSec, getCurrentThreadSerial());
1231 }
1232 }
1233
step()1234 void ANGLERenderTest::step()
1235 {
1236 beginInternalTraceEvent("step");
1237
1238 // Clear events that the application did not process from this frame
1239 Event event;
1240 bool closed = false;
1241 while (popEvent(&event))
1242 {
1243 // If the application did not catch a close event, close now
1244 if (event.Type == Event::EVENT_CLOSED)
1245 {
1246 closed = true;
1247 }
1248 }
1249
1250 if (closed)
1251 {
1252 abortTest();
1253 }
1254 else
1255 {
1256 drawBenchmark();
1257
1258 // Swap is needed so that the GPU driver will occasionally flush its
1259 // internal command queue to the GPU. This is enabled for null back-end
1260 // devices because some back-ends (e.g. Vulkan) also accumulate internal
1261 // command queues.
1262 if (mSwapEnabled)
1263 {
1264 updatePerfCounters();
1265 mGLWindow->swap();
1266 }
1267 mOSWindow->messageLoop();
1268
1269 #if defined(ANGLE_ENABLE_ASSERTS)
1270 if (!gRetraceMode)
1271 {
1272 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
1273 }
1274 #endif // defined(ANGLE_ENABLE_ASSERTS)
1275
1276 // Sample system memory
1277 uint64_t processMemoryUsageKB = GetProcessMemoryUsageKB();
1278 if (processMemoryUsageKB)
1279 {
1280 mProcessMemoryUsageKBSamples.push_back(processMemoryUsageKB);
1281 }
1282 }
1283
1284 endInternalTraceEvent("step");
1285 }
1286
startGpuTimer()1287 void ANGLERenderTest::startGpuTimer()
1288 {
1289 if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
1290 {
1291 glGenQueriesEXT(1, &mCurrentTimestampBeginQuery);
1292 glQueryCounterEXT(mCurrentTimestampBeginQuery, GL_TIMESTAMP_EXT);
1293 }
1294 }
1295
stopGpuTimer()1296 void ANGLERenderTest::stopGpuTimer()
1297 {
1298 if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
1299 {
1300 GLuint endQuery = 0;
1301 glGenQueriesEXT(1, &endQuery);
1302 glQueryCounterEXT(endQuery, GL_TIMESTAMP_EXT);
1303 mTimestampQueries.push({mCurrentTimestampBeginQuery, endQuery});
1304 }
1305 }
1306
computeGPUTime()1307 void ANGLERenderTest::computeGPUTime()
1308 {
1309 if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)
1310 {
1311 while (!mTimestampQueries.empty())
1312 {
1313 const TimestampSample &sample = mTimestampQueries.front();
1314 GLuint available = GL_FALSE;
1315 glGetQueryObjectuivEXT(sample.endQuery, GL_QUERY_RESULT_AVAILABLE_EXT, &available);
1316 if (available != GL_TRUE)
1317 {
1318 // query is not completed yet, bail out
1319 break;
1320 }
1321
1322 // frame's begin query must also completed.
1323 glGetQueryObjectuivEXT(sample.beginQuery, GL_QUERY_RESULT_AVAILABLE_EXT, &available);
1324 ASSERT(available == GL_TRUE);
1325
1326 // Retrieve query result
1327 uint64_t beginGLTimeNs = 0;
1328 uint64_t endGLTimeNs = 0;
1329 glGetQueryObjectui64vEXT(sample.beginQuery, GL_QUERY_RESULT_EXT, &beginGLTimeNs);
1330 glGetQueryObjectui64vEXT(sample.endQuery, GL_QUERY_RESULT_EXT, &endGLTimeNs);
1331 glDeleteQueriesEXT(1, &sample.beginQuery);
1332 glDeleteQueriesEXT(1, &sample.endQuery);
1333 mTimestampQueries.pop();
1334
1335 // compute GPU time
1336 mGPUTimeNs += endGLTimeNs - beginGLTimeNs;
1337 }
1338 }
1339 }
1340
startTest()1341 void ANGLERenderTest::startTest()
1342 {
1343 if (!mPerfCounterInfo.empty())
1344 {
1345 glBeginPerfMonitorAMD(mPerfMonitor);
1346 }
1347 }
1348
finishTest()1349 void ANGLERenderTest::finishTest()
1350 {
1351 if (mTestParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE &&
1352 !gNoFinish && !gRetraceMode)
1353 {
1354 FinishAndCheckForContextLoss();
1355 }
1356
1357 if (!mPerfCounterInfo.empty())
1358 {
1359 glEndPerfMonitorAMD(mPerfMonitor);
1360 }
1361 }
1362
popEvent(Event * event)1363 bool ANGLERenderTest::popEvent(Event *event)
1364 {
1365 return mOSWindow->popEvent(event);
1366 }
1367
getWindow()1368 OSWindow *ANGLERenderTest::getWindow()
1369 {
1370 return mOSWindow;
1371 }
1372
getGLWindow()1373 GLWindowBase *ANGLERenderTest::getGLWindow()
1374 {
1375 return mGLWindow;
1376 }
1377
skipTestIfMissingExtensionPrerequisites()1378 void ANGLERenderTest::skipTestIfMissingExtensionPrerequisites()
1379 {
1380 for (std::string extension : mExtensionPrerequisites)
1381 {
1382 if (!CheckExtensionExists(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),
1383 extension))
1384 {
1385 skipTest(std::string("Test skipped due to missing extension: ") + extension);
1386 return;
1387 }
1388 }
1389 }
1390
skipTestIfFailsIntegerPrerequisite()1391 void ANGLERenderTest::skipTestIfFailsIntegerPrerequisite()
1392 {
1393 for (const auto [target, minRequired] : mIntegerPrerequisites)
1394 {
1395 GLint driverValue;
1396 glGetIntegerv(target, &driverValue);
1397 if (static_cast<int>(driverValue) < minRequired)
1398 {
1399 std::stringstream ss;
1400 ss << "Test skipped due to value (" << std::to_string(static_cast<int>(driverValue))
1401 << ") being less than the prerequisite minimum (" << std::to_string(minRequired)
1402 << ") for GL constant " << gl::GLenumToString(gl::GLESEnum::AllEnums, target);
1403 skipTest(ss.str());
1404 }
1405 }
1406 }
1407
setWebGLCompatibilityEnabled(bool webglCompatibility)1408 void ANGLERenderTest::setWebGLCompatibilityEnabled(bool webglCompatibility)
1409 {
1410 mConfigParams.webGLCompatibility = webglCompatibility;
1411 }
1412
setRobustResourceInit(bool enabled)1413 void ANGLERenderTest::setRobustResourceInit(bool enabled)
1414 {
1415 mConfigParams.robustResourceInit = enabled;
1416 }
1417
getTraceEventBuffer()1418 std::vector<TraceEvent> &ANGLERenderTest::getTraceEventBuffer()
1419 {
1420 return mTraceEventBuffer;
1421 }
1422
onErrorMessage(const char * errorMessage)1423 void ANGLERenderTest::onErrorMessage(const char *errorMessage)
1424 {
1425 abortTest();
1426 std::ostringstream err;
1427 err << "Failing test because of unexpected error:\n" << errorMessage << "\n";
1428 failTest(err.str());
1429 }
1430
getCurrentThreadSerial()1431 uint32_t ANGLERenderTest::getCurrentThreadSerial()
1432 {
1433 uint64_t id = angle::GetCurrentThreadUniqueId();
1434
1435 for (uint32_t serial = 0; serial < static_cast<uint32_t>(mThreadIDs.size()); ++serial)
1436 {
1437 if (mThreadIDs[serial] == id)
1438 {
1439 return serial + 1;
1440 }
1441 }
1442
1443 mThreadIDs.push_back(id);
1444 return static_cast<uint32_t>(mThreadIDs.size());
1445 }
1446
1447 namespace angle
1448 {
GetHostTimeSeconds()1449 double GetHostTimeSeconds()
1450 {
1451 // Move the time origin to the first call to this function, to avoid generating unnecessarily
1452 // large timestamps.
1453 static double origin = GetCurrentSystemTime();
1454 return GetCurrentSystemTime() - origin;
1455 }
1456 } // namespace angle
1457