深度学习读取数据很慢_程序媛转TA之番外篇五:DX11的学习记录

本文探讨了DX11中资源绑定、管线阶段、延迟执行上下文、DXGI与D3D11的区别,以及输入装配、缓冲区管理、渲染技术(technique)和pass的使用。对比了DX11与DX12在内存管理、同步与异步操作上的差异,有助于理解两个API的性能优化策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

01b5580a7b8e780468d13ff13dce9557.png

学习DX12的时候,其实会有一些细节上的疑问,而这些问题需要通过学习DX11来更好的理解。所以就单独再写一篇DX11的,对里面一些重要的部分记录以下。

参考资料是一个物理老师的DX11龙书翻译笔记,地址是:

图形编程​enjoyphysics.cn
09e6f8208bfa6e84454cb0aa34a133b4.png

1.资源是没法直接绑定到一个管线阶段的,只能把资源关联的资源视图绑定到不同的管线阶段。文档是这么说的:运行时环境与驱动程序可以在视图创建执行相应的验证和映射,减少绑定时的类型检查。

2.Device接口用于检测显示适配器功能和分配资源。DeviceContext接口用于设置管线状态,将资源绑定到图形管线和生成渲染命令。

3.除了在主线程中立即执行上下文,还可以在工作线程中使用延迟执行上下文。每个工作线程可以将图形指令记录在一个命令列表中,然后每个工作线程中的命令列表可以在主渲染线程中加以执行。

4.DXGI时独立于D3D的API,用于处理和图形关联的东西,例如交换链等。这样的目的时因为其他图形API例如D2D也需要交换链等,通过这种设计,多个图形API都能使用DXGI API.

5.D3D11_USAGE枚举有四个值,USAGE_DEFAULT,表示GPU回对资源执行读写操作,CPU不能读写这种资源。对应于默认堆。IMMUTABLE,表示在创建资源后,资源中的内容不会改变,这样可以获得一些内部优化,因为GPU会以只读的方式访问这种资源。对除了在创建资源的时候CPU会写入初始化数据外,其他任何时候CPU都不会对这种资源执行任何读写操作。应于创建的时候放到上传堆,然后复制到默认堆中。DYNAMIC,表示CPU会频繁更新资源中的数据内容。GPU可以从这种资源中读取数据,而CPU可以向这种资源中写入数据。这个对应于上传堆,然后GPU直接从上传堆中读取数据。STAGING,表示CPU会读取该资源的一个副本,该资源支持从显存到系统内存的数据复制操作,对应于回读堆。

DX11的渲染管线的基本阶段:

10ec795d9d4865724cfcd42ea45b4614.png

6.在输入装配阶段,需要描述顶点结构的分量结构,D3D11_INPUT_ELEMENT_DESC用来填充这些信息。里面需要指定元素位于哪个输入槽,DX11支持16个输入槽。例如,当一个顶点由位置和颜色组成的时候,我们可以使用一个输入槽传入两种元素,也可以将两种元素分开,使用第一个输入槽传送顶点元素,使用第二个输入槽传送颜色元素。

10d00c3979a3e207bb35f62bc3b493b3.png

7.为了让GPU访问顶点数组,必须把它放在一个称为缓冲的特殊资源容器中。首先需要创建一个描述创建缓冲区的结构体,然后填写一个SUBRESOURCE_DATA的结构体,为缓冲指定初始化数据,然后CreateBuffer方法来创建缓冲区。这个时候,我们可以使用不同的USAGE,类似上面创建资源时候的USAGE:

DEFAULT,对应于默认堆,GPU对资源执行读写,使用Map映射后,CPU不能读写这种资源,但可以使用UpdateSubresource方法。

IMMUTABLE,创建资源后,资源内容不会改变,也无法用Map映射。

DYNAMIC,表示CPU会频繁更新资源中的数据内容。GPU可以从这种资源中读取数据,使用MAP映射时,CPU可以向这种资源写入数据。因为新的数据要从CPU内存传送到GPU显存,所有会有性能损失,对应于上传堆。

STAGING:表示CPU会读取该资源的一个副本。显存到系统内存的复制是一个缓慢操作,尽量避免。可以使用CopyResource和CopySubresourceRegion方法复制资源。

8.一个technique由一个或者多个pass组成,用于创建一个渲染技术。每个pass实现一种不同的几何体渲染方式,按照某些方式将pass的渲染结果混合在一起就可以得到我们想要的渲染结果。一个pass由一个顶点着色器,一个可选的几何着色器,一个像素着色器和一些渲染状态组成。像素着色器是可选的,例如只想绘制深度缓冲,不像绘制后台缓冲。 techniques可以组成一起称为effect组。你可以通过D3D11EffectVariable指针修改变量,但这些只是副本,你需要调用Apply方法来更新GPU内存。

9.Map函数可以获取缓冲区内存起使地址指针,并写入数据。它的参数:

efb7058caaa1019e22d4be31afb25c3d.png

pResource:指向要访问的用于读/写的资源的指针。

Subresource:包含在资源中的子资源的索引。

MapType:

203f3ca885614b366ffc369771091826.png

pMappedResource:返回一个指向资源的指针,这样就可以读写资源数据。

a0793a4f5b9c4f07a6c1a7459837f37d.png

当你完成缓冲区的操作后,必须调用Ummap函数。

上面基本就是DX11的一些要点,然后我们和DX12对应起来看。

首先我们只讨论由独立显存的架构。具体细节可以看一下龚大的这个回答:

资源从内存上传到显存时,在 DX11 中都对应哪些过程?​www.zhihu.com

1.对于只需要建立资源的时候创建的数据,以后不会改动,我们可以认为是同步的。在运行时创建一个资源,指定类型IMMUTABLE,底层会进行map->memcpy->unmap操作。因为map的时候资源还没有创建,所以不存在资源正在使用的复杂情况,所以性能好。在DX12中可以把创建好的数据放到上传堆,然后通过上传堆拷贝到默认堆实现类似的操作。

2.显式的通过Map->memcpy->Unmap来传递资源。也可以认为是同步的,在map的时候,资源可能正在被使用,所以如果没有NO_WAIT标志,流水线就会被block,等待资源可用才完成Map。一般都是从上传堆或者回读堆进行映射,见的比较多的是顶点数据,用Map上传到上传堆里面,然后Ummap。然后GPU会直接访问上传堆的数据。当然把顶点数据拷贝到默认堆也是一种选择,看你的具体需求了。

3.UpdateSubresource。这是一个接口,是异步的。驱动会在内部开辟一块临时空间,把数据拷进去,等待资源不被占用的时候进行map->memcpy->unmap。对于默认堆,这个可以把上传堆中的数据传到默认堆中。

然后看下不同的类型:

staging:它是一块线性的系统内存区域,map就是把它的指针给你。但每次给的地址可能会不同。所以我们使用map进行memcpy就是直接操作这个内存。然后调用接口可以处理这些数据。DX12中回读堆的话对于GPU是只有复制权限,所以有时候需要用自定义堆来获取需要的权限。

71d85fa522cb2899d362d9d09299cc26.png

4652a2952050414ab755b43727f959f3.png

dynamic:在runtime里会建立2个资源,一个sys mem,一个gpu mem,map的时候给你sys mem,在unmap的时候把新的数据同步到gpu mem。这个其实就是DX12的上传堆,但GPU访问的时候会访问gpu里面对应的一块内存。ummap的时候数据会同步。不过DX12里面,可以支持永远不ummap。首先DX12并不推荐从上传堆中读取数据,虽然这是允许的,但性能会非常差,因为GPU写入的数据要传回到系统内存才行。同样,写入合并的内存读取也是低速的。回读堆在某些架构上必须使用Map和Ummap。但上传堆可以Map之后不Ummap。但是如果上传堆里的最后一个资源也被释放了,那么这个指针就不能再被使用。而且你必须保证CPU完成了写入数据到内存里面,这样GPU才能执行一个命令列表来读取这个内存。

default:默认堆,在gpu mem,不能再API级别Map/Ummap,只能通过接口拷贝过去。

所以,这个过程就是,
app->map (runtime)->map(user mode driver)->address->memcpy->unmap (runtime)->unmap (user mode driver)->copyresource (runtime)->copyresource(user mode driver)->map(kernel mode driver)->memcpy to gpu mem (user mode driver)->unmap(kernel mode driver)。
还有一种特殊情况,可以先把2放入staging资源,然后在用copyresource放入default资源。如果目标资源正在使用,就会让copyresource变成异步,从而下一次map就会阻塞。可以用ringbuffer的方式使用,手动弄成异步拷贝。

感觉DX12里面分的更加清晰明确,但DX11其实可以有不少混用的地方,可以根据需要进行适当调整。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值