Edit

kc3-lang/angle/src/tests/perf_tests/TracePerfTest.cpp

Branch :

  • Show log

    Commit

  • Author : Cody Northrop
    Date : 2020-05-12 14:19:06
    Hash : 3a909841
    Message : Perf: Add Reset call to trace tests The traces are already updated to contain the Reset calls, now actually call them from TracePerfTests. Test: angle_perftests --gtest_filter="*Trace*" Bug: b/152512564 Bug: angleproject:4599 Change-Id: I18b6726bc98b96f132c3245c51420cbfed1b84d8 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2197284 Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Courtney Goeltzenleuchter <courtneygo@google.com> Commit-Queue: Cody Northrop <cnorthrop@google.com>

  • src/tests/perf_tests/TracePerfTest.cpp
  • //
    // Copyright 2020 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    // TracePerf:
    //   Performance test for ANGLE replaying traces.
    //
    
    #include <gtest/gtest.h>
    #include "common/PackedEnums.h"
    #include "common/system_utils.h"
    #include "tests/perf_tests/ANGLEPerfTest.h"
    #include "tests/perf_tests/DrawCallPerfParams.h"
    #include "util/egl_loader_autogen.h"
    #include "util/frame_capture_utils.h"
    #include "util/png_utils.h"
    
    #include "restricted_traces/restricted_traces_autogen.h"
    
    #include <cassert>
    #include <functional>
    #include <sstream>
    
    using namespace angle;
    using namespace egl_platform;
    
    namespace
    {
    void FramebufferChangeCallback(void *userData, GLenum target, GLuint framebuffer);
    
    struct TracePerfParams final : public RenderTestParams
    {
        // Common default options
        TracePerfParams()
        {
            majorVersion = 3;
            minorVersion = 0;
            windowWidth  = 1920;
            windowHeight = 1080;
            trackGpuTime = true;
    
            // Display the frame after every drawBenchmark invocation
            iterationsPerStep = 1;
        }
    
        std::string story() const override
        {
            std::stringstream strstr;
            strstr << RenderTestParams::story() << "_" << kTraceInfos[testID].name;
            return strstr.str();
        }
    
        RestrictedTraceID testID;
    };
    
    std::ostream &operator<<(std::ostream &os, const TracePerfParams &params)
    {
        os << params.backendAndStory().substr(1);
        return os;
    }
    
    class TracePerfTest : public ANGLERenderTest, public ::testing::WithParamInterface<TracePerfParams>
    {
      public:
        TracePerfTest();
    
        void initializeBenchmark() override;
        void destroyBenchmark() override;
        void drawBenchmark() override;
    
        void onFramebufferChange(GLenum target, GLuint framebuffer);
    
        uint32_t mStartFrame;
        uint32_t mEndFrame;
    
        double getHostTimeFromGLTime(GLint64 glTime);
    
      private:
        struct QueryInfo
        {
            GLuint beginTimestampQuery;
            GLuint endTimestampQuery;
            GLuint framebuffer;
        };
    
        struct TimeSample
        {
            GLint64 glTime;
            double hostTime;
        };
    
        void sampleTime();
        void saveScreenshot(const std::string &screenshotName) override;
    
        // For tracking RenderPass/FBO change timing.
        QueryInfo mCurrentQuery = {};
        std::vector<QueryInfo> mRunningQueries;
        std::vector<TimeSample> mTimeline;
    
        std::string mStartingDirectory;
    };
    
    TracePerfTest::TracePerfTest()
        : ANGLERenderTest("TracePerf", GetParam()), mStartFrame(0), mEndFrame(0)
    {
        // TODO(anglebug.com/4533) This fails after the upgrade to the 26.20.100.7870 driver.
        if (IsWindows() && IsIntel() &&
            GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE &&
            GetParam().testID == RestrictedTraceID::manhattan_10)
        {
            mSkipTest = true;
        }
    }
    
    void TracePerfTest::initializeBenchmark()
    {
        const auto &params = GetParam();
    
        mStartingDirectory = angle::GetCWD().value();
    
        // To load the trace data path correctly we set the CWD to the executable dir.
        if (!IsAndroid())
        {
            std::string exeDir = angle::GetExecutableDirectory();
            angle::SetCWD(exeDir.c_str());
        }
    
        const TraceInfo &traceInfo = kTraceInfos[params.testID];
        mStartFrame                = traceInfo.startFrame;
        mEndFrame                  = traceInfo.endFrame;
        SetBinaryDataDecompressCallback(params.testID, DecompressBinaryData);
    
        std::stringstream testDataDirStr;
        testDataDirStr << ANGLE_TRACE_DATA_DIR << "/" << traceInfo.name;
        std::string testDataDir = testDataDirStr.str();
        SetBinaryDataDir(params.testID, testDataDir.c_str());
    
        // Potentially slow. Can load a lot of resources.
        SetupReplay(params.testID);
    
        ASSERT_TRUE(mEndFrame > mStartFrame);
    
        getWindow()->setVisible(true);
    }
    
    #undef TRACE_TEST_CASE
    
    void TracePerfTest::destroyBenchmark()
    {
        // In order for the next test to load, restore the working directory
        angle::SetCWD(mStartingDirectory.c_str());
    }
    
    void TracePerfTest::sampleTime()
    {
        if (mIsTimestampQueryAvailable)
        {
            GLint64 glTime;
            // glGetInteger64vEXT is exported by newer versions of the timer query extensions.
            // Unfortunately only the core EP is exposed by some desktop drivers (e.g. NVIDIA).
            if (glGetInteger64vEXT)
            {
                glGetInteger64vEXT(GL_TIMESTAMP_EXT, &glTime);
            }
            else
            {
                glGetInteger64v(GL_TIMESTAMP_EXT, &glTime);
            }
            mTimeline.push_back({glTime, angle::GetHostTimeSeconds()});
        }
    }
    
    void TracePerfTest::drawBenchmark()
    {
        // Add a time sample from GL and the host.
        sampleTime();
    
        startGpuTimer();
    
        for (uint32_t frame = mStartFrame; frame < mEndFrame; ++frame)
        {
            char frameName[32];
            sprintf(frameName, "Frame %u", frame);
            beginInternalTraceEvent(frameName);
    
            ReplayFrame(GetParam().testID, frame);
            getGLWindow()->swap();
    
            endInternalTraceEvent(frameName);
        }
    
        ResetReplay(GetParam().testID);
    
        // Process any running queries once per iteration.
        for (size_t queryIndex = 0; queryIndex < mRunningQueries.size();)
        {
            const QueryInfo &query = mRunningQueries[queryIndex];
    
            GLuint endResultAvailable = 0;
            glGetQueryObjectuivEXT(query.endTimestampQuery, GL_QUERY_RESULT_AVAILABLE,
                                   &endResultAvailable);
    
            if (endResultAvailable == GL_TRUE)
            {
                char fboName[32];
                sprintf(fboName, "FBO %u", query.framebuffer);
    
                GLint64 beginTimestamp = 0;
                glGetQueryObjecti64vEXT(query.beginTimestampQuery, GL_QUERY_RESULT, &beginTimestamp);
                glDeleteQueriesEXT(1, &query.beginTimestampQuery);
                double beginHostTime = getHostTimeFromGLTime(beginTimestamp);
                beginGLTraceEvent(fboName, beginHostTime);
    
                GLint64 endTimestamp = 0;
                glGetQueryObjecti64vEXT(query.endTimestampQuery, GL_QUERY_RESULT, &endTimestamp);
                glDeleteQueriesEXT(1, &query.endTimestampQuery);
                double endHostTime = getHostTimeFromGLTime(endTimestamp);
                endGLTraceEvent(fboName, endHostTime);
    
                mRunningQueries.erase(mRunningQueries.begin() + queryIndex);
            }
            else
            {
                queryIndex++;
            }
        }
    
        stopGpuTimer();
    }
    
    // Converts a GL timestamp into a host-side CPU time aligned with "GetHostTimeSeconds".
    // This check is necessary to line up sampled trace events in a consistent timeline.
    // Uses a linear interpolation from a series of samples. We do a blocking call to sample
    // both host and GL time once per swap. We then find the two closest GL timestamps and
    // interpolate the host times between them to compute our result. If we are past the last
    // GL timestamp we sample a new data point pair.
    double TracePerfTest::getHostTimeFromGLTime(GLint64 glTime)
    {
        // Find two samples to do a lerp.
        size_t firstSampleIndex = mTimeline.size() - 1;
        while (firstSampleIndex > 0)
        {
            if (mTimeline[firstSampleIndex].glTime < glTime)
            {
                break;
            }
            firstSampleIndex--;
        }
    
        // Add an extra sample if we're missing an ending sample.
        if (firstSampleIndex == mTimeline.size() - 1)
        {
            sampleTime();
        }
    
        const TimeSample &start = mTimeline[firstSampleIndex];
        const TimeSample &end   = mTimeline[firstSampleIndex + 1];
    
        // Note: we have observed in some odd cases later timestamps producing values that are
        // smaller than preceding timestamps. This bears further investigation.
    
        // Compute the scaling factor for the lerp.
        double glDelta = static_cast<double>(glTime - start.glTime);
        double glRange = static_cast<double>(end.glTime - start.glTime);
        double t       = glDelta / glRange;
    
        // Lerp(t1, t2, t)
        double hostRange = end.hostTime - start.hostTime;
        return mTimeline[firstSampleIndex].hostTime + hostRange * t;
    }
    
    // Callback from the perf tests.
    void TracePerfTest::onFramebufferChange(GLenum target, GLuint framebuffer)
    {
        if (!mIsTimestampQueryAvailable)
            return;
    
        if (target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER)
            return;
    
        // We have at most one active timestamp query at a time. This code will end the current
        // query and immediately start a new one.
        if (mCurrentQuery.beginTimestampQuery != 0)
        {
            glGenQueriesEXT(1, &mCurrentQuery.endTimestampQuery);
            glQueryCounterEXT(mCurrentQuery.endTimestampQuery, GL_TIMESTAMP_EXT);
            mRunningQueries.push_back(mCurrentQuery);
            mCurrentQuery = {};
        }
    
        ASSERT(mCurrentQuery.beginTimestampQuery == 0);
    
        glGenQueriesEXT(1, &mCurrentQuery.beginTimestampQuery);
        glQueryCounterEXT(mCurrentQuery.beginTimestampQuery, GL_TIMESTAMP_EXT);
        mCurrentQuery.framebuffer = framebuffer;
    }
    
    void TracePerfTest::saveScreenshot(const std::string &screenshotName)
    {
        // Render a single frame.
        RestrictedTraceID testID   = GetParam().testID;
        const TraceInfo &traceInfo = kTraceInfos[testID];
        ReplayFrame(testID, traceInfo.startFrame);
    
        // RGBA 4-byte data.
        uint32_t pixelCount = mTestParams.windowWidth * mTestParams.windowHeight;
        std::vector<uint8_t> pixelData(pixelCount * 4);
    
        glBindFramebuffer(GL_FRAMEBUFFER, 0);
        glReadPixels(0, 0, mTestParams.windowWidth, mTestParams.windowHeight, GL_RGBA, GL_UNSIGNED_BYTE,
                     pixelData.data());
    
        // Convert to RGB and flip y.
        std::vector<uint8_t> rgbData(pixelCount * 3);
        for (EGLint y = 0; y < mTestParams.windowHeight; ++y)
        {
            for (EGLint x = 0; x < mTestParams.windowWidth; ++x)
            {
                EGLint srcPixel = x + y * mTestParams.windowWidth;
                EGLint dstPixel = x + (mTestParams.windowHeight - y - 1) * mTestParams.windowWidth;
                memcpy(&rgbData[dstPixel * 3], &pixelData[srcPixel * 4], 3);
            }
        }
    
        angle::SavePNGRGB(screenshotName.c_str(), "ANGLE Screenshot", mTestParams.windowWidth,
                          mTestParams.windowHeight, rgbData);
    
        // Finish the frame loop.
        for (uint32_t nextFrame = traceInfo.startFrame + 1; nextFrame < traceInfo.endFrame; ++nextFrame)
        {
            ReplayFrame(testID, nextFrame);
        }
        getGLWindow()->swap();
        glFinish();
    }
    
    ANGLE_MAYBE_UNUSED void FramebufferChangeCallback(void *userData, GLenum target, GLuint framebuffer)
    {
        reinterpret_cast<TracePerfTest *>(userData)->onFramebufferChange(target, framebuffer);
    }
    
    TEST_P(TracePerfTest, Run)
    {
        run();
    }
    
    TracePerfParams CombineTestID(const TracePerfParams &in, RestrictedTraceID id)
    {
        TracePerfParams out = in;
        out.testID          = id;
        return out;
    }
    
    using namespace params;
    using P = TracePerfParams;
    
    std::vector<P> gTestsWithID =
        CombineWithValues({P()}, AllEnums<RestrictedTraceID>(), CombineTestID);
    std::vector<P> gTestsWithRenderer = CombineWithFuncs(gTestsWithID, {Vulkan<P>, EGL<P>});
    ANGLE_INSTANTIATE_TEST_ARRAY(TracePerfTest, gTestsWithRenderer);
    
    }  // anonymous namespace