D3D11 Query查询耗时

    确实的来说,这是个Debug/Profile的需求,在运行期这个时间毫无意义,有意义的是两帧之间走过了多少时间,而这个,可以用来查询某一个效果所用耗时,废话不多少,进入正题.

    首先要创建查询接口,也就是ID3D11Query* 接口,与查询CPU耗时一样,我们需要三个量,GPU时钟频率,起始时的时间戳,结束时的时间戳.听上去听简单,不过略比CPU的复杂,因为需要通过Begin和End来操作,首先创建三个查询接口:

    使用ID3D11Device::CreateQuery函数,函数声明如下:

        HRESULT CreateQuery( 

            [in]   const D3D11_QUERY_DESC *pQueryDesc,

            [out]  ID3D11Query **ppQuery

        );

    我们需要填充一个D3D11_QUERY_DESC结构,来指明查询的内容, D3D11_QUERY_DESC结构如下:

        typedef struct D3D11_QUERY_DESC { 

            D3D11_QUERY Query;

            UINT MiscFlags;

} D3D11_QUERY_DESC

    简单的,将MiscFlags置为0,查询类型有很多种,只捡我们需要的讲,其中一个就是查询时间戳的 D3D11_QUERY_TIMESTAMP,其他的查询,需要在查询之前调用ID3D11Device::Begin,查询之后使用ID3D11Device::End,但是时间戳很明显的不是"一段时间"的查询,而是一个点,所以我们只需要调用End就行了,首先我们就是要获取起始时间戳,意味着创建完查询后,立即调用End,代码如下:

        D3D11_QUERY_DESC desc;

desc.Query =D3D11_QUERY_TIMESTAMP;

desc.MiscFlags = 0;

        device->CreateQuery(&desc, & timestampstart;

        context->End(timestampstart);

    这样便获得了起始时间的查询接口,同理,我们还需要创建一个结束时的时间戳查询接口,按照上面的代码再创建一个,然后在结束时调用End,获得数据,所有查询接口的数据通过ID3D11Device::GetData返回,不同的查询接口返回的数据大小不一样,这个函数需要你自己提供一个BUFFER(你得保证有足够的大小),对于D3D11_QUERY_TIMESTAMP,GetData返回的是一个UINT64(windows.h),于是我们需要这样:

            std::uint64_t starttime = 0;

            while (context->GetData(timestampstart, &starttime, sizeof(starttime), 0) != S_OK)

                ;

            std::uint64_t endtime = 0;

            while (context->GetData(timestampend, &endtime, sizeof(endtime), 0) != S_OK)

                ;

 

    查询不成功可是不断的去循环调用的哦,接下来是查询GPU时钟频率,值得注意的是,这应该不是一个固定值(),使用D3D11_QUERY_TIMESTAMP_DISJOINT  来查询时钟频率,GetData会填充这样一个

        typedef struct D3D11_QUERY_DATA_TIMESTAMP_DISJOINT { 

             UINT64 Frequency; 

             BOOL   Disjoint; 

        } D3D11_QUERY_DATA_TIMESTAMP_DISJOINT; 

     

结构,很显然,Frequency放的是频率,BOOL变量指示是否有效,如同Disjoint的意思,当其为TRUE的时候,说明时间戳计数因为某些原因而导致了不准确,只有当其为FALSE,查询的两个时间戳之差才是有效的,很显然,加上如下代码:

    desc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;

    desc.MiscFlags = 0;

    device->CreateQuery(&desc, &disjoint);

注意这个查询需要遵循Begin,End调用,于是,创建完之后,我们应该调用Begin

    context->Begin(disjoint);

在查询结束时调用timestampend的End和disjoint的End

    context->End(timestampend);

    context->End(disjoint);

然后我们就可以获得经过时间了:

    std::uint64_t starttime = 0;

    while (context->GetData(timestampstart, &starttime, sizeof(starttime), 0) != S_OK)

                ;

    std::uint64_t endtime = 0;

    while (context->GetData(timestampend, &endtime, sizeof(endtime), 0) != S_OK)

        ;

    D3D11_QUERY_DATA_TIMESTAMP_DISJOINT disjointdata;

    while (context->GetData(disjoint, &disjointdata, sizeof(disjointdata), 0) != S_OK)

                ;

 

    float time = 0.0f;

    if (!disjointdata.Disjoint)

    {

        std::uint64_t delta = endtime - starttime;

        float frequency = static_cast<float>(disjointdata.Frequency);

        time = (delta / frequency);

    }

 

为了方便查询,我们可以写个block类,在某个子操作之前构造一个block,构造函数建立一个查询,析构函数结束一个查询.这个block将自己注册在一个查询管理类中,大概就想这样子:

    profileblock::profileblock(const std::wstring& name)

            :name(name)

        {

            profiler::global_profiler.startprofile(name);

        }

 

    profileblock::~profileblock()

    {

        profiler::global_profiler.endprofile(name);

    }

至于profiler就用一个map,来实现一个字符串对应一个查询

 

具体实现,我是每5帧才进行一次输出的,但没有取平均时间,代码如下:

//头文件

struct d3d11_timer

        {

            static const std::uint64_t querylatency = 5;

 

            ID3D11QueryPtr disjoint[querylatency];

            ID3D11QueryPtr timestampstart[querylatency];

            ID3D11QueryPtr timestampend[querylatency];

            bool started{ false };

            bool finished{ false };

 

            void start(std::uint64_t currframe,ID3D11Device* device, ID3D11DeviceContext* context);

            void end(std::uint64_t currframe, ID3D11DeviceContext* context);

            float time(std::uint64_t currframe, ID3D11DeviceContext* context);

        };

        class profiler

        {

        public:

            static profiler global_profiler;

            

            void init(ID3D11Device* device, ID3D11DeviceContext* context);

 

            void startprofile(const std::wstring& name);

            void endprofile(const std::wstring& name);

 

            void endframe();

        protected:

            static const std::uint64_t querylatency = d3d11_timer::querylatency;

 

            using profilemap = std::map<std::wstring, d3d11_timer>;

 

            profilemap profiles;

            std::uint64_t currframe;

 

            ID3D11DevicePtr device;

            ID3D11DeviceContextPtr context;

        };

        class profileblock

        {

        public:

            profileblock(const std::wstring& name);

            ~profileblock();

        protected:

            std::wstring name;

        };

//源文件

void d3d11_timer::start(std::uint64_t currframe, ID3D11Device* device, ID3D11DeviceContext* context)

        {

            assert(!started);

            assert(!finished);

 

            if (!disjoint[currframe])

            {

                D3D11_QUERY_DESC desc;

                desc.Query = D3D11_QUERY_TIMESTAMP_DISJOINT;

                desc.MiscFlags = 0;

                //there should checke error and throw exception,but i am not write exception hpp

                device->CreateQuery(&desc, &disjoint[currframe]);

                desc.Query = D3D11_QUERY_TIMESTAMP;

                device->CreateQuery(&desc, &timestampstart[currframe]);

                device->CreateQuery(&desc, &timestampend[currframe]);

            }

 

            context->Begin(disjoint[currframe]);

            context->End(timestampstart[currframe]);

 

            started = true;

        }

 

        void d3d11_timer::end(std::uint64_t currframe, ID3D11DeviceContext* context)

        {

            assert(started);

            assert(!finished);

            //this mean insert the timestampend

            //http://msdn.microsoft.com/en-us/library/windows/desktop/ff476191(v=vs.85).aspx

            context->End(timestampend[currframe]);

            context->End(disjoint[currframe]);

 

            started = false;

            finished = true;

        }

 

        float d3d11_timer::time(std::uint64_t currframe, ID3D11DeviceContext* context)

        {

            if (!finished)

                return 0.f;

            finished = false;

            if (!disjoint[currframe])

                return 0.f;

 

            //maybe you can record query time

 

            std::uint64_t starttime = 0;

            while (context->GetData(timestampstart[currframe], &starttime, sizeof(starttime), 0) != S_OK)

                ;

            std::uint64_t endtime = 0;

            while (context->GetData(timestampend[currframe], &endtime, sizeof(endtime), 0) != S_OK)

                ;

            D3D11_QUERY_DATA_TIMESTAMP_DISJOINT disjointdata;

            while (context->GetData(disjoint[currframe], &disjointdata, sizeof(disjointdata), 0) != S_OK)

                ;

 

            float time = 0.0f;

            if (!disjointdata.Disjoint)

            {

                std::uint64_t delta = endtime - starttime;

                float frequency = static_cast<float>(disjointdata.Frequency);

                time = (delta / frequency);

            }

            return time;

        }

 

        profiler profiler::global_profiler;

 

        void profiler::init(ID3D11Device* device, ID3D11DeviceContext* context)

        {

            this->device = device;

            this->context = context;

        }

 

        void profiler::startprofile(const std::wstring& name)

        {

            //Todo : query "game setting"

            auto & profile = profiles[name];

            profile.start(currframe, device, context);

        }

 

        void profiler::endprofile(const std::wstring& name)

        {

            //Todo : query "game setting"

            auto & profile = profiles[name];

            profile.end(currframe, context);

        }

        

        void profiler::endframe()//

        {

            currframe = (currframe + 1) % querylatency;

 

            //Todo : query "query time"

 

            for (auto iter = profiles.begin(); iter != profiles.end(); ++iter)

            {

                auto & profile = (*iter).second;

                

                float time = profile.time(currframe, context);

 

                if (time == 0.f)

                    continue;

                DebugPrintf(L"currframe: %u ",currframe);

                DebugPrintf(L"%s: %f\n", iter->first.c_str(), time);

            }

        }

 

        profileblock::profileblock(const std::wstring& name)

            :name(name)

        {

            profiler::global_profiler.startprofile(name);

        }

 

        profileblock::~profileblock()

        {

            profiler::global_profiler.endprofile(name);

        }

使用他则是

    Void Render()

    {

        XX();

        ….

        global_profiler.endframe();

    }

在XX里面如下

    {

        Profileblock block("XXX")'

        …….

    }

你可能感兴趣的:(query)