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