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,
×tampstart[currframe]);
device->CreateQuery(&desc,
×tampend[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")'
…….
}
D3D11 Query查询耗时的更多相关文章
- Hibernate 学习之Query查询(HQL查询)
package com.itcloud.test; import com.itcloud.pojo.Dept; import org.hibernate.Session; import org.hib ...
- php mysqli query 查询数据库后读取内容的方法
php mysqli query 查询数据库后读取内容的方法 <?php$mysqli = new mysqli("localhost", "my_user&quo ...
- Spring Data @Query查询注解的使用(六)
按照上一篇文章 我们知道 我们定义的方法 都要根据它的规范进行定义 不然就没法实用 这篇我们讲@Query 查询注解 我们就可以不需要遵循它的方法规则去编写 咱们讲@Query定义到方法上 ...
- 分享知识-快乐自己:Hibernate 中Criteria Query查询详解
1):Hibernate 中Criteria Query查询详解 当查询数据时,人们往往需要设置查询条件.在SQL或HQL语句中,查询条件常常放在where子句中. 此外,Hibernate还支持Cr ...
- Elasticsearch(5) --- Query查询和Filter查询
Elasticsearch(5) --- Query查询和Filter查询 这篇博客主要分为 :Query查询和Filter查询.有关复合查询.聚合查询也会单独写篇博客. 一.概念 1.概念 一个查询 ...
- 20191217-关于JPA @Query查询数据一直为空,直接在数据库里执行SQL则可以查出来
20191217-关于JPA @Query查询数据一直为空,直接在数据库里执行SQL则可以查出来 前提:数据库中查询,由于在视图中无主键概念,只是在代码中由逻辑主键.结果:数据中作为逻辑主键中有个字段 ...
- NHibernate系列文章二十六:NHibernate查询之SQL Query查询(附程序下载)
摘要 NHibernate在很早的版本就提供了SQL Query(原生SQL查询),对于很复杂的查询,如果使用其他的查询方式实现比较困难的时候,一般使用SQL Query.使用SQL Query是基于 ...
- 关于MySql数据库设计表与查询耗时分析
本地建一张表persons,使用脚本插入了1000万条数据 下面比较几种查询方法的耗时(查询9000000到9000005这中间5条数据) 查询结果: 1: SELECT * FROM test.pe ...
- Mongodb query查询
Query.All("name", "a", "b");//通过多个元素来匹配数组Query.And(Query.EQ("name ...
随机推荐
- jxse2.6在jdk8下,JxtaMulticastSocket存在的问题
JxtaMulticastSocket覆写了java.net.MulticastSocket的bind方法: @Override public void bind(SocketAddress addr ...
- MYSQL数据库重点:自定义函数、存储过程、触发器、事件、视图
一.自定义函数 mysql自定义函数就是实现程序员需要sql逻辑处理,参数是IN参数,含有RETURNS字句用来指定函数的返回类型,而且函数体必须包含一个RETURN value语句. 语法: 创建: ...
- C语言中返回字符串函数的四种实现方法 2015-05-17 15:00 23人阅读 评论(0) 收藏
C语言中返回字符串函数的四种实现方法 分类: UNIX/LINUX C/C++ 2010-12-29 02:54 11954人阅读 评论(1) 收藏 举报 语言func存储 有四种方式: 1.使用堆空 ...
- OSPF虚链路配置.示例2
先看一个拓扑图 黄色区域是area0,即骨干区域,如果如图示RT1与RT6之间的链路断了,那么会出现骨干区域被“分裂”的情况,很明显骨干区域是不能被分割开的,出现这种状况的时候可能会影响到整个自制系统 ...
- OracleCommand.CommandText 无效
OracleCommand insertADataCmd = conn.CreateCommand(); insertBDataCmd.CommandText = SQL OracleParamete ...
- 30几个HTML5经典动画应用回顾 让你大饱眼福
周末大放送,让我们来回顾一下HTML5经典动画应用,一定会让你大饱眼福. 1.HTML5 Canvas画板画图工具 可定义笔刷和画布 HTML5 Canvas还有一个比较实用的应用,那就是网络画板,这 ...
- HDU 3853LOOPS(简单概率DP)
HDU 3853 LOOPS 题目大意是说人现在在1,1,需要走到N,N,每次有p1的可能在元位置不变,p2的可能走到右边一格,有p3的可能走到下面一格,问从起点走到终点的期望值 这是弱菜做的第 ...
- hdoj 5288 OO’s Sequence
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5288 //*************头文件区************* #include<ios ...
- 实现带查询功能的Combox控件
前言 ComBox 还可以实现查询功能,通过设置 ComBox 控件的 AutoCompleteSource 属性和 AutoCompleteMode 属性,可以实现从 Combox 控件中查询已存在 ...
- Oracle用户、权限、角色管理
Oracle 权限设置一.权限分类:系统权限:系统规定用户使用数据库的权限.(系统权限是对用户而言). 实体权限:某种权限用户对其它用户的表或视图的存取权限.(是针对表或视图而言的). 二.系统权 ...