Introductionto3DGameProgrammingwithDirectx12系列小结

Introductionto3DGameProgrammingwithDirectx12系列⼩结
  Introduction to 3D Game Programming with DirectX系列书箱对DirectX技术的学习推⼴起到了⾮常⼤的作⽤,从原理到实践、从数学到物理、从渲染管线到常见技术思路都进⾏了⽐较深⼊的讲解,是难得的DirectX学习资料。DirectX12是Directx系列API的最新迭代,其出现代表了微软在图形硬件处理上的最新成果,也在⼀定程度上代表了未来图形处理的⽅向。
新时代的起点
  本系列是以《Introduction to 3D Game Programming with DirectX12》作为学习蓝本,也是以《Introduction to 3D Game Programming with DirectX12》课后练习题作为主线的系统归纳技术资料。也是国内第⼀次公开对DirectX12进⾏讨论的系列资料。在写作本系列资料过程中,国内中⽂有价值的参考资料⼏乎没有,国外对Directx12的讨论也是刚刚起步萌芽,由于DirectX12体系架构的巨⼤变化,在碰到问题时可供参考的资料、代码极其有限,很多问题都是在摸索中前进,在前进中跌倒,在跌倒中爬起,在爬起后想办法解决,整个过程还是略为艰⾟。
⼀、DirectX12的性能优先设计思路
  在学习完本书后,我们再反过头来看DirectX12,⼀种豁然开朗的感觉,似乎突然明⽩了微软的良苦⽤⼼。之前我们也提到
过,Directx12最重要的变化就是其更接近底层硬件,Directx12在贴近硬件的路上⾛得很深⼊,因此能更加有效的发挥GPU、CPU的性能,原⽣的命令队列设计可以更加有效的利⽤多核CPU,资源绑定⽅式的变化带来了校验⽅式的变⾰,GPU与CPU的异步设计能更多的释放双⽅的性能潜⼒。因此来说,Directx12的设计确实是符合了多CPU核⼼时代发展的要求,能显著利⽤多核⼼的能⼒。
  现在可以看到,在整个DirectX12的设计过程中,“性能优先”是贯穿了整个DirectX12的研发过程,每⼀处的变动、每⼀处的改进都把这⼀理念体现的淋漓尽致,没有多余的设计,没有化繁为简的操作,⼀切均是向性能看齐。
(⼀)、D3D12 命令队列/列表
  D3D12 命令队列负责缓冲渲染命令,然后这些内置到由驱动程序已知且最后由命令队列执⾏的硬件命令。由于每个命令队列都可以在没有任何中间锁保护的情况下独⽴填写渲染命令,所以它的运⾏速度要⽐ D3D11 中的对应物快得多。
  命令队列可反复重新使⽤。当应⽤要重新提交相同的命令队列来执⾏ GPU 时,它必须等待 GPU 上的命令队列完成执⾏;否则,其⾏为是未定义的。⽆论命令队列是否仍然在 GPU 上执⾏,应⽤在关闭后还可以重置命令队列。已重置的命令队列不再保留任何以往的渲染状态,所以需要再次设置,如 PSO、视区、ScissorRect、RTV 和 DSV。
  在⼀般情况下,为了避免每个帧同步等待命令队在前⼀帧中执⾏,我们可以准备很多备⽤命令队列,然后在每帧结束时查看之前待定的命令队列。如果最近的命令队列已执⾏,则表⽰之前的命令队列也已执⾏,这是因为命令队列严格按顺序执⾏,相当于⼀个 FIFO 队列。为了确定某个命令队列是否已执⾏,我们需要使⽤围栏(Fence)的对象。当命令队列调⽤ ExecuteCommandList 函数时,通过将传递给信号函数的预期值设置成围栏对象,信号函数允许系统⽴即通知围栏对象何时执⾏了命令队列。因此在正常情况下,我们将各帧的累计帧号⽤作预期值,并将它传递给信号函数。查询命令队列命令队列是否完成的⽅式决定了 GetCompletedValue 的返回值是等于还是⼤于预期值。
  命令队列(Command Queue)是线程独⽴的,⼀个命令队列可以由多个线程执⾏,命令列表(Command List)是线程不独⽴的,⼀个命令列表只由⼀个线程执⾏。⼀个命令队列中可以放置多个命令列表,换句话说,这种设计的意义在于从源头上可以把任务分配到不同的GPU和CPU上,这是对之前DirectX的⼀个巨⼤的改进。
(⼆)、资源绑定
  在了解资源绑定之前,我们必须先了解⼀个核⼼概念,即根签名。就资源绑定模型⽽⾔,DirectX12 和 DirectX11 之间存在巨⼤的差异。DirectX11中的资源绑定是固定的。运⾏时间为每个着⾊器安排⼀定量的资源槽孔,并且该应⽤只需调⽤相应的接⼝,以便能够将资源绑定到着⾊器。在DirectX12中,
资源绑定过程⾮常灵活,并且不限制绑定资源的⽅式以及所绑定资源的数量,可以随意设置资源绑定风格。绑定资源最常⽤的⽅法是描述符表和根描述符。描述符表⽅法有点复杂,这是因为它提前将⼀组资源的描述符放在⼀个描述符堆中,这样,当绘制调⽤需要引⽤这些资源时,需对其进⾏初步处理即可。着⾊器会根据此项处理发现所有后续的描述符。这是⼀种指针类数组,这意味着着⾊器需要做第⼆个寻址以定位最终的资源。⽽根描述符的优势在于,不是提前将描述符放在描述符堆中,⽽是将资源的 GPU 地址设置到命令列表中,这就相当于在命令列表中动态地构建⼀个描述符,这样,只需⼀个寻址操作,着⾊器就可以定位资源。然⽽,根描述符消耗的参数空间是描述符表的两倍。由于根签名的最⼤尺⼨有限,所以根描述符和描述符表之间的合理⽐例安排⾮常重要。
  在正常情况下,我们把 SRV 和 UAV 放在描述符表中(⽽采样器只可存在于描述符表中),但将 CBV 放在根描述符中。因为 CBV
消耗的⼤部分资源是动态的,它的地址变化频繁,所以使⽤描述符表会导致组合激增。不仅内存的占⽤量会急剧增加,⽽且管理起来也很⿇烦。相⽐之下,采样器、SRV 和 UAV 组合的变化也⽐ CBV 少得多,尤其是采样器。只要上层渲染逻辑设计正确,采样器组合的数量可以⼩于 128。因此,直接将其放到描述符堆中更合适。此处,为了重复利⽤描述符堆中的描述符组合,我们必须使⽤ PSO 类对象管理技术,以便⾸先对每个采样器、SRV 和 UAV 编号,然后根据着⾊器的需求,将其放在⼀起,并⽣成⽤于创建和索引描述符堆中的描述符组合的唯⼀散列值。由于在着⾊器中使⽤的采样器的最⼤
ixos
数量是 16,所以,每个采样器组合可被放置在每单元 16 的跨度。SRV 和 UAV 也可以使⽤采样器的⽅法进⾏管理,所以,对其进⾏引⽤的着⾊器的上限最好也是 16。当然,可变组合跨度单元也是⼀种选择,但是不⽅便跨框架对其重复利⽤,这是因为由 SRV 指向的纹理被释放时,它的序号将被该应⽤回收,并且所有引⽤它的描述符组合将被标记为“删除”。此时,如果描述符堆中的组合块在⼤⼩⽅⾯出现变化并且不连续,则它们会难以重新分配到内存池碎⽚,除⾮花费⼤量的时间进⾏反碎⽚努⼒。出于这个原因,对于描述符组合,折衷的解决⽅法是使⽤固定长度的跨度。
  在命令列表中最多只能设置两个描述符堆,每类描述符堆分别⼀个。“采样器”以及“SRV / UAV / CBV”属于两类不同的描述符堆,并且不能进⾏混合。
  当我们需要重写描述符堆的描述符时,我们可以先在 CPU 可见的描述符堆中完成更新,然后通过 CopyDescriptors* 命令将堆的内容复制到 GPU 可见的描述符堆。这种资源绑定⽅式⽐DirectX11要⾼效得多,特别是使⽤根描述符,在编译阶段就可以对资源绑定情况进⾏检查,这样在运⾏时可以不⽤再频繁的进⾏资源检查,得以⼤辐提升性能。
(三)、资源屏障
包场中学
  这是⼀个新概念。在 DirectX12 之前,资源状态管理由驱动程序完成。现在,DirectX12 将其从驱动程序层剥离出来,并让应⽤控制何时以及如何处理它。
  存在三类不同的资源屏障。最常见的类型是转换,主要⽤来切换资源的状态。当资源的应⽤场景发⽣变化时,我们将先放置相应的资源壁垒,然后再使⽤该资源。
提问题
  在实践中,⾮常常见的转换屏障是在 ShaderResourceView 和 RenderTargetView 之间来回切换的资源。因此,我们需要在资源的包装类中添加⼀个成员变量来记录当前的资源状态。当上层逻辑调⽤ OMSetRenderTargets 时,我们应该⾸先确认当前的状态是否为RenderTargetView,如果不是则放置⼀个屏障。其 StateBefore 填充了存储在成员变量中的状态值,⽽其 StateAfter 则填充了RenderTargetView 状态。如果渲染逻辑调⽤ XXSetShaderResources,那么,我们继续按照上述流程执⾏类似的操作,除了StateAfter 应填⼊ ShaderResourceView 状态。
  资源屏障的引⼊,最⼤的优势在于其由应⽤去控制资源⽽不是由驱动来管理资源,因为由驱动来管理资源,驱动需要跟踪资源的状态,从⽽带来不必要的同步,因为它会阻⽌后续命令的执⾏,这会极⼤的消耗性能,⽽现在由应⽤来管理,应⽤知道管理的状态,也知道何时转换状态,这对硬件性能利⽤上起来⼀个⾮常有效的帮助。
(四)、管线状态对象
  管线状态对象是 DirectX12 的核⼼概念。它包含 Shader、RasterizerState、BlendState、DepthStencilState、InputLayout 和其他数据。⼀旦 PSO 对象被传送到系统,这些与 PSO 相关联的
状态将在同⼀时间进⾏设置。以前在 DirectX11 的接⼝层,这些渲染参数使⽤不同的 API 进⾏设置。为了完成适应阶段,我们必须使⽤可查询的运⾏时间容器对其进⾏管理。最常见的对象容器是 HashMap,它可⽤于避免多余的 PSO 和对应的 API 调⽤。在使⽤ HashMap 之前,我们必须先准备资源 ID,第⼀件事就是资源的内存地址。它在整个应⽤⽣命周期具有全局唯⼀性,但它存在⼀个缺点,就是占⽤了太多的内存空间:尤其是 64 位系统上的 8 个字节。实证分析表明,⼤多数应⽤不使⽤如此数额巨⼤的对象,因此,我们可以采⽤序号⽅式减少表⽰资源对象的空间,也就是说,使⽤⼀个单调递增的整数值来表⽰⼀个资源对象。相同的整数还可以表⽰不同的资源,只要这些资源是不同类型的。例如,RasterizerState 和 BlendState 可以使⽤不同的资源计数器。这种管理⽅法的⼀个重要优势是,它让资源的编码空间更紧凑且易于⽣成较短的散列值。否则,如果使⽤拼接内存地址后⽣成的散列值,则由散列值占⽤的内存字节数将很⼤,这不仅会影响 PSO 的存储,⽽且也会影响查询速度。需要在实践中到为计数器定义的上限。不同的项⽬可能存在很⼤的差异,但我们可以先在测试中使⽤⼀个较⼤值,并在分配序号的位置添加断⾔。⼀旦它超过上限,则系统会触发警报。
  为了进⼀步减少 PSO 实例的数量,当⽣成 RasterizerState、BlendState 和 DepthStencilState 时,我们需要观察它们之间的状态依赖性。例如,当我们禁⽤ DepthStencilState 中的深度测试时,可以忽略 RasterizerState 中的深度偏移设置。为了避免产⽣多余的对象,在这些情况下,我们使⽤默认值。
  RTV 和 DSV 也与 PSO 相关。由于 DSV 可以控制是否读取和写⼊深度图中的“深度”或“模版”,所以当启⽤深度测试且禁⽤深度写⼊时,需要在系统中设置只读 DSV。DSV 存在三种只读模式:1) 深度只读 2) 模板只读 3) 深度及模板只读。此外,PSO 还需要 RTV 和 DSV 的格式信息,所以,最好将 OMSetRenderTargets 操作推迟到设置 PSO 的时间。
  ScissorEnable 属性已从 RasterizerState 删除。在硬件端,Scissor 测试将处于永远在线状态。因此,如果应⽤需要禁⽤ Scissor 测试,则应设置 ScissorRect 的宽度和⾼度,以匹配视区或设置为硬件所允许的最⼤分辨率,如 16K。primative主要拓扑类型需要在PSO 中设置,其中包括点、线、三⾓形和补丁。当调⽤ IASetPrimitiveTopology 直接将其转换为上述主要拓扑类型时,我们可以使⽤预建的转换表。PSO 的 HashMap 也可根据主要拓扑类型进⾏分类,其中,每种拓扑类型对应⼀个 HashMap,这样,它就将使⽤数组下标直接定位。
  管线状态对象的⼀⼤优势是其可以在初始化阶段设置。⼀个管线状态对象设置完,则在同⼀时间载⼊到GPU中去以便通知GPU做好相应准备,⽽不是像以前DirectX11中使⽤的分离设置,GPU需要等待到所有状态设置完再进⾏状态更新。
2011安徽高考语文  应该来说,DirectX12整个设计过程中⽆不贯穿这种“性能优先”的设计理念,包括GPU与CPU同步、架构的重建、资源绑定、根描述符、PSO使⽤、命令队列等等等,这种“性能优先”设计带来⾼⼤的改
变就是GPU/CPU利⽤率的提⾼,这在当今CPU/GPU多核化,追求模型逼真,追求效果震撼的时代,这种设计⽆疑是正确⽽且是具有前瞻性的。i
⼆、Introduction to 3D Game Programming with Diectx12⼀书的成功之处
  Introduction to 3D Game Programming with Diectx12是luna写的DirectX学习资料系列的最新版本,就本书⽽⾔,很多章节还是在原章节上改的,但因为DirectX12与DirectX11的巨⼤差异,代码基本都进⾏了更新重写。应该说本书作者在对DirectX渲染管线的理解上还是很深⼊的,同时,作者语⾔表达能⼒也很好,能把⽐较深奥的内容⽤通俗易懂的⽅式加以描述⽽不是故弄⽞虚⽤专业术语罗列。在本书的编排上,先进⾏基本的数学知识介绍,为⽅便以门,然后就技术点对DirectX12进⾏分块讨论,最后再对⼀些常⽤技能或应⽤进⾏讲解。符合学习的⼀般逻辑,也有很强的系统性。从原理到实践、从数学到物理、从渲染管线到常见技术思路都进⾏了⽐较深⼊的讲解,是难得的DirectX学习资料。建议希望了解图形渲染底层技术的读者学习了解⼀下。
三、对未来⾏业的影响
  从国内的⾏业特点来说,我们研究底层技术的真的不算太多,使⽤软件、依赖插件的思想真的很严重,不能说这个⼀定是坏事,但从长远来看,这种功利思想显得极为短视。这样做的结果是跟着跑但永远也跑不到别⼈前⾯去。
  近期,微软对DirectX12进⾏了⼀项⼤的更新DXR,DXR技术要在当前的GPU硬件上实时运⾏,则需要有性能⾮常良好的底层架构,这在之前可能是没法想象的。
  从⽬前现状来看,出现⼀个有意思的现象,⽬前绝⼤部分⽤户的电脑都采⽤了多核⼼CPU/GPU,但之前的图形硬件API不能充分利⽤多核⼼硬件资源,虽然⽤户计算资源充裕,但渲染性能却⼀直提不上去。因此,⽆论是AMD,NVIDIA,微软,OPENGL,⼤家都在想办法提升对多核资源的利⽤效能。从⾏业发展的⾓度来看,旧版Directx API已经不能堪当⼤任了,Directx12或者在DirectX12基础上发展起来的新型图形硬件API必将取代之,因为它代表着未来!
参考⽂献
1、将应⽤迁移到 DirectX12

本文发布于:2024-09-21 18:59:12,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/14629.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:资源   描述符   命令   队列   设置   状态   需要
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议