Direct3D 是底层图形应用程序编程接口。我们可以通过它对GPU进行控制和编程,从而在渲染虚拟3D场景时获得硬件加速的效果。12 比 11 更加偏于底层,更能压榨硬件性能。从今天开始我们就会开始进行DX12龙书的学习,主要还是为了能够熟悉它的接口,并可以使用它进行编程,这样为我们未来的渲染器打好基础。

首先我们来说说DX12中一些特性(相对于Vulkan或OpenGL中有或没有的一些概念)。

一、组件对象模型(Component Object Model, COM)

COM的全称是Component Object Model,它让DX称为独立的编程语言并且向下兼容。我们通常使用特定的函数或者其它COM接口来获取COM接口引用的指针。另外,COM对接口是引用计数的,使用完毕后,需要调用Release方法来释放它们。为了管理COM对象的生命周期,Windows Runtime Library提供了wrl.h来管理COM对象的智能指针。龙书中主要用到了ComPtr三个方法:

  • Get: 返回其包含的COM接口。
  • GetAddressOf: 返回其包含的COM接口的指针指向的地址。
  • Reset: 将ComPtr接口设置为空指针,并且减少其包含的COM接口的引用计数。

例如在Office文档中,可以放入一个视频,然后这个东西就会显示成一张静态的图片。我们双击这个图片,就会启用支持这个的软件对其进行播放。然后后台程序会启动,但窗口被隐藏。Office当中的一小块客户区域传递给这个后台程序,让其负责处理这块区域的绘制和用户输入。也就是说,Office程序的WM_PAINT事件处理当中,将Office窗口的整个客户区域分割为由自己绘制和由OLE绘制的部分。由OLE绘制的部分通过COM技术传递给后台应用进行绘制。

二、纹理格式

组成纹理的数据元素必须是 DXGI_FORMAT 枚举类型中定义的特定格式。例如:

DXGI_FORMAT_R32G32B32_FLOAT: 每个元素由 3 个 32 位浮点数分量构成
DXGI_FORMAT_R8G8B8A8_SNORM: 每个元素由 4 个 8 位无符号分量构成
DXGI_FORMAT_R32G32_UINT 每个元素由 2 个 32 位无符号整数构成
...

纹理是渲染时信息的主要储存介质,而不仅限于存储图像数据。

三、交换链

每一帧图像的绘制都需要一个过程,为了使这个过程对于使用者不可见,我们需要先将内容全部绘制到后台缓冲区的纹理中。在一帧的内容全部绘制完成时,前后台缓冲区对调,新的前台缓冲区为用户呈现内容,新的后台缓冲区开始绘制下一帧。

前后台缓冲区构成交换链(IDXGISwapChain)。使用两个缓冲区的叫做双缓冲。而且可以使用更多的缓冲区,例如使用 3 个缓冲区的叫三重缓冲。

四、深度缓冲

深度缓冲区是一种储存特定像素深度信息的缓冲区。其值为 0.0~1.0 之间。0.0 表示平截头体中最近能看到的物体,1.0 表示平截头体最远能看到的物体。

深度测试用于对比写入后台缓冲区的同一像素的不同深度值。较小的值(物体靠前)会最终写入后台缓冲区。

深度缓冲区也是一种纹理,所以在 DXGI_FORMAT 枚举类型中也定义了它能使用的格式:

DXGI_FORMAT_D32_FLOAT_S8X24_UINT 32位深度缓冲 + 8 位无符号模板缓冲 + 24 位对齐
DXGI_FORMAT_D32_FLOAT
DXGI_FORMAT_D24_UNORM_S8_UINT
DXGI_FORMAT_D16_UNORM

五、资源与描述符

GPU想要对资源进行读写,就需要将该资源先绑定的渲染管线上。GPU在使用资源的时候大致需要知道两件事:

  • 要使用的是哪个资源?
  • 该资源充当什么角色?

资源描述符主要解决上述两件事。通过资源描述符引用要使用的资源很好理解。主要是资源描述符还需要解释资源充当的角色。因此我们可以把资源描述符分类,其中常用的有:

  • CBV/SRV/UAV 常量缓冲区视图(constant buffer view)/ 着色器资源视图(shader resource
    view)/ 无序访问视图(unordered access view)
  • 采样器(sampler)用于纹理贴图
  • RTV 渲染目标视图(render target view)
  • DSV 深度/模板视图(depth/stencil view)

描述符堆表示特定类型描述符的一块内存

六、多重采样技术

书中主要介绍了两种方式:

  • 超级采样(Super Sample Anti-Aliasing,SSAA)
  • 多重采样(MultiSample Anti-Aliasing,MSAA)

超级采样使用四倍于屏幕分辨率的后台缓冲区和深度缓冲区,然后在降采样的时候以四个像素为一组计算平均值。

采用4X多重采样,也是需要四倍于屏幕分辨率的后台缓冲区和深度缓冲区,但在降采样的时候直接计算像素中心的颜色,然后根据可视性和覆盖性将信息分享给子像素。

相当于超级采样每个子像素都要算一遍。而多重采样是每个像素算一遍然后把结果按条件分给子像素。

子像素是指在像素内部再次细分出用于采样的子像素。在上述条件下,四倍于屏幕分辨率,则屏幕的每个像素下就有4个用户采用的子像素。

七、命令队列&命令列表&命令分配器

每个GPU都至少维护一个命令队列(command queue)。CPU可以通过命令列表(command list)将命令提交到GPU的命令队列中。

命令分配器保存由CPU通过命令列表添加的命令,在提交到命令队列中时,队列也会引用分配器中的命令。

命令提交到命令队列中时并不会立即执行。而是添加到执行列表排号。

命令列表和分配器不是线程安全的,不过命令队列是线程安全的。所以可以在多个线程分别创建命令列表和分配器,而在提交命令时使用相同的命令队列。

八、CPU与GPU的同步问题

由于两个处理器并行工作,就必然会遇到需要同步的情况。此时我们需要运用围栏点(fence point)功能。

围栏点的逻辑简单来讲就是 CPU 向 GPU 执行路径上添加一个围栏点,然后 CPU 可以在需要和 GPU 同步的时候使用该围栏点的值等待 GPU。如果 GPU 执行到该围栏点值处会触发事件,从而达到同步的目的。

九、资源驻留

Direct3D 12 可以主动管理资源在显存中的驻留情况。可以将短时间不用的资源清出以节省缓存。

十、资源转换屏障(transition resource barrier)

假如我们要对某个资源进行先写后读两种操作。但是如果 GPU 在写还没开始时或未完成时就开始读资源明显就会出问题(资源冒险 resource hazard)。为了解决这个问题,Direct3D 引入了一种状态机制。在操作资源的时候更改资源的状态。GPU 会根据资源状态来调整行为避免资源冒险的产生。

十一、多线程

11.1 D3D11多线程渲染基本原理

在D3D11中,对于多线程渲染的支持主要可以概括为几个“小点”:
首先利用ID3D11Device::CreateDeferredContext方法创建一个(或多个)延迟渲染(Deferred Device Context)的设备上下文接口ID3D11DeviceContext;
接着利用这个Deferred Device Context接口调用诸如:IASetPrimitiveTopology、IASetInputLayout、RSSetState、OMSetBlendState 、OMSetDepthStencilState、DrawIndexed、DrawIndexedInstanced(最后两个为Draw Call)等等方法(可以统称为命令)像往常一样进行渲染调用;
所有的渲染调用都结束后,接着调用ID3D11DeviceContext::FinishCommandList方法,得到一个ID3D11CommandList接口;
最终通过即时设备接口(Immediate Device Context)的ID3D11DeviceContext::ExecuteCommandList方法执行这个Command List;
当所有的Command List都Execute完成之后,就可以调用IDXGISwapChain::Present方法呈现最终渲染画面了。
其中最最吸引人的就是Deferred Device Context的ID3D11DeviceContext接口可以有多个,并且每一个可以在不同的CPU线程(Windows线程)中分别记录命令(Command),然后提交给Immediate Device Context所对应的CPU线程(Windows线程)进行Execute(MSDN中称之为Queues commands),然后也在同样的线程中调用Present即可。这也就是D3D11多线程渲染的全部核心奥秘了。
当然这不是全貌,只是一个示意性的核心原理说明,但至少说明使用D3D11加入多线程渲染在编程原理和具体实现上其实并不复杂。在D3D11中命令列表中的命令(其实主要是CPU发送给GPU执行的命令)是被快速记录下来,而不是立即执行的(包括那些可怕的被称之为Draw Call的性能杀手,当然一定要记得升级你的显卡驱动程序),直到你调用ExecuteCommandList方法(调用即返回,不等待)才被GPU真正的执行,此时那些使用延迟渲染设备接口的CPU线程以及主渲染线程(不一定是进程的主线程,此处是指调用Immediate Device Context::ExecuteCommandList方法的线程)又可以去干别的事情了,比如继续下一帧的输入变换、碰撞检测、物理变换、动画矩阵调色板准备、光照准备等等,从而为记录形成新的命令列表做准备。而此时GPU就忙碌的开始执行渲染命令了。这也就是延迟渲染设备名字的真正含义。最终这就形成了CPU和GPU同时都在忙碌的高效渲染效果。
而在拥有D3D11多线程渲染之前,CPU和GPU的工作就好像两个人打台球一样,一个击球时,另一个只能在旁边观望(CPU线程在Draw Call上等待GPU完成渲染)。而D3D11引入的多线程渲染,不但让CPU和GPU可以同时处于忙碌状态,更让现代多核CPU及多GPU并行执行任务的能力得到根本上的解放。由此也可以看出来多线程渲染也是为解放生产力而生!

11.2 D3D12多线程渲染基本原理

而在D3D12中多线程渲染与D3D11中是异曲同工的:
首先在D3D12中利用命令队列(Command Queue 接口:ID3D12CommandQueue)代替了ID3D11DeviceContext接口,更准确的说是代替了Immediate Device Context的ID3D11DeviceContext接口,在D3D12中不论什么队列(Queues)都需要自己创建,而不是像D3D11中Immediate Device Context伴随ID3D11Device一同被创建。
在D3D12中Command Queue被进一步细分为D3D12_COMMAND_LIST_TYPE_DIRECT(直接命令队列), D3D12_COMMAND_LIST_TYPE_BUNDLE(捆绑包), D3D12_COMMAND_LIST_TYPE_COMPUTE(计算命令队列),D3D12_COMMAND_LIST_TYPE_COPY(复制命令队列), D3D12_COMMAND_LIST_TYPE_VIDEO_DECODE(视频解码命令队列), D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS(视频处理命令队列)。
不论什么命令队列,它们本质上就是用来执行命令列表的,相当于D3D11中的Immediate Device Context,同样也是调用ExecuteCommandLists方法来执行命令列表。因此从其丰富的种类就可以感受到D3D12中命令队列本身就已经开始大大扩展了。
在D3D12中也有与D3D11中相类似的命令列表的概念,具体是用ID3D12GraphicsCommandList接口来表达,它就相当于D3D11中的Deferred Device Context。当然其内涵比在D3D11中要丰富的多,在D3D11中记录命令列表是由Deferred Device Context越庖代俎的,也就是我们按逻辑调用一堆ID3D11DeviceContext的方法,结束的时候使用FinishCommandList方法得到一个命令队列的接口ID3D11CommandList,而这个接口几乎没什么方法,仅仅是个“概念标志物”,或者直白的说就是仅仅代表GPU上的一个命令队列而已,其自身并没有什么方法可供调用。而在D3D12中ID3D12GraphicsCommandList却包含了几乎所有的可供放入命令队列中的命令方法(几乎全部是渲染相关的方法),并且在D3D12中命令队列本身是被创建的,使用的是ID3D12Device::CreateCommandList方法,有了这个接口以后你就可以在对应的其它CPU线程中按照渲染逻辑调用ID3D12GraphicsCommandList接口的方法生成一个命令列表(Command List)了,通常这个过程被称作“记录(或录制)”一个命令列表。同样录制只是说记录了你调用的顺序和使用的资源(CPU从内存传入显存),而不是立即执行这些方法,这些方法都会快速返回,因为是CPU调用这些方法,这个过程可以想象为你到餐馆去点菜,并不是你点一个菜,就做一个菜上一个菜再点下一个,而是生成一个菜单(Command Lists),统一提交到厨房(Queues Commands & Execute)。对应于不同的命令队列,命令列表也分为很多种类,基本上就是有多少种命令队列,就有多少种命令列表。
最后在命令列表(Command Lists)记录完成后,与D3D11中相同,提交到对应的命令队列(Command Queues)上去执行(ExecuteCommandLists)即可。
在命令队列执行命令列表的对应关系上,D3D12中基本的原则就是直接命令队列几乎可以执行所有种类的命令列表,而其它的命令队列只能执行对应种类的命令列表,如复制命令队列原则上只执行复制命令列表。
最终当所有的命令列表都执行结束后,主渲染线程(这时往往指的就是运行直接命令队列的CPU线程了)再调用Present方法将最终画面呈现出来即可。
需要注意的就是在D3D12中最终的Execute Command Lists操作也是立即返回,此时CPU线程可以进行其它的操作,而GPU就同时忙着执行各种渲染命令了,只有到所有都结束后才调用Present。 当然这很好理解,在所有渲染没有完成之前,后台缓冲区里还不是我们想要的最终画面。
由此可以看出,至少从原理上来说D3D12与D3D11多线程渲染框架基本是一致的。都是通过在不同的CPU线程中录制命令列表(Command Lists),最后再统一执行(Execute)的方式完成多线程渲染。并且都从根本上屏蔽了令人发指的Draw Call同步调用,而改为CPU和GPU完全异步执行的方式(并行!),从而在整体渲染效率和性能上获得巨大的提升。

DirectX12_基础知识相关推荐

  1. 嵌入式Linux的OTA更新,基础知识和实现

    嵌入式Linux的OTA更新,基础知识和实现 OTA updates for Embedded Linux, Fundamentals and implementation 更新的需要 一旦嵌入式Li ...

  2. 计算机基础知识第十讲,计算机文化基础(第十讲)学习笔记

    计算机文化基础(第十讲)学习笔记 采样和量化PictureElement Pixel(像素)(链接: 采样的实质就是要用多少点(这个点我们叫像素)来描述一张图像,比如,一幅420x570的图像,就表示 ...

  3. 嵌入式linux编程,嵌入式Linux学习笔记 - 嵌入式Linux基础知识和开发环境的构建_Linux编程_Linux公社-Linux系统门户网站...

    注:所有内容基于友善之臂Mini2440开发板 一.嵌入式Linux开发环境的构建 嵌入式开发一般分为三个步骤: 1.编译bootloader,烧到开发板 2.编译嵌入式Linux内核,烧到开发板 3 ...

  4. 《计算机网络应用基础》模拟试卷(六),《计算机与网络应用基础知识1》模拟试卷...

    <计算机与网络应用基础知识1>模拟试卷 (4页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 9.9 积分 <计算机与网络应用基础知识1& ...

  5. python向量计算库教程_NumPy库入门教程:基础知识总结

    原标题:NumPy库入门教程:基础知识总结 视学算法 | 作者 知乎专栏 | 来源 numpy可以说是 Python运用于人工智能和科学计算的一个重要基础,近段时间恰好学习了numpy,pandas, ...

  6. python常用变量名_python基础知识整理

    Python Python开发 Python语言 python基础知识整理 序言:本文简单介绍python基础知识的一些重要知识点,用于总结复习,每个知识点的具体用法会在后面的博客中一一补充程序: 一 ...

  7. 计算机基础知识掌握欠缺,《计算机基础知识》实验教学改革探讨.pdf

    <计算机基础知识>实验教学改革探讨.pdf Science& TechnologyVision 科 技 视 界 科技 探·索·争鸣 计<算机基础知识>实验教学改革探讨 ...

  8. python计算wav的语谱图_Python实现电脑录音(含音频基础知识讲解)

    前言 今天开始进入近期系列文章的第一篇,如何用 Python 来实现录音功能. 在开始"造轮子"之前,个人一直强调一个观点,如果有些东西已经有了,不妨直接去 github 上搜,用 ...

  9. 计算机wrod初级考试题及答案,计算机基础知识+Word基础知识+Excel基础知识试题答案解析.doc...

    文档介绍: 计算机基础知识+ Word基础知识+ Excel基础知识 第一部分 一.单项选择题 1.世界上第一台电子数字计算机取名为(    ). A.UNIVAC    B.EDSAC    C.E ...

  10. java 前端基础知识_【计算机·知识】关于前端的计算机基础知识

    原标题:[计算机·知识]关于前端的计算机基础知识 作为一个刚刚入门的程序猿,你是否对专业知识有足够的了解?今天新闻君带你走进前端的世界. 前端的语言接触起来相对于后端的语言要容易不少,但前端的语言也有 ...

最新文章

  1. 大数据调度平台Airflow(三):Airflow单机搭建
  2. Spring框架是怎么解决Bean之间的循环依赖的 (转)
  3. Linux纯脚本故障转移集群
  4. GB2312、GBK与UTF-8的区别
  5. 如何在debian 中启用 fbcon
  6. 强化学习(八)价值函数的近似表示与Deep Q-Learning
  7. 川普签署的 H-1B 禁令昨日正式实施,最着急的是谁?
  8. 两个比较好的java在线手册网站
  9. 数据结构与算法之BFPRT算法
  10. no.2_用绳子计时15分钟
  11. IterableThread
  12. UI登录表单使用模板素材
  13. linux服务器查看系统装到哪个盘,查看linux安装了什么服务器地址
  14. IIS的Server Application Error报错解决方法
  15. ShowDoc v2.4.8 发布,IT团队的在线 API 文档工具
  16. UTM坐标转GPS方法分享
  17. curl的安装与配置
  18. 同济版《线性代数》引争议,从清华改用MIT数学课程看中美数学教育差距!
  19. 原核DNA甲基化简述
  20. bat脚本 - 通过bat脚本一键启动[开机启动]日常应用

热门文章

  1. 【Anylogic智能体状态转移】
  2. 十、基于FPGA的PCIE协议介绍(二)
  3. [Matlab科学绘图] Matlab画图常用函数和命令
  4. 利用Think远程代码执行漏洞进行脱库上传免杀木马情报
  5. java winform 工具,重拾JAVA之WinForm实战之(六)
  6. java初学者练手项目_最适合初学者的Java练手项目!
  7. android 修复工具,牛学长安卓手机修复工具(安卓手机修复助手)V2.4.0.11 免费版
  8. 5.1声道定位测试音源(PCM-WAV)定位测试音源(PCM-WAV)
  9. 语音测试,串口和adb
  10. 平面设计版式构成实用技巧