Direct3D资源管理(Management Best Practices)

Direct3D资源管理(Management Best Practices)

 By Chuck Walbourn, Software Design Engineer

Advanced Technology Group (ATG)

June 2005

摘要

托管贴图(Managed textures),自动纹理管理,在DX6中被引入后,经过一系列的后续修改和增强,在DX9中自动资源管理类型支持:贴图,顶点缓冲,索引缓冲,所有这些资源使用统一的公共接口(shared interface)。通过使用D3D资源管理器,应用程序可以轻松的处理设备丢失、处理适度过量的显存使用。

有时,开发者在使用托管资源时会遇到困难,部分归咎于系统的抽象特性。许多一般情况下,使用托管资源是不错的选择,然而有些情况下使用非托管资源,性能更优。这篇文章将讨论一般情况下如何处理资源,托管与非托管资源的行为差别,以及涉及一些在运行时和驱动程序中的细节.

文章涉及内容如下:

l         显卡内存

l         托管资源

l         驱动管制资源

l         默认资源

l         系统内存资源

l         一般性的建议

显存(视频卡存储器,Video Memory)

显示系统为了利用资源,资源必须被加载到显存中,GPU能直接访问。GPU访问本地显存(Local video memory)是最高效的,并且某些资源(例如Render Targets,深度缓存、模板缓存)必须在本地显存中定位。由于AGP的出现,GPU可以直接访问部分系统内存.这部分系统内存区域,是指AGP区域内存容量(AGP aperture),就指非本地显存,这部分内存也不能派作他用。非本地显存可以被GPU访问(读/写),效率通常比直接访问本地显存低一些,因此它可以作为共享的内存资源来使用。需要明确的是,像本地显存一样,AGP内存在设备丢失时都会失效,都需要在恢复他们。

Figure 1.  Relationship of the GPU, CPU, video RAM, and system RAM

一些集成显卡方案使用统一内存构架UMA(Unified Memory Architecture), 任何设备都可以对主内存寻址Direct3D已经支持UMA,使用本地显存相同的标记来配置不需要对应用程序做任何修改。对于这样的系统,资源总是位于系统内存中,驱动确保:利用UMA特性和硬件专门实现,资源就像在传统的构架中进行工作(言下之意:UMA、硬件和驱动程序的行为,对于应用程序来说,都是透明的。)。

Figure 2.  UMA 构架中,GPU 和 CPU都可以访问系统内存

托管资源(Managed Resources)

大部分资源应该使用POOL_MANAGED(托管)方式创建。所有(托管)资源将被创建在系统内存中,在需要的时候拷贝到显存中。当发生设备丢失时,会自动从系统内存拷贝(到显存)。因为不会要求同时把所有管资源送入显存,提交的显存,可以超过渲染任何帧的集合。但是这样会使得大量后备系统内存,逐渐被分页到磁盘上(非常耗时),所以恢复丢失的显存(Restore the lost video memory),就要从磁盘上复制回数据,这也是为什么重置操作(Rest Operation)如此耗时的原因。

DirectX会为每份资源在最后一次使用时加上时间戳,为新加载托管资源分配显存失败时,LRU算法(最近最少使用的资源)根据时间戳,释放资源。使用SetPriority函数可以把资源标记为优于时间戳的判断,所以那些比较常用的资源应该设置高优先级。在Direct3D 9.0 中,驱动程序提供有限的托管显存信息,为保证运行时能满足新的分配,可能要清除好几个资源。可以通过设置合适的优先级来减少这样的情况:刚清除的资源又立刻访问。应用程序可以强制调用EvictManagedResources清除所有管资源,但是重新加载下一帧需要的资源,是非常耗时的,这功能对于场景切换(场景环境变化很大)和清理显存碎片,还是非常有用的。

    在运行时,通过计算该资源从使用到清除的间隔帧数,就可以知道是否超负荷了(thrashing,使用的资源比显存能承受的资源多 ) 。这就触发了资源管理策略从LRU切换到MRU,这个时候使用MRU资源调度方式(最长时间没有使用的资源)比LRU性能稍优。这样的超负荷对渲染性能影响巨大。注意,这里“当前帧”的概念是绑定于EndScene,所以任何使用托管资源的应用程序都常规地调用了这方法。

开发人员如果想得到关于管资源的更多信息,可以通过IDirect3DQuery9接口查询,但是这个接口仅能用于调试模式(debug runtimes),虽然在运行时提供了很详细的资源托管,应用程序不能依靠该接口。

了解资源管理器如何工作可以帮助我们调试、调整程序,重要的是应用程序不要太过依赖当前的运行库或者驱动程序的资源管理方式,驱动更新或者硬件实现的改变,可能导致其行为发生变化,将来的Direct3D版本将会有更先进和成熟优雅的资源管理方式。


驱动托管资源(Driver-Managed Resources)

Direct3D驱动可以任意地实现“驱动托管纹理”的特性,D3DCAPS2_CANMANAGERESOURCE标记硬件驱动是否支持这个特性,这样驱动将代替运行时库(Direct3D)来管理资源。对于级少数实现了这特性的硬件,驱动管理资源的行为不尽相同,你可以咨询你的产品提供商获得这方面信息。或者,你可是使用D3DCREATE_DISABLE_DRIVER_MANAGEMENT方式创建设备,确保由运行时库(Direct3D)来管理资源。(注:可以查看D3DCAPS2_CANMANAGERESOURCE,硬件是否支持托管,可以让驱动来管理官托管方式,也可以通过D3DCREATE_DISABLE_DRIVER_MANAGEMENT开启Direct3D的托管方式,来禁止驱动托管方式。)


缺省资源管理(非管资源)(Default Resources)

虽然管资源非常简单,容易使用,高效,但是有时我们希望直接使用显存。需要使用POOL_DEFAULT方式创建资源。使用这种方式会增加程序的复杂性,代码需要为POOL_DEFAULT创建的资源,应付设备丢失的情况,并且当把数据复制给它们时,需要谨慎考虑性能。错误的指定USAGE_WRITEONLY标记或者设置渲染目标(Render Target)可被 Lock ,都会严重影响性能。

对POOL_DEFAULT资源进行 Lock 操作,相对于对POOL_MANAGED资源,更可能导致GPU停顿,除非使用某些的指示标记。根据资源分布位置的不同,锁定后获得的指针,可能是一块临时的系统内存,也可能直接指向AGP内存。如果是临时的系统内存,Unlock后将把这段数据送入显存。如果显存资源不是只写的(write-only),Lock时数据必须传输到临时的内存。如果是AGP内存区域,避免了临时的拷贝但是由于对缓存的要求,导致降低性能

写入整个cache line的数据到AGP内存区,来避免write-combing性能损耗。应注意到,这样会引起read-write周期,所以 优先选择顺序访问内存区。如果在创建期,应用程序需要随机访问数据,而你又不希望对buffer使用托管资源,那么可以使用系统内存副本 。一旦数据创建后,可以把结果输入到锁定的资源内存中,避免了缓存write-combing操作的性能损耗。

(注:write-combing)

对于某些类型的资源,使用LOCK_NOOVERWRITE标记会使添加数据比较有效率,但是应该避免对同一资源多次Lock和Unlock调用。适当利用不同的锁定标记,对于效率优化是非常重要的,当填充锁定内存时,最好使用cache-friendly的数据模式。

托管资源和缺省资源混合使用(Using Both Managed and Default Resources)

管资源与缺省资源的混合分配使用,可能导致显存碎片,并且扰乱了管显存资源的运行环境。最好在使用管资源前,创建所有缺省资源,或者使用EvictManagedResources清除那些管资源再分配非托管资源。记住,所有缺省资源在生命期间,都会常驻于显存中,这期间资源托管器和其他需求都不能使用这块显存。

注意,与以往的Direct3D版本不同,在显存缺乏时,Direct3D 9在分配非托管资源失败时,会自动清除托管资源,这有可能导致潜在的显存碎片,甚至强制把资源放入不适当的地方(比如,位于非本地显存的静态纹理)。再次强调,最好在使用托管资源之前分配全部的非托管资源。

动态缺省资源(Dynamic Default Resources)

经常需要生成和更新的数据,不需要后备存储器,因为所有信息在设备恢复时,都要重新创建。这样的数据最好使用缺省资源,并使用USAGE_DYNAMIC标记,这样驱动程序会做些优化决策,在最适合的地方放置经常更新的数据。这通常意味着资源放置在非本地显存中,这样对于GPU访问本地显存来说,访问速度通常会慢很多。对于UMA(Unified Memory Architecture)架构,驱动程序将动态资源放置在CPU(写)访问效率较高的特定地方。

这种用法(动态缺省资源类型)一般用于软件蒙皮(software skinning)和基于CPU计算的粒子系统中,填充顶点/顶点索引的Buffer,LOCK_DISCARD标记可以保证,资源在前一帧仍被使用的时候,不会导致系统工作的停顿。在这种情况下使用会更新系统内存,拷贝到显存,然后使用了1或2帧后,又将被替换。对于非本地显存系统,合理使用动态方式(dynamic pattern),减少了多余的拷贝。

标准纹理不允许锁定,仅仅可以通过UpdateSurface和UpdateTexture函数更新。一些系统支持动态纹理,它可以通过配合使用LOCK_DISCARD标记进行锁定,但这需要检查D3DCAPS2_DYNAMICTEXTURES硬件能力。对于高动态纹理(视频或程序生成纹理),最好创建匹配POOL_DEFAULT 和POOL_SYSTEMMEM 的资源,并且通过UpdateTexture函数更新纹理。对于高频度和部分更新,UpdateTexture函数可能是最好的选择。

尽管动态资源很常用,但也应注意系统对于动态提交(dynamic submission)的过度依赖。静态资源应使用托管方式,保证在本地显存中的高效,并且合理利用有限的总线和主存带宽。对于半静态的资源,你会发现,偶尔上载到本地显存的代价,比动态资源不断的总线传输相比小很多。(言下之意,对于半静态资源,托管方式更有效率。)

系统内存资源(System Memory Resources)

资源也可以使用POOL_SYSTEMMEM方式创建。但他们不能被图形管线使用,他们能做为源数据用来更新POOL_DEFAULT资源,这是通过UpdateSurface和UpdateTexture函数完成的。他们的锁定操作也非常简单,尽管他们在使用时,可能因为前面提到的原因导致系统停顿。

虽然是在系统内存中,但POOL_SYSTEMMEM资源的格式和能力(比如最大尺寸),受设备驱动程序的制约。POOL_SCRATCH是另一中系统内存的类型,它支持所有格式和特性,但不能被设备直接访问。SCRATCH资源主要用于内容创建工具(max,maya?)。

Figure 3.  Memory resources in video RAM, AGP aperture, and system RAM

一般性的建议(General Recommendations)

正确理解资源管理的技术实现细节,会帮助你构造出高效的应用程序。规划好如何把资源提交到Direct3D,并构架合适的方式来载入数据,是一个相当复杂的任务。以下是当你做决策时,推荐的一些实践经验:

l         预处理所有资源。不理会耗时的加载资源、资源转换、资源优化,虽然这样便于开发,但会给用户电脑增加性能负担。预处理这些资源,可以加快载人,更快使用,你也可以做些更好的离线工作(预处理工作)。

l         避免在每帧创建过多的资源。驱动喜欢连续地与CPU或GPU交互,因为涉及内核操作,所以这个操作有点重量级。在多帧中分散创建,或重复利用资源而不是时不时创建/释放他们。最好在锁定和释放最近使用的资源前,多等待几帧。

l         在一帧结束时,保证断开所有资源通道。(比如,数据流,texture stages,顶点索引)。这样做能保证清除悬浮引用的资源,否则实际上不在使用的资源,也会常驻于内存中。

l         使用压缩纹理格式(如:DXTn),并使用mip-map,还可将小贴图拼接为大贴图(Texture Atlas)使用。这样可大大减少带宽,减少资源大小的负载,提升性能。

l         顶点索引图元。这将减少顶点缓存,并且现代显卡硬件对顶点做了很大优化。利用可编程顶点着色,压缩顶点信息,在顶点处理时,展开(还原)。同时,这样减少了带宽要求,使顶点缓存资源更高效。

l       避免过渡优化资源管理。如果你的程序过分依赖驱动、硬件和操作系统的某些特征,那么这些程序、硬件的修改将会导致潜在的兼容性问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: CMake是一个跨平台的生成代码工具,用于管理项目构建过程,它可以自动化编译过程中的各种任务,如编译、链接和打包等。在使用CMake时,有一些最佳实践需要遵循,以确保项目构建的正确性和高校。CMake Best Practices PDF是一份指南,旨在帮助用户了解如何使用CMake,并通过最佳实践来管理和构建项目。 该指南的主要内容包括使用现代CMake方法编写清晰、可读和可维护的CMake脚本,按功能分割项目并将其组织成库、模块和应用程序,并基于目标架构编写通用CMake文件,以确保跨平台的兼容性。此外,该指南还提供了一些实用技巧和工具,如依赖管理、安装和打包,以帮助开发人员进行更好的构建管理和项目发布。 总的来说,CMake Best Practices PDF是一份非常有价值的指南,旨在帮助开发人员遵循最佳实践来编写和管理CMake项目,以提高项目构建的高校和可维护性。 ### 回答2: 《CMake最佳实践(CMake Best Practices)》是一份技术文档,主要介绍了使用CMake构建C++项目的最佳实践。该文档由Daniel Pfeifer和其它CMake社区成员合作编写,为开发人员提供了一系列关于如何使用CMake构建现代C++应用程序的经验和建议。 该文档详细介绍了CMake的基本工作原理,并提供了使用CMake构建项目的步骤。同时,该文档还着重强调了在编写CMakeLists.txt文件时应该注意的细节,并提供了一些令人信服的理由。 例如,该文档建议编写简洁、模块化的CMakeLists.txt文件,避免使用过多的IF语句和过多的变量。此外,该文档也强调了对目标平台进行适当的测试、生成合适的安装脚本以及使用包管理器等最佳实践。 通过该文档的指导,开发人员可以更好地理解CMake的使用,确保其具有良好的可移植性、可扩展性和可维护性。该文档是CMake社区的重要资源之一,为C++开发人员提供了优秀的参考资料。 ### 回答3: CMake是一款功能强大的构建工具,可用于生成跨平台的软件代码。为了确保项目的顺利构建和维护,需要遵循一些最佳实践。这些实践包括: 1. 确保每个项目都有自己的CMakeLists.txt文件,以便更好地组织项目的结构和依赖关系。同时,应根据需要使用外部项目和库。 2. 编写可移植的CMake代码。这可以帮助确保代码在不同的平台上能够正确地构建和运行。 3. 使用版本控制工具管理代码,并确保将CMake配置文件包含在版本控制系统中。这可以确保在不同的开发环境之间保持一致。 4. 将构建和安装相关的命令分离到不同的CMake文件中,以便更好地管理项目。可以使用类似于“build.sh”或“install.sh”的脚本来组织这些文件。 5. 使用CMake变量来拆分项目配置,以便更好地管理和维护代码。 6. 如果有必要,可以使用预处理器语句来动态配置CMake文件。这可以帮助实现更高级的构建过程。 7. 编写清晰、易于理解的CMake文件。必要时可以添加注释或文档,以便其他开发人员能够理解和维护代码。 总之,CMake是一款非常有用的构建工具,可以帮助简化复杂的项目结构和依赖关系。但是,为了确保项目的顺利构建和维护,需要遵循一些最佳实践,并编写清晰、易于理解的CMake文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值