November 14, 2016

GPU PROFILING

GPU performance調べ

ID3D11Query

ID3D11Queryで、GPUが処理した様々な情報を取得できるようです。 time stampを取得して、処理にかかった時間を計測できるようにしました。

StartProfileでは、

  • QUERY_TIMESTAMP_DISJOINT を ID3D11DeviceContext::Begin
  • start time用の QUERY_TIMESTAMP を ID3D11DeviceContext::End

EndProfileでは、

  • end time用の QUERY_TIMESTAMP を ID11DeviceContext::End
  • QUERY_TIMESTAMP_DISJOINT を ID3D11DeviceContext::End

Profileの結果取得では、

  • ID11DeviceContext::GetData で、結果を取得

上記の呼び出しで、time stampを取得でき、その差分で時間を計測することができました。 ID11DeviceContext::GetDataは、データが取得できるまで待たなければならない為、別のスレッドで取得するか。 取得自体を遅らせて、数フレーム前の情報を取得しなければ、CPU側の待ちが大きくなってしまうようです。 数フレーム前の情報を取得するには、 Queryをリングバッファで作成して、遅延フレームを設定して数フレーム前の情報を取得するようにしました。

コードは以下の感じ、

    void Profiler::StartProfile(const std::string& name)
    {
        ProfileData& profileData = m_profiles[name];

        if (profileData.DisjointQuery[m_currFrame] == nullptr)
        {
            // Create the queries
            D3D11_QUERY_DESC desc;
            desc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;
            desc.MiscFlags = 0;
            device->CreateQuery(&desc, &profileData.DisjointQuery[m_currFrame]);

            desc.Query = D3D11_QUERY_TIMESTAMP;
            device->CreateQuery(&desc, &profileData.TimestampStartQuery[m_currFrame]);
            device->CreateQuery(&desc, &profileData.TimestampEndQuery[m_currFrame]);
        }

        // Start a disjoint query first
        context->Begin(profileData.DisjointQuery[m_currFrame]);

        // Insert the start timestamp
        context->End(profileData.TimestampStartQuery[m_currFrame]);
    }

    void Profiler::EndProfile(const std::string& name)
    {
        ProfileData& profileData = m_profiles[name];

        // Insert the end timestamp
        context->End(profileData.TimestampEndQuery[m_currFrame]);

        // End the disjoint query
        context->End(profileData.DisjointQuery[m_currFrame]);

        profileData.QueryStarted = false;
        profileData.QueryFinished = true;
    }

    void Profiler::EndFrame()
    {
        m_currFrame = (m_currFrame + 1) % QueryLatency;

        // Iterate over all of the profiles
        for (auto& iter : m_profiles)
        {
            ProfileData& profile = iter.second;
            if (profile.QueryFinished == false) {
                continue;
            }

            profile.QueryFinished = false;

            if (profile.DisjointQuery[m_currFrame] == nullptr)
                continue;

            // Get the query data
            uint64_t startTime = 0;
            while (context->GetData(profile.TimestampStartQuery[m_currFrame], &startTime, sizeof(startTime), 0) != S_OK);

            uint64_t endTime = 0;
            while (context->GetData(profile.TimestampEndQuery[m_currFrame], &endTime, sizeof(endTime), 0) != S_OK);

            D3D11_QUERY_DATA_TIMESTAMP_DISJOINT disjointData;
            while (context->GetData(profile.DisjointQuery[m_currFrame], &disjointData, sizeof(disjointData), 0) != S_OK);

            if (disjointData.Disjoint == false)
            {
                UINT64 delta = endTime - startTime;
                float frequency = static_cast<float>(disjointData.Frequency);
                profile.time = (delta / frequency) * 1000.0f; // msec
            }
        }
    }

結果

その他

EndFrameのCPU処理時間は、0.02msec程度でした。

参考

No comments:

Post a Comment