Path: blob/main_old/src/tests/perf_tests/ANGLEPerfTest.cpp
1693 views
//1// Copyright 2014 The ANGLE Project Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file.4//5// ANGLEPerfTests:6// Base class for google test performance tests7//89#include "ANGLEPerfTest.h"1011#include "ANGLEPerfTestArgs.h"12#include "common/debug.h"13#include "common/mathutil.h"14#include "common/platform.h"15#include "common/system_utils.h"16#include "common/utilities.h"17#include "test_utils/runner/TestSuite.h"18#include "third_party/perf/perf_test.h"19#include "third_party/trace_event/trace_event.h"20#include "util/shader_utils.h"21#include "util/test_utils.h"2223#include <cassert>24#include <cmath>25#include <fstream>26#include <iostream>27#include <sstream>2829#include <rapidjson/document.h>30#include <rapidjson/filewritestream.h>31#include <rapidjson/istreamwrapper.h>32#include <rapidjson/prettywriter.h>3334#if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)35# include "util/windows/WGLWindow.h"36#endif // defined(ANGLE_USE_UTIL_LOADER) &&defined(ANGLE_PLATFORM_WINDOWS)3738using namespace angle;39namespace js = rapidjson;4041namespace42{43constexpr size_t kInitialTraceEventBufferSize = 50000;44constexpr double kMilliSecondsPerSecond = 1e3;45constexpr double kMicroSecondsPerSecond = 1e6;46constexpr double kNanoSecondsPerSecond = 1e9;4748struct TraceCategory49{50unsigned char enabled;51const char *name;52};5354constexpr TraceCategory gTraceCategories[2] = {55{1, "gpu.angle"},56{1, "gpu.angle.gpu"},57};5859void EmptyPlatformMethod(PlatformMethods *, const char *) {}6061void CustomLogError(PlatformMethods *platform, const char *errorMessage)62{63auto *angleRenderTest = static_cast<ANGLERenderTest *>(platform->context);64angleRenderTest->onErrorMessage(errorMessage);65}6667void OverrideWorkaroundsD3D(PlatformMethods *platform, FeaturesD3D *featuresD3D)68{69auto *angleRenderTest = static_cast<ANGLERenderTest *>(platform->context);70angleRenderTest->overrideWorkaroundsD3D(featuresD3D);71}7273TraceEventHandle AddPerfTraceEvent(PlatformMethods *platform,74char phase,75const unsigned char *categoryEnabledFlag,76const char *name,77unsigned long long id,78double timestamp,79int numArgs,80const char **argNames,81const unsigned char *argTypes,82const unsigned long long *argValues,83unsigned char flags)84{85if (!gEnableTrace)86return 0;8788// Discover the category name based on categoryEnabledFlag. This flag comes from the first89// parameter of TraceCategory, and corresponds to one of the entries in gTraceCategories.90static_assert(offsetof(TraceCategory, enabled) == 0,91"|enabled| must be the first field of the TraceCategory class.");92const TraceCategory *category = reinterpret_cast<const TraceCategory *>(categoryEnabledFlag);9394ANGLERenderTest *renderTest = static_cast<ANGLERenderTest *>(platform->context);9596std::lock_guard<std::mutex> lock(renderTest->getTraceEventMutex());9798uint32_t tid = renderTest->getCurrentThreadSerial();99100std::vector<TraceEvent> &buffer = renderTest->getTraceEventBuffer();101buffer.emplace_back(phase, category->name, name, timestamp, tid);102return buffer.size();103}104105const unsigned char *GetPerfTraceCategoryEnabled(PlatformMethods *platform,106const char *categoryName)107{108if (gEnableTrace)109{110for (const TraceCategory &category : gTraceCategories)111{112if (strcmp(category.name, categoryName) == 0)113{114return &category.enabled;115}116}117}118119constexpr static unsigned char kZero = 0;120return &kZero;121}122123void UpdateTraceEventDuration(PlatformMethods *platform,124const unsigned char *categoryEnabledFlag,125const char *name,126TraceEventHandle eventHandle)127{128// Not implemented.129}130131double MonotonicallyIncreasingTime(PlatformMethods *platform)132{133return GetHostTimeSeconds();134}135136bool WriteJsonFile(const std::string &outputFile, js::Document *doc)137{138FILE *fp = fopen(outputFile.c_str(), "w");139if (!fp)140{141return false;142}143144constexpr size_t kBufferSize = 0xFFFF;145std::vector<char> writeBuffer(kBufferSize);146js::FileWriteStream os(fp, writeBuffer.data(), kBufferSize);147js::PrettyWriter<js::FileWriteStream> writer(os);148if (!doc->Accept(writer))149{150fclose(fp);151return false;152}153fclose(fp);154return true;155}156157void DumpTraceEventsToJSONFile(const std::vector<TraceEvent> &traceEvents,158const char *outputFileName)159{160js::Document doc(js::kObjectType);161js::Document::AllocatorType &allocator = doc.GetAllocator();162163js::Value events(js::kArrayType);164165for (const TraceEvent &traceEvent : traceEvents)166{167js::Value value(js::kObjectType);168169const uint64_t microseconds = static_cast<uint64_t>(traceEvent.timestamp * 1000.0 * 1000.0);170171js::Document::StringRefType eventName(traceEvent.name);172js::Document::StringRefType categoryName(traceEvent.categoryName);173js::Document::StringRefType pidName(174strcmp(traceEvent.categoryName, "gpu.angle.gpu") == 0 ? "GPU" : "ANGLE");175176value.AddMember("name", eventName, allocator);177value.AddMember("cat", categoryName, allocator);178value.AddMember("ph", std::string(1, traceEvent.phase), allocator);179value.AddMember("ts", microseconds, allocator);180value.AddMember("pid", pidName, allocator);181value.AddMember("tid", traceEvent.tid, allocator);182183events.PushBack(value, allocator);184}185186doc.AddMember("traceEvents", events, allocator);187188if (WriteJsonFile(outputFileName, &doc))189{190printf("Wrote trace file to %s\n", outputFileName);191}192else193{194printf("Error writing trace file to %s\n", outputFileName);195}196}197198ANGLE_MAYBE_UNUSED void KHRONOS_APIENTRY PerfTestDebugCallback(GLenum source,199GLenum type,200GLuint id,201GLenum severity,202GLsizei length,203const GLchar *message,204const void *userParam)205{206// Early exit on non-errors.207if (type != GL_DEBUG_TYPE_ERROR || !userParam)208{209return;210}211212ANGLERenderTest *renderTest =213const_cast<ANGLERenderTest *>(reinterpret_cast<const ANGLERenderTest *>(userParam));214renderTest->onErrorMessage(message);215}216217double ComputeMean(const std::vector<double> &values)218{219double mean = 0;220for (double value : values)221{222mean += value;223}224mean /= static_cast<double>(values.size());225return mean;226}227} // anonymous namespace228229TraceEvent::TraceEvent(char phaseIn,230const char *categoryNameIn,231const char *nameIn,232double timestampIn,233uint32_t tidIn)234: phase(phaseIn), categoryName(categoryNameIn), name{}, timestamp(timestampIn), tid(tidIn)235{236ASSERT(strlen(nameIn) < kMaxNameLen);237strcpy(name, nameIn);238}239240ANGLEPerfTest::ANGLEPerfTest(const std::string &name,241const std::string &backend,242const std::string &story,243unsigned int iterationsPerStep,244const char *units)245: mName(name),246mBackend(backend),247mStory(story),248mGPUTimeNs(0),249mSkipTest(false),250mStepsToRun(std::max(gStepsPerTrial, gMaxStepsPerformed)),251mTrialNumStepsPerformed(0),252mTotalNumStepsPerformed(0),253mIterationsPerStep(iterationsPerStep),254mRunning(true)255{256if (mStory == "")257{258mStory = "baseline_story";259}260if (mStory[0] == '_')261{262mStory = mStory.substr(1);263}264mReporter = std::make_unique<perf_test::PerfResultReporter>(mName + mBackend, mStory);265mReporter->RegisterImportantMetric(".wall_time", units);266mReporter->RegisterImportantMetric(".gpu_time", units);267mReporter->RegisterFyiMetric(".trial_steps", "count");268mReporter->RegisterFyiMetric(".total_steps", "count");269mReporter->RegisterFyiMetric(".steps_to_run", "count");270}271272ANGLEPerfTest::~ANGLEPerfTest() {}273274void ANGLEPerfTest::run()275{276if (mSkipTest)277{278return;279}280281if (mStepsToRun <= 0)282{283// We don't call finish between calibration steps when calibrating non-Render tests. The284// Render tests will have already calibrated when this code is run.285calibrateStepsToRun(RunLoopPolicy::RunContinuously);286ASSERT(mStepsToRun > 0);287}288289uint32_t numTrials = OneFrame() ? 1 : gTestTrials;290if (gVerboseLogging)291{292printf("Test Trials: %d\n", static_cast<int>(numTrials));293}294295for (uint32_t trial = 0; trial < numTrials; ++trial)296{297doRunLoop(gMaxTrialTimeSeconds, mStepsToRun, RunLoopPolicy::RunContinuously);298printResults();299if (gVerboseLogging)300{301double trialTime = mTimer.getElapsedTime();302printf("Trial %d time: %.2lf seconds.\n", trial + 1, trialTime);303304double secondsPerStep = trialTime / static_cast<double>(mTrialNumStepsPerformed);305double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);306mTestTrialResults.push_back(secondsPerIteration * 1000.0);307}308}309310if (gVerboseLogging && !mTestTrialResults.empty())311{312double numResults = static_cast<double>(mTestTrialResults.size());313double mean = ComputeMean(mTestTrialResults);314315double variance = 0;316for (double trialResult : mTestTrialResults)317{318double difference = trialResult - mean;319variance += difference * difference;320}321variance /= numResults;322323double standardDeviation = std::sqrt(variance);324double coefficientOfVariation = standardDeviation / mean;325326if (mean < 0.001)327{328printf("Mean result time: %.4lf ns.\n", mean * 1000.0);329}330else331{332printf("Mean result time: %.4lf ms.\n", mean);333}334printf("Coefficient of variation: %.2lf%%\n", coefficientOfVariation * 100.0);335}336}337338void ANGLEPerfTest::doRunLoop(double maxRunTime, int maxStepsToRun, RunLoopPolicy runPolicy)339{340mTrialNumStepsPerformed = 0;341mRunning = true;342mGPUTimeNs = 0;343mTimer.start();344startTest();345346while (mRunning)347{348if (gMaxStepsPerformed > 0 && mTotalNumStepsPerformed >= gMaxStepsPerformed)349{350if (gVerboseLogging)351{352printf("Stopping test after %d steps.\n", mTotalNumStepsPerformed);353}354mRunning = false;355}356else if (mTimer.getElapsedTime() > maxRunTime)357{358mRunning = false;359}360else if (mTrialNumStepsPerformed >= maxStepsToRun)361{362mRunning = false;363}364else365{366step();367368if (runPolicy == RunLoopPolicy::FinishEveryStep)369{370glFinish();371}372373if (mRunning)374{375mTrialNumStepsPerformed++;376mTotalNumStepsPerformed++;377}378}379}380finishTest();381mTimer.stop();382computeGPUTime();383}384385void ANGLEPerfTest::SetUp() {}386387void ANGLEPerfTest::TearDown() {}388389double ANGLEPerfTest::printResults()390{391double elapsedTimeSeconds[2] = {392mTimer.getElapsedTime(),393mGPUTimeNs * 1e-9,394};395396const char *clockNames[2] = {397".wall_time",398".gpu_time",399};400401// If measured gpu time is non-zero, print that too.402size_t clocksToOutput = mGPUTimeNs > 0 ? 2 : 1;403404double retValue = 0.0;405for (size_t i = 0; i < clocksToOutput; ++i)406{407double secondsPerStep =408elapsedTimeSeconds[i] / static_cast<double>(mTrialNumStepsPerformed);409double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);410411perf_test::MetricInfo metricInfo;412std::string units;413// Lazily register the metric, re-using the existing units if it is414// already registered.415if (!mReporter->GetMetricInfo(clockNames[i], &metricInfo))416{417printf("Seconds per iteration: %lf\n", secondsPerIteration);418units = secondsPerIteration > 1e-3 ? "us" : "ns";419mReporter->RegisterImportantMetric(clockNames[i], units);420}421else422{423units = metricInfo.units;424}425426if (units == "ms")427{428retValue = secondsPerIteration * kMilliSecondsPerSecond;429}430else if (units == "us")431{432retValue = secondsPerIteration * kMicroSecondsPerSecond;433}434else435{436retValue = secondsPerIteration * kNanoSecondsPerSecond;437}438mReporter->AddResult(clockNames[i], retValue);439}440441if (gVerboseLogging)442{443double fps = static_cast<double>(mTrialNumStepsPerformed * mIterationsPerStep) /444elapsedTimeSeconds[0];445printf("Ran %0.2lf iterations per second\n", fps);446}447448if (gCalibration)449{450mReporter->AddResult(".steps_to_run", static_cast<size_t>(mStepsToRun));451}452else453{454mReporter->AddResult(".trial_steps", static_cast<size_t>(mTrialNumStepsPerformed));455mReporter->AddResult(".total_steps", static_cast<size_t>(mTotalNumStepsPerformed));456}457458// Output histogram JSON set format if enabled.459double secondsPerStep = elapsedTimeSeconds[0] / static_cast<double>(mTrialNumStepsPerformed);460double secondsPerIteration = secondsPerStep / static_cast<double>(mIterationsPerStep);461TestSuite::GetInstance()->addHistogramSample(mName + mBackend, mStory,462secondsPerIteration * kMilliSecondsPerSecond,463"ms_smallerIsBetter");464return retValue;465}466467double ANGLEPerfTest::normalizedTime(size_t value) const468{469return static_cast<double>(value) / static_cast<double>(mTrialNumStepsPerformed);470}471472void ANGLEPerfTest::calibrateStepsToRun(RunLoopPolicy policy)473{474// Run initially for "gCalibrationTimeSeconds" using the run loop policy.475doRunLoop(gCalibrationTimeSeconds, std::numeric_limits<int>::max(), policy);476477double elapsedTime = mTimer.getElapsedTime();478int stepsPerformed = mTrialNumStepsPerformed;479480double scale = gCalibrationTimeSeconds / elapsedTime;481int stepsToRun = static_cast<int>(static_cast<double>(stepsPerformed) * scale);482stepsToRun = std::max(1, stepsPerformed);483if (getStepAlignment() != 1)484{485stepsToRun = rx::roundUp(stepsToRun, getStepAlignment());486}487488// The run loop policy "FinishEveryStep" indicates we're running GPU tests. GPU work489// completes asynchronously from the issued CPU steps. Therefore we need to call490// glFinish before we can compute an accurate time elapsed by the test.491//492// To compute an accurate value for "mStepsToRun" we do a two-pass calibration. The493// first pass runs for "gCalibrationTimeSeconds" and calls glFinish every step. The494// estimated steps to run using this method is very inaccurate but is guaranteed to495// complete in a fixed amount of time. Using that estimate we then run a second pass496// and call glFinish a single time after "mStepsToRun" steps. We can then use the497// "actual" time elapsed to compute an accurate estimate for "mStepsToRun".498499if (policy == RunLoopPolicy::FinishEveryStep)500{501for (int loopIndex = 0; loopIndex < gWarmupLoops; ++loopIndex)502{503doRunLoop(gMaxTrialTimeSeconds, stepsToRun, RunLoopPolicy::RunContinuously);504505// Compute mean of the calibration results.506double sampleElapsedTime = mTimer.getElapsedTime();507int sampleStepsPerformed = mTrialNumStepsPerformed;508509if (gVerboseLogging)510{511printf("Calibration loop took %.2lf seconds, with %d steps.\n", sampleElapsedTime,512sampleStepsPerformed);513}514515// Scale steps down according to the time that exceeded one second.516double sampleScale = gCalibrationTimeSeconds / sampleElapsedTime;517stepsToRun = static_cast<int>(static_cast<double>(sampleStepsPerformed) * sampleScale);518stepsToRun = std::max(1, stepsToRun);519if (getStepAlignment() != 1)520{521stepsToRun = rx::roundUp(stepsToRun, getStepAlignment());522}523}524}525526// Scale steps down according to the time that exceeded one second.527mStepsToRun = stepsToRun;528529if (gVerboseLogging)530{531printf("Running %d steps after calibration.", mStepsToRun);532}533534// Calibration allows the perf test runner script to save some time.535if (gCalibration)536{537printResults();538return;539}540}541542int ANGLEPerfTest::getStepAlignment() const543{544// Default: No special alignment rules.545return 1;546}547548std::string RenderTestParams::backend() const549{550std::stringstream strstr;551552switch (driver)553{554case GLESDriverType::AngleEGL:555break;556case GLESDriverType::SystemWGL:557case GLESDriverType::SystemEGL:558strstr << "_native";559break;560default:561assert(0);562return "_unk";563}564565switch (getRenderer())566{567case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:568break;569case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE:570strstr << "_d3d11";571break;572case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE:573strstr << "_d3d9";574break;575case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:576strstr << "_gl";577break;578case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:579strstr << "_gles";580break;581case EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE:582strstr << "_vulkan";583break;584default:585assert(0);586return "_unk";587}588589switch (eglParameters.deviceType)590{591case EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE:592strstr << "_null";593break;594case EGL_PLATFORM_ANGLE_DEVICE_TYPE_SWIFTSHADER_ANGLE:595strstr << "_swiftshader";596break;597default:598break;599}600601return strstr.str();602}603604std::string RenderTestParams::story() const605{606switch (surfaceType)607{608case SurfaceType::Window:609return "";610case SurfaceType::WindowWithVSync:611return "_vsync";612case SurfaceType::Offscreen:613return "_offscreen";614default:615UNREACHABLE();616return "";617}618}619620std::string RenderTestParams::backendAndStory() const621{622return backend() + story();623}624625ANGLERenderTest::ANGLERenderTest(const std::string &name,626const RenderTestParams &testParams,627const char *units)628: ANGLEPerfTest(name,629testParams.backend(),630testParams.story(),631OneFrame() ? 1 : testParams.iterationsPerStep,632units),633mTestParams(testParams),634mIsTimestampQueryAvailable(false),635mGLWindow(nullptr),636mOSWindow(nullptr),637mSwapEnabled(true)638{639// Force fast tests to make sure our slowest bots don't time out.640if (OneFrame())641{642const_cast<RenderTestParams &>(testParams).iterationsPerStep = 1;643}644645// Try to ensure we don't trigger allocation during execution.646mTraceEventBuffer.reserve(kInitialTraceEventBufferSize);647648switch (testParams.driver)649{650case GLESDriverType::AngleEGL:651mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);652mEntryPointsLib.reset(OpenSharedLibrary(ANGLE_EGL_LIBRARY_NAME, SearchType::ModuleDir));653break;654case GLESDriverType::SystemEGL:655#if defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)656mGLWindow = EGLWindow::New(testParams.majorVersion, testParams.minorVersion);657mEntryPointsLib.reset(OpenSharedLibraryWithExtension(658GetNativeEGLLibraryNameWithExtension(), SearchType::SystemDir));659#else660std::cerr << "Not implemented." << std::endl;661mSkipTest = true;662#endif // defined(ANGLE_USE_UTIL_LOADER) && !defined(ANGLE_PLATFORM_WINDOWS)663break;664case GLESDriverType::SystemWGL:665#if defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)666mGLWindow = WGLWindow::New(testParams.majorVersion, testParams.minorVersion);667mEntryPointsLib.reset(OpenSharedLibrary("opengl32", SearchType::SystemDir));668#else669std::cout << "WGL driver not available. Skipping test." << std::endl;670mSkipTest = true;671#endif // defined(ANGLE_USE_UTIL_LOADER) && defined(ANGLE_PLATFORM_WINDOWS)672break;673default:674std::cerr << "Error in switch." << std::endl;675mSkipTest = true;676break;677}678}679680ANGLERenderTest::~ANGLERenderTest()681{682OSWindow::Delete(&mOSWindow);683GLWindowBase::Delete(&mGLWindow);684}685686void ANGLERenderTest::addExtensionPrerequisite(const char *extensionName)687{688mExtensionPrerequisites.push_back(extensionName);689}690691void ANGLERenderTest::SetUp()692{693if (mSkipTest)694{695return;696}697698ANGLEPerfTest::SetUp();699700// Set a consistent CPU core affinity and high priority.701StabilizeCPUForBenchmarking();702703mOSWindow = OSWindow::New();704705if (!mGLWindow)706{707mSkipTest = true;708return;709}710711mPlatformMethods.overrideWorkaroundsD3D = OverrideWorkaroundsD3D;712mPlatformMethods.logError = CustomLogError;713mPlatformMethods.logWarning = EmptyPlatformMethod;714mPlatformMethods.logInfo = EmptyPlatformMethod;715mPlatformMethods.addTraceEvent = AddPerfTraceEvent;716mPlatformMethods.getTraceCategoryEnabledFlag = GetPerfTraceCategoryEnabled;717mPlatformMethods.updateTraceEventDuration = UpdateTraceEventDuration;718mPlatformMethods.monotonicallyIncreasingTime = MonotonicallyIncreasingTime;719mPlatformMethods.context = this;720721if (!mOSWindow->initialize(mName, mTestParams.windowWidth, mTestParams.windowHeight))722{723mSkipTest = true;724FAIL() << "Failed initializing OSWindow";725// FAIL returns.726}727728// Override platform method parameter.729EGLPlatformParameters withMethods = mTestParams.eglParameters;730withMethods.platformMethods = &mPlatformMethods;731732// Request a common framebuffer config733mConfigParams.redBits = 8;734mConfigParams.greenBits = 8;735mConfigParams.blueBits = 8;736mConfigParams.alphaBits = 8;737mConfigParams.depthBits = 24;738mConfigParams.stencilBits = 8;739mConfigParams.colorSpace = mTestParams.colorSpace;740if (mTestParams.surfaceType != SurfaceType::WindowWithVSync)741{742mConfigParams.swapInterval = 0;743}744745if (!mGLWindow->initializeGL(mOSWindow, mEntryPointsLib.get(), mTestParams.driver, withMethods,746mConfigParams))747{748mSkipTest = true;749FAIL() << "Failed initializing GL Window";750// FAIL returns.751}752753// Disable vsync (if not done by the window init).754if (mTestParams.surfaceType != SurfaceType::WindowWithVSync)755{756if (!mGLWindow->setSwapInterval(0))757{758mSkipTest = true;759FAIL() << "Failed setting swap interval";760// FAIL returns.761}762}763764mIsTimestampQueryAvailable = IsGLExtensionEnabled("GL_EXT_disjoint_timer_query");765766if (!areExtensionPrerequisitesFulfilled())767{768mSkipTest = true;769}770771if (mSkipTest)772{773return;774}775776#if defined(ANGLE_ENABLE_ASSERTS)777if (IsGLExtensionEnabled("GL_KHR_debug"))778{779EnableDebugCallback(&PerfTestDebugCallback, this);780}781#endif782783initializeBenchmark();784785if (mTestParams.iterationsPerStep == 0)786{787mSkipTest = true;788FAIL() << "Please initialize 'iterationsPerStep'.";789// FAIL returns.790}791792if (gVerboseLogging)793{794printf("GL_RENDERER: %s\n", glGetString(GL_RENDERER));795printf("GL_VERSION: %s\n", glGetString(GL_VERSION));796}797798mTestTrialResults.reserve(gTestTrials);799800for (int loopIndex = 0; loopIndex < gWarmupLoops; ++loopIndex)801{802doRunLoop(gCalibrationTimeSeconds, std::numeric_limits<int>::max(),803RunLoopPolicy::FinishEveryStep);804if (gVerboseLogging)805{806printf("Warm-up loop took %.2lf seconds.\n", mTimer.getElapsedTime());807}808}809810if (mStepsToRun <= 0)811{812// Ensure we always call Finish when calibrating Render tests. This completes our work813// between calibration measurements.814calibrateStepsToRun(RunLoopPolicy::FinishEveryStep);815}816}817818void ANGLERenderTest::TearDown()819{820if (!mSkipTest)821{822destroyBenchmark();823}824825if (mGLWindow)826{827mGLWindow->destroyGL();828mGLWindow = nullptr;829}830831if (mOSWindow)832{833mOSWindow->destroy();834mOSWindow = nullptr;835}836837// Dump trace events to json file.838if (gEnableTrace)839{840DumpTraceEventsToJSONFile(mTraceEventBuffer, gTraceFile);841}842843ANGLEPerfTest::TearDown();844}845846void ANGLERenderTest::beginInternalTraceEvent(const char *name)847{848if (gEnableTrace)849{850mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[0].name, name,851MonotonicallyIncreasingTime(&mPlatformMethods),852getCurrentThreadSerial());853}854}855856void ANGLERenderTest::endInternalTraceEvent(const char *name)857{858if (gEnableTrace)859{860mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[0].name, name,861MonotonicallyIncreasingTime(&mPlatformMethods),862getCurrentThreadSerial());863}864}865866void ANGLERenderTest::beginGLTraceEvent(const char *name, double hostTimeSec)867{868if (gEnableTrace)869{870mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_BEGIN, gTraceCategories[1].name, name,871hostTimeSec, getCurrentThreadSerial());872}873}874875void ANGLERenderTest::endGLTraceEvent(const char *name, double hostTimeSec)876{877if (gEnableTrace)878{879mTraceEventBuffer.emplace_back(TRACE_EVENT_PHASE_END, gTraceCategories[1].name, name,880hostTimeSec, getCurrentThreadSerial());881}882}883884void ANGLERenderTest::step()885{886beginInternalTraceEvent("step");887888// Clear events that the application did not process from this frame889Event event;890bool closed = false;891while (popEvent(&event))892{893// If the application did not catch a close event, close now894if (event.Type == Event::EVENT_CLOSED)895{896closed = true;897}898}899900if (closed)901{902abortTest();903}904else905{906drawBenchmark();907908// Swap is needed so that the GPU driver will occasionally flush its909// internal command queue to the GPU. This is enabled for null back-end910// devices because some back-ends (e.g. Vulkan) also accumulate internal911// command queues.912if (mSwapEnabled)913{914mGLWindow->swap();915}916mOSWindow->messageLoop();917918#if defined(ANGLE_ENABLE_ASSERTS)919if (!gRetraceMode)920{921EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());922}923#endif // defined(ANGLE_ENABLE_ASSERTS)924}925926endInternalTraceEvent("step");927}928929void ANGLERenderTest::startGpuTimer()930{931if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)932{933glGenQueriesEXT(1, &mCurrentTimestampBeginQuery);934glQueryCounterEXT(mCurrentTimestampBeginQuery, GL_TIMESTAMP_EXT);935}936}937938void ANGLERenderTest::stopGpuTimer()939{940if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)941{942GLuint endQuery = 0;943glGenQueriesEXT(1, &endQuery);944glQueryCounterEXT(endQuery, GL_TIMESTAMP_EXT);945mTimestampQueries.push_back({mCurrentTimestampBeginQuery, endQuery});946}947}948949void ANGLERenderTest::computeGPUTime()950{951if (mTestParams.trackGpuTime && mIsTimestampQueryAvailable)952{953for (const TimestampSample &sample : mTimestampQueries)954{955uint64_t beginGLTimeNs = 0;956uint64_t endGLTimeNs = 0;957glGetQueryObjectui64vEXT(sample.beginQuery, GL_QUERY_RESULT_EXT, &beginGLTimeNs);958glGetQueryObjectui64vEXT(sample.endQuery, GL_QUERY_RESULT_EXT, &endGLTimeNs);959glDeleteQueriesEXT(1, &sample.beginQuery);960glDeleteQueriesEXT(1, &sample.endQuery);961mGPUTimeNs += endGLTimeNs - beginGLTimeNs;962}963964mTimestampQueries.clear();965}966}967968void ANGLERenderTest::startTest() {}969970void ANGLERenderTest::finishTest()971{972if (mTestParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE &&973!gNoFinish && !gRetraceMode)974{975glFinish();976}977}978979bool ANGLERenderTest::popEvent(Event *event)980{981return mOSWindow->popEvent(event);982}983984OSWindow *ANGLERenderTest::getWindow()985{986return mOSWindow;987}988989GLWindowBase *ANGLERenderTest::getGLWindow()990{991return mGLWindow;992}993994bool ANGLERenderTest::areExtensionPrerequisitesFulfilled() const995{996for (const char *extension : mExtensionPrerequisites)997{998if (!CheckExtensionExists(reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)),999extension))1000{1001std::cout << "Test skipped due to missing extension: " << extension << std::endl;1002return false;1003}1004}1005return true;1006}10071008void ANGLERenderTest::setWebGLCompatibilityEnabled(bool webglCompatibility)1009{1010mConfigParams.webGLCompatibility = webglCompatibility;1011}10121013void ANGLERenderTest::setRobustResourceInit(bool enabled)1014{1015mConfigParams.robustResourceInit = enabled;1016}10171018std::vector<TraceEvent> &ANGLERenderTest::getTraceEventBuffer()1019{1020return mTraceEventBuffer;1021}10221023void ANGLERenderTest::onErrorMessage(const char *errorMessage)1024{1025abortTest();1026FAIL() << "Failing test because of unexpected error:\n" << errorMessage << "\n";1027}10281029uint32_t ANGLERenderTest::getCurrentThreadSerial()1030{1031std::thread::id id = std::this_thread::get_id();10321033for (uint32_t serial = 0; serial < static_cast<uint32_t>(mThreadIDs.size()); ++serial)1034{1035if (mThreadIDs[serial] == id)1036{1037return serial + 1;1038}1039}10401041mThreadIDs.push_back(id);1042return static_cast<uint32_t>(mThreadIDs.size());1043}10441045namespace angle1046{1047double GetHostTimeSeconds()1048{1049// Move the time origin to the first call to this function, to avoid generating unnecessarily1050// large timestamps.1051static double origin = GetCurrentTime();1052return GetCurrentTime() - origin;1053}1054} // namespace angle105510561057