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