本文包含有关 Microsoft DirectX 的常见问题解答的集合。
常规 DirectX 开发问题
游戏开发者是否真的关心支持 x64 版本?
绝对是。 x64 技术在市场上广泛使用。 过去几年中销售的大多数新 CPU,以及 AMD 和 Intel 开发的几乎所有处理器线路,都支持 x64。 Windows XP Professional x64 Edition 引入了 2005 年 4 月发布的 x64 的 OS 启用技术。 由于 x64 版本需要新一代的 64 位本机驱动程序,因此第一个版本仅限于 OEM 分发版。
借助 Windows Vista,客户在购买基于 Windows 的计算机时可以自由选择 32 位或 64 位版本,而 Windows Vista 的许可证适用于 32 位或 64 位版本的操作系统。 此外,框中提供了许多 64 位驱动程序,并且设备制造需要同时提供 32 位和 64 位本机驱动程序作为 Windows 认证计划的一部分。
所有这些因素都会大大增加 64 位版本的 Windows 部署。 随着新计算机开始交付超过 2 GB 的物理 RAM,使用 32 位操作系统的激励大大减少,有利于 64 位版本。 64 位技术完全支持 32 位本机代码,尽管需要 64 位本机实现才能充分利用新的 64 位内存空间。 每个 32 位应用程序都应具有 64 位兼容性作为最低发货要求,并且满足该要求是 Windows Vista 兼容性的基线要求。 通常,由于使用专为 Windows 3.1 操作系统设计的 16 位代码或安装 32 位和 64 位本机窗体中未提供的驱动程序,会出现不相容情况。
有关 64 位技术的更多详细信息,请参阅面向游戏开发人员的 64 位编程。
游戏开发人员是否仍应该发布适用于 Windows 95、Windows 98 或 Windows ME 的游戏?
不再出于两个原因:性能和功能集。
如果游戏所需的最低 CPU 速度为 1.2GHz 或更高版本(这对于高性能游戏更常见),则绝大多数符合条件的计算机将运行 Windows XP。 当 CPU 速度超过 1.2GHz 的计算机销售时,几乎所有制造商都安装了 Windows XP 作为默认操作系统。 这意味着在 Windows XP 中会发现许多功能,当今的游戏开发人员应利用以下功能:
- 改进的多任务 - 这为视频、音频和游戏提供了更好的、更流畅的体验。
- 更稳定的视频驱动程序模型 - 它允许更轻松地调试、更流畅的游戏和更好的性能。
- 网络配置更简单 - 可以更轻松地访问多玩家游戏。
- 默认情况下,支持从硬盘驱动器传输 DMA - 这会导致应用程序加载更快、更流畅。
- Windows 错误报告 - 这会导致 OS、驱动程序和应用程序更加稳定。
- Unicode 支持 - 极大地简化了本地化问题。
- 更好的安全性和稳定性 - 这会导致更好的消费者体验。
- 更好地支持新式硬件 - 其中大多数不再使用 Windows 98 驱动程序。
- 改进的内存管理 - 从而提高了稳定性和安全性。
- 改进的 NTFS 文件系统 - 更耐故障,并且具有更好的安全功能表现。
游戏开发人员是否仍应该发布适用于 Windows 2000 的游戏?
不再是。 除了游戏开发者是否仍应该发布适用于 Windows 95、Windows 98 或 Windows ME 的游戏?列出的原因,Windows 2000 没有这些功能:
- Windows XP 支持高级处理器功能,如 Hyper-Threading、多核和 x64。
- Windows XP 支持并行组件,这大大减少了应用程序版本控制冲突。
- Windows XP 支持无执行内存保护,这有助于防止恶意程序,并有助于调试。
- Windows XP 改进了对高级 AGP 和基于 PCI Express 的视频卡的支持。
- Windows XP 支持快速用户切换、远程桌面和远程帮助,这有助于降低产品支持成本。
- PIX(在 DirectX 开发人员 SDK 中)等性能工具不再支持 Windows 2000。
简言之,Windows 2000 从未作为使用者操作系统设计或销售。
Windows Vista 各个版本之间的区别是什么? 它们如何影响我的 DirectX 应用程序?
Windows Vista 系列包括五个版本:
- Windows Vista Home Basic
- Windows Vista Home Premium
- Windows Vista 商用版
- Windows Vista 企业版
- Windows Vista 旗舰版
家庭基本版和家庭高级版是以消费者为中心的版本,具有家庭安全(以前称为家长控制)等功能,家庭高级版包括媒体中心。 企业版是以企业为中心的版本,具有域加入和远程桌面/终端服务等功能。 旗舰版将消费者版和企业版的所有功能合并为一个版本。 所有版本都同时提供 32 位 (x86) 和 64 位 (x64) 版本,并且用户可以免费为这两个平台使用相同的产品标识符。
各种版本的基础技术是相同的,它们都具有相同版本的 DirectX 运行时和其他组件。 但是,这些版本在游戏方面确实存在一些细微的差异:
- 游戏资源管理器存在于所有版本中,但开始菜单上的游戏快捷方式仅在家庭基本版、家庭高级版和旗舰版中。 游戏资源管理器仍可在所有版本上找到(通过单击“开始”,指向“所有程序”,然后单击“游戏”),以及所有版本的 IGameExplorer 接口功能。
- Windows 附带的游戏在企业版中默认不可用,但管理员可以启用这些游戏。
- 家庭安全和游戏分级不会显示或对业务或企业的行为有任何影响,并且当加入域时,它们在旗舰版上被禁用。
用户帐户控制设置在所有版本上具有相同的默认值,但可以通过企业版和旗舰版上的域的组策略设置替代这些设置。 例如,策略设置用户帐户控制:标准用户提升提示的行为很可能设置为自动拒绝许多业务设置中的提升请求,以提高安全性,并且这些环境中的许多用户将始终作为标准用户运行,甚至无法选择作为管理员服务器运行。 任何需要管理权限的程序(如安装程序),无论是由于旧安装检测还是具有将请求的执行级别指定为“requireAdministrator”的清单,在此类情况下始终无法启动。 其他策略设置(如用户帐户控制:仅提升已签名和验证的可执行文件)也可能会阻止安装程序使用 Authenticode 对可执行文件进行签名。
这些类型的策略更改可以应用于任何版本的 Windows Vista,但更有可能在加入域的计算机上。
Windows 7 各个版本之间的区别是什么? 它们如何影响我的 DirectX 应用程序?
大多数 Windows 7 用户可能具有两个版本之一:Windows 7 家庭高级版,适用于家庭用户,或 Windows 7 专业版,适用于业务用户和开发人员。 对于大型公司,有批量许可 Windows 7 企业版,其中包括所有 Windows 7 功能;Windows 7 旗舰版是该版本的零售等效项。
Windows 7 初级版面向 OEM 提供全球版本,预计将使用上网本、超低功率笔记本计算机进行部署。 Windows 7 家庭普通版仅在新兴市场提供。
请注意,Windows 7 的所有版本(初级版除外)都可用于 32 位 (x86) 和 64 位 (x64) 版本,Windows 7 的所有零售包都包括这两个版本的媒体。 与 Windows Vista 一样,用户可在任一平台上免费使用相同的零售产品标识符。
各种版本中的基础技术是相同的,所有版本具有相同版本的 DirectX 运行时和其他组件。 它们确实在游戏功能方面存在一些差异:
- 游戏资源管理器存在于所有版本中,但在 Windows 7 专业版和企业版中默认隐藏开始菜单的游戏快捷方式。 游戏资源管理器仍可在开始菜单上找到(通过单击“所有程序”,然后双击“游戏”),游戏快捷方式可由用户直接启用。
- Windows 附带的游戏在 Windows 7 专业版和企业版中默认不可用,但管理员可以启用这些游戏。
- 家庭保险和游戏分级在所有版本中均可用,但在操作系统加入域时,它们已在 Windows 7 专业版、企业版和旗舰版上禁用。 与Windows Vista 旗舰版一样,可以在已加入域的计算机上重新启用此功能。
用户帐户控制 (UAC) 设置可能会受到 Windows 7 专业版、企业版和旗舰版上的组策略设置的影响,这与 Windows Vista 非常类似。 有关详细信息,请参阅 Windows Vista 各个版本之间的区别是什么?它们如何影响我的 DirectX 应用程序?
DirectX 10 是否可用于 Windows XP?
不是。 具有 DirectX 10 的 Windows Vista 包括基于 Windows XP SP2 (DirectX 9.0c) 中的运行时的更新 DirectX 运行时,以及使用新的 Windows 显示驱动程序模型 (WDDM) 和新音频驱动程序堆栈的更改,以及操作系统中的其他更新。 除了 Direct3D 9,当存在正确的视频硬件和驱动程序时,Windows Vista 还支持两个新接口:Direct3D9Ex 和 Direct3D10。
由于这些新接口依赖于 WDDM 技术,因此它们永远不会在早期版本的 Windows 上使用。 对适用于 Windows Vista 的 DirectX 技术所做的所有其他更改也特定于新版本的 Windows。 DirectX 10 名称具有误导性,此版本号未包含 DirectX SDK (XACT, XINPUT, D3DX) 中的许多技术。 因此,将 DirectX 运行时的版本号作为一个整体失去了大部分意义,即使对于 9.0c 也是如此。 Windows Vista 上的 DirectX 诊断工具 (DXdiag.exe) 确实报告 DirectX 10,但这实际上指的只是 Direct3D 10。
DirectX 11 是否可用于 Windows Vista 或 Windows XP?
DirectX 11 内置于 Windows 7 中,它可用作 Windows Vista 的更新(请参阅 https://go.microsoft.com/fwlink/p/?linkid=160189)。 这包括 Direct3D 11 API、DirectX 图形基础结构 (DXGI) 1.1、10Level9 功能级别、Windows 高级光栅化平台 (WARP) 10 软件呈现设备、Direct2D、DirectWrite 和 Direct3D 10.1 API 的更新,以支持 10Level9 和 WARP 10。
由于上述问题(DirectX 10 是否可用于 Windows XP?)中所述的相同原因,Direct3D 11 和相关 API 在 Windows XP 上不可用。
DirectShow 发生了什么情况? 我在 DirectX SDK 中找不到它。
DirectShow 自 2005 年 4 月起从 DirectX SDK 中删除。 可以在 Windows 软件开发工具包(以前称为平台 SDK)中获取 DirectShow 的标头、库、工具和样本。 DirectX SDK 中的 DirectSetup 继续支持 DirectShow 系统组件的重新分发,并且最新的组件已安装在以下操作系统上:Microsoft Windows XP Service Pack 2、Windows XP Professional x64 Edition、Windows Server 2003 Service Pack 1 和 Windows Vista。
对 Windows Vista 的 DirectX 运行时进行了哪些更改?
进行了主要更改以支持新的 WDDM。 有关新驱动程序模型、Direct3D 9 的影响以及两个新的图形接口 Direct3D 9Ex 和 Direct3D 10 的详细信息,请查看 Windows 中的图形 API。 适用于 Windows 7 的新图形 API(Direct3D 11、Direct2D、DirectWrite、DXGI 1.1 和更新的 Direct3D 10.1)可用作 Windows Vista 的更新(请参阅 https://go.microsoft.com/fwlink/p/?linkid=160189)。
Windows Vista Service Pack 1 包括 DirectX 运行时的更新版本。 此更新扩展了对 Windows Vista 的支持,以包括 Direct3D 10.1,公开新的可选硬件功能。 (能够支持 Direct3D 10.1 的所有硬件也完全支持 Direct3D 10 的所有功能。)
DirectSound 已更新,以公开新的 Windows Vista 音频驱动程序堆栈的功能,该堆栈支持多声道软件缓冲区。 Direct3D 保留模式 API 已完全从 Windows Vista 中删除。 DirectPlay Voice 也已删除,以及 DirectPlay 的 NAT 帮助程序和 DirectInput 的操作映射器 UI。 Windows Vista 上不提供对 Visual Basic 6.0 的 DirectX 7 和 DirectX 8 接口的支持。
对 Windows 7 的 DirectX 运行时进行了哪些更改?
Windows 7 包括 Windows Vista 中找到的所有 DirectX 运行时组件,并添加了 Direct3D 11、DXGI 1.1、10Level9 功能级别、WARP10 软件设备、Direct2D、DirectWrite 和 Direct3D 10.1 更新以支持 10Level9 和 WARP10。 有关详细信息,请参阅 Windows 中的图形 API。
所有其他组件都与 Windows Vista 相同,增加了 64 位 (x64) 本机支持的核心 DirectMusic API,这与带时间戳的 MIDI 相关。 DirectMusic 的性能层仍然已弃用,并且它仅适用于 Windows 7 上的 32 位应用程序,以适应应用程序兼容性。 请注意,Windows Vista 上不提供对 DirectMusic 的 64 位本机支持。
我想我发现了一个驱动程序 bug,我该怎么办?
首先,请确保已使用参考光栅器检查结果。 然后,使用最新 WHQL 认证的 IHV 驱动程序版本检查结果。 可以使用 IDirect3D9 接口上的 GetAdapterIdentifier() 方法以编程方式检查 WHQL 状态,并传递 D3DENUM_WHQL_LEVEL 标志。
尝试编译示例时,为何会收到这么多错误消息?
你可能没有正确设置包含路径。 许多编译器(包括 Microsoft Visual C++)包括早期版本的 SDK,因此,如果包含路径先搜索标准编译器包含目录,则会收到标头文件的不正确版本。 若要解决此问题,请确保将包含路径和库路径设置为首先搜索 Microsoft DirectX 包含路径和库路径。 另请参阅 SDK 中的 dxreadme.txt 文件。 如果安装 DirectX SDK 并使用 Visual C++,安装程序可以选择为你设置包含路径。
我收到有关全局唯一标识符 (GUID) 的多个或缺失符号的链接器错误,我该怎么办?
使用的各种 GUID 应定义一次且仅一次。 如果在包括 DirectX 头文件之前 #define INITGUID 符号,则会插入 GUID 的定义。 因此,应确保这只发生在一个编译单元中。 此方法的替代方法是与 dxguid.lib 库链接,该库包含所有 DirectX GUID 的定义。 如果使用此方法(建议这样做),则绝不应 #define INITGUID 符号。
是否可以将指向 DirectX 接口的指针转换为较低的版本号?
不是。 DirectX 接口是 COM 接口。 这意味着,不需要从相应的编号较低的接口派生更高编号的接口。 因此,获取 DirectX 对象不同接口的唯一安全方法是使用接口的 QueryInterface 方法。 此方法是标准 IUnknown 接口的一部分,必须从中派生所有 COM 接口。
是否可以在同一应用程序中混合使用 DirectX 9 组件和 DirectX 8 或更早的组件?
可以自由混合不同版本的不同组件;例如,可以在同一应用程序中将 DirectInput 8 与 Direct3D 9 配合使用。 但是,通常不能在同一应用程序中混合相同组件的不同版本;例如,不能将 DirectDraw 7 与 Direct3D 9 混合(因为这些组件实际上是相同的组件,因为 DirectDraw 的组件已从 DirectX 8 开始细分为 Direct3D)。 但是,存在例外情况,例如在同一应用程序中同时使用 Direct3D 9 和 Direct3D 10,这是允许的。
是否可以在同一应用程序中混合使用 Direct3D 9 和 Direct3D 10?
是的,可以在同一应用程序中将这些版本的 Direct3D 一起使用。
Release 或 AddRef 方法的返回值意味着什么?
返回值将是对象的当前引用计数。 但是,COM 规范指出不应依赖此功能,并且该值通常仅用于调试目的。 观察到的值可能是意外值,因为其他各种系统对象可能保存对所创建的 DirectX 对象的引用。 因此,在引用计数为零之前,不应编写重复调用 Release 的代码,因为对象可能随后可以释放,即使另一个组件可能仍在引用它。
发布 DirectX 接口的顺序是否重要?
这一点并不重要,因为对 COM 接口进行引用计数。 但是,某些版本的 DirectX 中接口发布顺序存在一些已知 bug。 安全起见,建议尽可能按反向创建顺序释放接口。
什么是智能指针,我应该使用它吗?
智能指针是一个 C++ 模板类,旨在封装指针功能。 具体而言,有一些标准智能指针类旨在封装 COM 接口指针。 这些指针会自动执行 QueryInterface,而不是强制转换,它们为你处理 AddRef 和 Release。 是否应该使用它们在很大程度上是品味问题。 如果代码包含大量复制接口指针,并且具有多个 AddRefs 和 Releases,则智能指针可能会使代码更整洁,且不容易出错。 否则,可以在不使用它们的情况下执行此操作。 Visual C++ 包括标准 Microsoft COM 智能指针,在“comdef.h”头文件中定义(在帮助中查找 com_ptr_t)。
调试 DirectX 应用程序时遇到问题,有没有什么处理提示?
调试 DirectX 应用程序最常见的问题是在锁定 DirectDraw 图面时尝试调试。 这种情况可能会导致 Microsoft Windows 9x 系统上出现“Win16 锁定”,从而阻止调试器窗口进行绘制。 锁定图面时指定 D3DLOCK_NOSYSLOCK 标志通常可以消除这种情况。 Windows 2000 不会遇到此问题。 开发应用程序时,使用 DirectX 运行时的调试版本(在安装 SDK 时选择)运行非常有用,该版本执行一些参数验证,并将有用的消息输出到调试器输出。
检查返回代码的正确方法是什么?
使用 SUCCEEDED 和 FAILED 宏。 DirectX 方法可以返回多个成功和失败代码,因此简单的:
== D3D_OK
或类似的测试并不总是足够。
如何禁用 Alt+TAB 和其他任务切换?
别这样! 游戏需要能够正常处理任务切换,因为许多因素都会导致它发生:ALT+TAB、远程桌面连接、快速用户切换、家长控制使用限制和其他许多事件。
同时,使用键盘为中心的控制方案在游戏上意外切换任务的两个常见来源是按 Windows 徽标键,还有使用 SHIFT 键激活辅助功能 StickyKeys。 若要通过禁用功能来解决这些情况,请参阅在游戏中禁用快捷键中所述的技术。
是否有介绍 COM 的推荐书?
微软出版社出版的 Dale Rogerson 的《Inside COM》是 COM 的优秀介绍。 有关 COM 的更详细的了解,也强烈推荐 Don Box 的《Essential COM》一书。
什么是托管代码?
托管代码是由 .NET Framework 公共语言运行时 (CLR) 管理其执行的代码。 它是指本机执行代码与运行时之间的合作协定。 此协定指定在任何执行点,运行时可能会停止执行 CPU,并检索特定于当前 CPU 指令地址的信息。 必须可查询的信息通常与运行时状态有关,例如寄存器或堆栈内存内容。
在运行代码之前,IL 将编译为本机可执行代码。 而且,由于此编译由托管执行环境(或者更正确地,由知道如何面向托管执行环境的运行时感知编译器执行),因此托管执行环境可以保证代码将执行的操作。 它可以插入陷阱和适当的垃圾回收挂钩、异常处理、类型安全性、数组边界和索引检查等。 例如,此类编译器确保将堆栈帧和所有内容布局正确,以便垃圾回收器可以在单独的线程上后台运行,不断走活动调用堆栈,查找所有根,追逐所有实时对象。 此外,由于 IL 具有类型安全性的概念,因此执行引擎将维护类型安全性的保证,从而消除一系列经常导致安全漏洞的编程错误。
与此相反,非托管世界:非托管可执行文件基本上是加载到内存中的二进制图像 x86 代码。 程序计数器将放在其中,这是 OS 知道的最后一点。 内存管理和端口 I/O 等存在保护,但系统实际上不知道应用程序正在做什么。 因此,它无法保证应用程序运行时会发生什么。
关于常规 Windows 编程的书籍有哪些?
有很多。 但是,强烈推荐以下两本:
- Programming Window,Charles Petzold (Microsoft Press)
- Programming Applications for Windows,Jeffrey Richter (Microsoft Press)
如何实现使用 Windows 符号文件进行调试?
Microsoft 发布所有系统 DLL 的剥离符号(外加一些其他)。 若要访问它们,请在 Visual Studio 中的项目设置中将以下内容添加到符号路径:
srv*https://msdl.microsoft.com/download/symbols
对于本地缓存符号,请使用以下语法:
srv*c:\cache*https://msdl.microsoft.com/download/symbols
其中 c:\cache 是用于缓存符号文件的本地目录。
Direct3D 问题
常规 Direct3D 问题
在哪里可以找到有关 3D 图形技术的信息?
该主题的标准书是《计算机图形学:原理及实践》(Computer Graphics: Principles and Practice),Van Dam 等著。对于任何想要了解几何图形、光栅化和照明技术的数学基础的人来说,这是一个有价值的资源。 comp.graphics.algorithms Usenet 组的常见问题解答还包含有用的材料。
Direct3D 仿真功能是否不由硬件提供?
视情况而定。 Direct3D 具有功能齐全的软件顶点处理管道(包括对自定义顶点着色器的支持)。 但是,没有为像素级别操作提供仿真;应用程序必须检查相应的上限位,并使用 ValidateDevice API 来确定支持。
Direct3D 是否包含软件光栅器?
不适用于性能应用程序。 参考光栅器用于驱动程序验证,但实现旨在实现准确性,而不是性能。 Direct3D 支持插件软件光栅器。
如何使用 DirectX 图形执行颜色键?
颜色键不直接受支持,而是必须使用 alpha 混合来模拟颜色键。 D3DXCreateTextureFromFileEx() 函数可用于促进此操作。 此函数接受关键颜色参数,并将包含指定颜色的源图像中的所有像素替换为所创建的纹理中的透明黑色像素。
Direct3D 几何代码是否利用 3DNow? 和/或 Pentum III SIMD 说明?
是的。 Direct3D 几何图形管道具有多个不同的代码路径,具体取决于处理器类型,它将利用 3DNow 提供的特殊浮点操作。 或具有以下可用位置的 Pentium III SIMD 说明。 这包括处理自定义顶点着色器。
如何实现阻止将透明像素写入 z 缓冲区?
可以使用高于或低于给定阈值的 alpha 值筛选出像素。 通过使用 renderstates ALPHATESTENABLE、ALPHAREF 和 ALPHAFUNC 来控制此行为。
什么是模具缓冲区?
模具缓冲区是每个像素信息的附加缓冲区,非常类似于 Z 缓冲区。 事实上,它驻留在 Z 缓冲区的某些位中。 常见的模具/z 缓冲区格式为 15 位 z 和 1 位模具,或 24 位 z 和 8 位模具。 在呈现多边形时,可以按像素对模具缓冲区的内容执行简单的算术运算。 例如,模具缓冲区可以递增或递减,或者如果模具值未能通过简单的比较测试,则可以拒绝像素。 这对于涉及标记帧缓冲区区域,然后仅执行呈现标记区域(或未标记)区域的效果非常有用。 良好的示例是阴影卷等音量效果。
如何实现使用模具缓冲区呈现阴影卷?
此模具缓冲区和其他卷模具缓冲区效果的关键是模具缓冲区与 Z 缓冲区之间的交互。 阴影卷的场景以三个阶段呈现。 首先,使用 Z 缓冲区像往常一样呈现没有阴影的场景。 接下来,阴影在模具缓冲区中标记为如下所示。 阴影卷的正面是使用不可见多边形绘制的,已启用 z 测试但禁用 z 写入,模具缓冲区在通过 z 测试的每个像素上递增。 阴影卷的背面也以类似的方式呈现,但会减少模具值。
现在,请考虑单个像素。 假设相机不在阴影音量中,场景中的相应点有四种可能性。 如果相机到该点的光线不与阴影音量相交,则不会在那里绘制任何阴影多边形,模具缓冲区仍为零。 否则,如果点位于阴影卷前,则阴影多边形将得到 Z 缓冲,模具再次保持不变。 如果点位于阴影卷后面,则呈现的正面阴影面数与背面人脸相同,模具将为零,递减次数也递增。
最终的可能性是点位于阴影卷内。 在这种情况下,阴影卷的背面将缓冲出 z,但不是前侧,因此模具缓冲区将是非零值。 结果是阴影中位于阴影中的帧缓冲区部分具有非零模具值。 最后,为了实际呈现阴影,整个场景将用 alpha 混合多边形进行冲刷,以仅影响具有非零模具值的像素。 在 DirectX SDK 附带的“阴影卷”示例中可以看到此方法的示例。
什么是纹素对齐规则? 如何实现获取一对一映射?
Direct3D 9 文档中对此进行了充分说明。 但是,执行摘要是,应将屏幕坐标偏差为像素的 -0.5,以便与纹素正确对齐。 大多数卡现在都符合纹素对齐规则,但有些较旧的卡或驱动程序没有。 若要处理这些情况,最好的建议是联系有问题的硬件供应商,并请求更新的驱动程序或其建议的解决方法。 请注意,在 Direct3D 10 中,此规则不再保留。
D3DCREATE\_PUREDEVICE 标志的用途是什么?
在创建设备期间使用 D3DCREATE_PUREDEVICE 标志创建纯设备。 纯设备不会保存当前状态(在状态更改期间),这通常会提高性能。此设备还需要硬件顶点处理。 完成开发和调试时,通常会使用纯设备,并且你想要实现最佳性能。
纯设备的一个缺点是它不支持所有 Get* API 调用:这意味着不能使用纯设备来查询管道状态。 这使得在运行应用程序时调试更加困难。 下面是纯设备禁用的所有方法的列表。
- IDirect3DDevice9::GetClipPlane
- IDirect3DDevice9::GetClipStatus
- IDirect3DDevice9::GetLight
- IDirect3DDevice9::GetLightEnable
- IDirect3DDevice9::GetMaterial
- IDirect3DDevice9::GetPixelShaderConstantF
- IDirect3DDevice9::GetPixelShaderConstantI
- IDirect3DDevice9::GetPixelShaderConstantB
- IDirect3DDevice9::GetRenderState
- IDirect3DDevice9::GetSamplerState
- IDirect3DDevice9::GetTextureStageState
- IDirect3DDevice9::GetTransform
- IDirect3DDevice9::GetVertexShaderConstantF
- IDirect3DDevice9::GetVertexShaderConstantI
- IDirect3DDevice9::GetVertexShaderConstantB
纯设备的第二个缺点是它不会筛选任何冗余状态更改。 使用纯设备时,应用程序应将呈现循环中的状态更改数减少到最小值;这可能包括筛选状态更改,以确保状态不会多次设置。 此权衡取决于应用程序;如果使用每个帧超过 1000 个集调用,应考虑利用非纯设备自动执行的冗余筛选。
与所有性能问题一样,了解应用程序是否在纯设备中表现更好的唯一方法是将应用程序的性能与纯设备与非纯设备进行比较。 纯设备可以通过减少 API 的 CPU 开销来加速应用程序。 但请小心! 在某些情况下,纯设备会降低应用程序速度(由于冗余状态更改导致的 CPU 工作增加)。 如果不确定哪种类型的设备最适合应用程序,并且不会筛选应用程序中的冗余更改,请使用非纯设备。
如何枚举多监视器系统中的显示设备?
应用程序可以使用 IDirect3D9 接口的方法通过简单迭代来执行枚举。 调用 GetAdapterCount 以确定系统中的显示适配器数。 调用 GetAdapterMonitor 以确定适配器连接到的物理监视器(此方法返回 HMONITOR,然后可在 Win32 API GetMonitorInfo 中使用该监视器来确定有关物理监视器的信息)。 确定特定显示适配器或在该适配器上创建 Direct3D 设备的特征与在调用 GetDeviceCaps、CreateDevice 或其他方法时传递适当的适配器编号代替 D3DADAPTER_DEFAULT 一样简单。
D3D9 中的固定函数凸起映射发生了什么情况?
从 Direct3D 9 开始,我们在只能支持> 2 个同时纹理的卡上加强了验证。 使用特定的 alpha 调节操作时,某些较旧的卡只有 3 个纹理阶段可用。 人们使用 3 个阶段的最常见用法是浮雕凸起贴图,你仍然可以使用 D3D9 执行此操作。
高度字段必须存储在 alpha 通道中,用于调节照明贡献,即:
// Stage 0 is the base texture, with the height map in the alpha channel
m_pd3dDevice->SetTexture(0, m_pEmbossTexture );
m_pd3dDevice->SetTextureStageState(0, D3DTSS_TEXCOORDINDEX, 0 );
m_pd3dDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_MODULATE );
m_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pd3dDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE );
m_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_SELECTARG1 );
m_pd3dDevice->SetTextureStageState(0, D3DTSS_ALPHAARG1, D3DTA_TEXTURE );
if( m_bShowEmbossMethod )
{
// Stage 1 passes through the RGB channels (SELECTARG2 = CURRENT), and
// does a signed add with the inverted alpha channel.
// The texture coords associated with Stage 1 are the shifted ones, so
// the result is:
// (height - shifted_height) * tex.RGB * diffuse.RGB
m_pd3dDevice->SetTexture( 1, m_pEmbossTexture );
m_pd3dDevice->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX, 1 );
m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP, D3DTOP_SELECTARG2 );
m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
m_pd3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );
m_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAOP, D3DTOP_ADDSIGNED );
m_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAARG1, D3DTA_TEXTURE|D3DTA_COMPLEMENT );
m_pd3dDevice->SetTextureStageState( 1, D3DTSS_ALPHAARG2, D3DTA_CURRENT );
// Set up the alpha blender to multiply the alpha channel
// (monochrome emboss) with the src color (lighted texture)
m_pd3dDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE );
m_pd3dDevice->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_SRCALPHA );
m_pd3dDevice->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ZERO );
}
此示例和其他较旧的示例不再在当前 SDK 版本中提供,将来的 SDK 版本中不会提供。
几何图形(顶点)处理
我很困惑:顶点流的工作原理是什么?
Direct3D 从一个或多个顶点流将馈送到管道的处理部分的每个顶点。 只有一个顶点流对应于旧的 DirectX 8 模型,其中顶点来自单个源。 使用 DirectX 8 时,不同的顶点组件可能来自不同的源;例如,一个顶点缓冲区可以容纳位置和法线,而第二个保留的颜色值和纹理坐标。
什么是顶点着色器?
顶点着色器是处理单个顶点的过程。 它使用由 D3DX 实用工具库组合的简单类似程序集的语言定义为 Direct3D 接受的令牌流。 顶点着色器采用单个顶点和一组常量值作为输入;它输出顶点位置(在剪辑空间中)和一组颜色和纹理坐标,这些坐标用于光栅化。 请注意,当你具有自定义顶点着色器时,顶点组件不再具有 Direct3D 对其应用的任何语义,顶点只是由你创建的顶点着色器解释的任意数据。
顶点着色器是否执行透视除法或剪裁?
不是。 顶点着色器在剪辑空间中输出转换的顶点位置的同质坐标。 透视除法和剪裁会自动在着色器后执行。
是否可以使用顶点着色器生成几何图形?
顶点着色器无法创建或销毁顶点;它一次在单个顶点上运行,采用一个未处理的顶点作为输入并输出单个处理的顶点。 因此,它可用于操作现有几何体(应用变形或执行皮肤操作),但实际上无法生成新的几何图形。
是否可以将自定义顶点着色器应用于固定函数几何图形管道的结果(反之亦然)?
不是。 必须选择一个或另一个。 如果使用自定义顶点着色器,则负责执行整个顶点转换。
如果硬件不支持自定义顶点着色器,是否可以使用自定义顶点着色器?
是的。 Direct3D 软件顶点处理引擎完全支持具有惊人高性能的自定义顶点着色器。
如何确定硬件是否支持自定义顶点着色器?
硬件中支持顶点着色器的设备需要填写 D3DCAPS9::VertexShaderVersion 字段,以指示它们支持的顶点着色器的版本级别。 任何声明支持特定级别的顶点着色器的设备都必须支持满足该级别或更低级别的规范的所有合法顶点着色器。
顶点着色器可以使用多少常量寄存器?
支持与 1.0 顶点着色器的设备至少支持 96 个常量寄存器。 设备可能支持超过此最小数目,并且可以通过 D3DCAPS9::MaxVertexShaderConst 字段报告此值。
是否可以在不同的纹理坐标的顶点之间共享位置数据?
这种情况的通常示例是一个立方体,你希望为每个面使用不同的纹理。 遗憾的是,答案为否,目前无法独立为顶点组件编制索引。 即使有多个顶点流,所有流也会一起编制索引。
提交基元索引列表时,Direct3D 是否处理缓冲区中的所有顶点,还是只处理索引的顶点?
使用软件几何图形管道时,Direct3D 首先转换所提交范围中的所有顶点,而不是在索引时“按需”转换它们。 对于密集打包的数据(即使用大多数顶点的位置),这更加高效,尤其是在 SIMD 指令可用时。 如果数据被稀疏打包(即未使用许多顶点),则可能需要考虑重新排列数据,以避免过多的冗余转换。 使用硬件几何加速时,顶点通常按需转换,因为它们是必需的。
什么是索引缓冲区?
索引缓冲区与顶点缓冲区完全相同,但它包含用于 DrawIndexedPrimitive 调用的索引。 强烈建议尽可能使用索引缓冲区,而不是原始应用程序分配的内存,原因与顶点缓冲区相同。
我注意到 32 位索引是受支持的类型,是否可以在所有设备上使用它们?
不是。 必须检查 D3DCAPS9::MaxVertexIndex 字段来确定设备支持的最大索引值。 此值必须大于 2 到第 16 个电源 -1 (0xffff),才能支持类型为 D3DFMT_INDEX32 的索引缓冲区。 此外,请注意,某些设备可能支持 32 位索引,但支持小于 2 到第 32 个电源 -1 的最大索引值 (0xffffffff):在这种情况下,应用程序必须遵循设备报告的限制。
S/W 顶点处理是否支持 64 位?
x64 有优化的 s/w 顶点管道,但在 IA64 不存在。
性能优化
如何提高 Direct3D 应用程序的性能?
以下是优化性能时要研究的关键方面:
批大小
Direct3D 针对大量基元进行优化。 单个调用中可以发送的多边形越多越好。 良好的经验法则是针对每个基元调用平均 1000 个顶点。 低于该级别,你可能没有获得最佳性能,高于该级别,并且你正在减少与并发注意事项的返回和潜在冲突(请参阅下文)。
状态更改
更改呈现状态可能是一项昂贵的操作,尤其是在更改纹理时。 因此,必须尽可能少地减少每个帧所做的状态更改数。 此外,尝试最大程度地减少顶点或索引缓冲区的更改。
注意
从 DirectX 8 开始,更改顶点缓冲区的成本不再像以前版本一样昂贵,但最好在可能的情况下避免更改顶点缓冲区。
并发
如果可以安排与其他处理同时执行呈现,则将充分利用系统性能。 此目标可以与减少呈现状态更改的目标冲突。 你需要在批处理之间取得平衡,以减少状态更改,并将数据提前推送到驱动程序,以帮助实现并发。 以轮循机制方式使用多个顶点缓冲区有助于实现并发。
纹理上传
将纹理上传到设备会消耗带宽,并导致与顶点数据的带宽竞争。 因此,请务必不要过度提交纹理内存,这会强制缓存方案上传每个帧过多的纹理。
顶点和索引缓冲区
应始终使用顶点和索引缓冲区,而不是应用程序分配内存的纯块。 顶点和索引缓冲区的锁定语义至少可以避免冗余复制操作。 使用某些驱动程序时,顶点或索引缓冲区可能放置在更理想的内存(可能位于视频或 AGP 内存中)供硬件访问。
状态宏块
DirectX 7.0 中引入了这些内容。 它们提供了一种机制,用于将一系列状态更改(包括照明、材料和矩阵更改)记录到宏中,然后可以通过单个调用重播该宏。 这样做有两个优点:
- 通过进行一次调用而不是多次来减少呼叫开销。
- 感知驱动程序可以预先分析和预编译状态更改,从而更快地提交到图形硬件。
状态更改可能仍然很昂贵,但使用状态宏有助于至少降低一些成本。 仅使用单个 Direct3D 设备。 如果需要呈现到多个目标,请使用 SetRenderTarget。 如果要创建具有多个 3D 窗口的开窗应用程序,请使用 CreateAdditionalSwapChain API。 运行时针对单个设备进行优化,使用多个设备的速度会受到相当大的惩罚。
我应该使用哪些基元类型(条带、风扇、列表等)?
在实际数据特征顶点中遇到的许多网格,这些顶点由多个多边形共享。 为了最大程度地提高性能,最好减少转换的顶点中的重复,并通过总线发送到呈现设备。 很明显,使用简单的三角形列表不会实现顶点共享,这使其成为最不适合的方法。 然后,选择落在了使用条带和风扇,这意味着多边形与使用索引列表之间存在特定连接关系。 如果数据自然属于条带和风扇,则这是最合适的选择,因为它们将发送到驱动程序的数据降到最低。 但是,将网格分解成条带和风扇通常会产生大量单独的片段,这意味着大量的 DrawPrimitive 调用。 因此,最有效的方法一般是将单个 DrawIndexedPrimitive 调用与三角形列表一起使用。 使用索引列表的另一个优点是,即使连续三角形只共享单个顶点,也可以获得好处。 总之,如果数据自然属于大型条带或风扇,请使用条带或风扇;否则使用索引列表。
如何确定卡包含的总纹理内存(不包括 AGP 内存)?
IDirect3DDevice9::GetAvailableTextureMem 返回总可用内存,包括 AGP。 根据拥有的视频内存数量的假设分配资源并不是一个好主意。 例如,如果卡片在统一内存体系结构 (UMA) 下运行或能够压缩纹理,该怎么办? 可用空间可能比你想象的要多。 应为“内存不足”错误创建资源和检查,然后缩减纹理。 例如,可以删除纹理的顶级 mip 级别。
如果生成动态数据,顶点缓冲区的使用情况模式是什么?
- 使用 D3DUSAGE_DYNAMIC 和 D3DUSAGE_WRITEONLY 使用标志与 D3DPOOL_DEFAULT 池标志创建顶点缓冲区。 (如果使用的是软件顶点处理,还要指定 D3DUSAGE_SOFTWAREPROCESSING。)
- I = 0.
- 设置状态(纹理、呈现状态等)。
- 检查缓冲区中是否有空间,例如,I + M <= N? (其中 M 是新顶点的数目)。
- 如果是,请使用 D3DLOCK_NOOVERWRITE 锁定 VB。 这会告知 Direct3D 和驱动程序要添加顶点,并且不会修改之前批处理的顶点。 因此,如果 DMA 操作正在进行,则不会中断。 如果没有,请转到 11。
- 填写 I 处的 M 顶点。
- 解锁。
- 调用 Draw[Indexed]Primitive。 对于非索引基元,请使用 I 作为 StartVertex 参数。 对于索引基元,请确保索引指向顶点缓冲区的正确部分(使用 SetIndices 调用的 BaseVertexIndex 参数最容易实现此目的)。
- I += M.
- Goto 3.
- 好,我们空间不足,就从新的 VB 开始。 我们不想使用相同的操作,因为可能存在正在进行的 DMA 操作。 我们通过锁定具有 D3DLOCK_DISCARD 标志的同一 VB 来与 Direct3D 和驱动程序通信。 这意味着“你可以给我一个新的指针,因为我已经完成了旧指针,不再真正关心旧内容”。
- I = 0.
- 转到 4(或 6)。
为什么必须指定 D3DVERTEXELEMENT9 结构中的详细信息?
从 Direct3D 9 开始,顶点流声明不再是 DWORD 数组,现在是 D3DVERTEXELEMENT9 结构的数组。 运行时利用其他语义和用法信息将顶点流的内容绑定到顶点着色器输入寄存器/变量。 对于 Direct3D 9,顶点声明与顶点着色器分离,这使得在运行时仅绑定着色器所需的数据时,能够更轻松地将具有不同格式的着色器与几何图形结合使用。
新的顶点声明可与固定函数管道或着色器一起使用。 对于固定函数管道,无需调用 SetVertexShader。 但是,如果要切换到固定函数管道,并且以前使用了顶点着色器,请调用 SetVertexShader(NULL)。 完成此操作后,仍需要调用 SetFVF 来声明 FVF 代码。
使用顶点着色器时,使用顶点着色器对象调用 SetVertexShader。 此外,调用 SetFVF 以设置顶点声明。 这使用 FVF 中隐式的信息。 可以调用 SetVertexDeclaration 来代替 SetFVF,因为它支持不能用 FVF 表示的顶点声明。
D3DX 实用工具库
D3DX 图像文件加载程序函数支持哪些文件格式?
D3DX 图像文件加载程序函数支持 BMP、TGA、JPG、DIB、PPM 和 DDS 文件。
D3DX 中的文本呈现函数似乎不起作用,我该怎么办?
使用 ID3DXFont::D rawText 函数时常见的错误是指定颜色参数的零 alpha 分量;导致完全透明(即不可见)文本。 对于完全不透明的文本,请确保颜色参数的 alpha 分量完全饱和 (255)。
如何将图面或纹理的内容保存到文件中?
DirectX 8.1 SDK 专门为此目的向 D3DX 库添加了两个函数:D3DXSaveSurfaceToFile() 和 D3DXSaveTextureToFile()。 这些函数支持以 BMP 或 DDS 格式将图像保存到文件。 在以前的版本中,必须锁定图面并读取图像数据,然后将其写入位图文件。 有关编写用于存储位图的函数的信息,请参阅存储图像。
或者,GDI+ 可用于以各种格式保存图像,不过这需要额外的支持文件才能随应用程序一起分发。
如何在游戏中使用高级着色器语言 (HLSL)?
可通过三种方式将 Microsoft 高级着色器语言 (HLSL) 合并到游戏引擎中:
- 将着色器源编译为顶点或像素底纹程序集(使用命令行实用工具 fxc.exe),并在运行时使用 D3DXAssembleShader()。 这样,即使是 DirectX 8 游戏也可以利用 HLSL 的强大功能。
- 使用 D3DXCompileShader() 将着色器源编译为令牌流和常量表形式。 运行时加载令牌流和常量表,并在设备上调用 CreateVertexShader() 或 CreatePixelShader() 以创建着色器。
- 启动和运行的最简单方法是使用效果文件调用 D3DXCreateEffectFromFile() 或 D3DXCreateEffectFromResource() 来利用 D3DX 效果系统。
新的着色器编译器标志的用途是什么?
从 2006 年 12 月 DirectX SDK 开始,为 Direct3D 10 开发的新 HLSL 编译器已为 Direct3D 9 目标启用。 新编译器不支持 ps_1_x 目标,现在是所有 Direct3D HLSL 着色器的默认编译器。 用于向后兼容性的标志可用于强制 ps_1_x 目标编译为 ps_2_0 目标。
希望使用旧编译器的应用程序可以通过在运行时(请参阅编译器标志)或使用 fxc 时提供开关来继续执行此操作。
从效果获取着色器的正确方法是什么?
使用 D3DXCreateEffect 创建 ID3DXEffect,然后使用 GetPassDesc 检索 D3DXPASS_DESC。 此结构包含指向顶点和像素着色器的指针。
请勿使用 ID3DXEffectCompiler::GetPassDesc。 从此方法返回的顶点和像素着色器句柄为 NULL。
HLSL noise() 的内在用途是什么?
干扰内部函数根据 Ken Perlin 的定义生成 perlin 噪声。 HLSL 函数当前只能用于填充纹理着色器中的纹理,因为当前 h/w 本身不支持该方法。 纹理着色器与 D3DXFill*Texture() 函数结合使用,这些函数是有用的帮助程序函数,用于在加载期间生成程序定义的纹理。
如何检测是使用像素着色器模型 2.0 还是 2.a?
可以使用 D3DXGetPixelShaderProfile() 和 D3DXGetPixelShaderProfile() 函数,他们返回的字符串确定哪个 HLSL 配置文件最适合正在运行的设备。
如何访问预编译效果着色器中的参数?
通过用于访问常量表的 ID3DXConstantTable 接口。 此表包含高级语言着色器和效果使用的变量。
是否有办法将用户数据添加到效果或其他资源?
是的,若要设置调用 SetPrivateData 的专用数据(pReal 是 D3D 纹理对象,pSpoof 是包装的纹理对象)。
hr = pReal->SetPrivateData(IID_Spoof, &pSpoof,
sizeof(IDirect3DResource9*), 0)));
查找包装的指针:
IDirect3DResource9* pSpoof;
DWORD dwSize = sizeof(pSpoof);
hr = pReal->GetPrivateData(IID_Spoof, (void*) &pSpoof, &dwSize);
为什么定义子集后,ID3DXMesh 对象的呈现速度会显著下降?
定义面属性后,可能尚未优化网格。 如果指定属性,然后调用 ID3DXMesh::D rawSubset(),此方法必须针对包含所请求属性的所有面执行网格搜索。 此外,呈现的喜欢可能采用随机访问模式,因此不会利用顶点缓存。 定义子集的面属性后,调用 ID3DXMesh::Optimize 或 ID3DXMesh::OptimizeInPlace 方法,并指定 D3DXMESHOPT_ATTRSORT 或更强的优化方法。 请注意,为了获得最佳性能,应使用 D3DXMESHOPT_VERTEXCACHE 标志进行优化,这也将为最佳顶点缓存利用率重新排序顶点。 为 D3DX 网格生成的相邻数组具有每个面的三个条目,但某些面可能没有所有三个边缘的相邻面。 此编码方式是什么? 没有相邻面的条目将编码为 0xffffffff。
我听到了很多关于预计算辐射传输 (PRT) 的信息,我可以在哪里了解详细信息?
PRT 是 D3DX 在 2003 年夏季 SDK 更新中添加的一项新功能。 它支持实时呈现复杂的照明方案,例如全局 - 照明、软阴影和子表面散点。 SDK 包含有关如何将技术集成到游戏中的文档和示例。 PRT 演示示例和 LocalDeformablePRT 示例演示如何分别对每个顶点和每个像素照明方案使用模拟器。 有关此主题和其他主题的详细信息,也可在 Peter Pike Sloan 的网页上找到。
如何呈现到纹理并使用抗锯齿?
使用 Direct3DDevice9::CreateRenderTarget 创建多采样呈现目标。 将场景呈现到该呈现目标后,StretchRect 从该场景呈现到呈现目标纹理。 如果对屏幕外文本进行任何更改(如模糊或开花),请将其复制回后台缓冲区,然后再 present()。
DirectSound 问题
为什么在应用程序启动时出现静态突发? 我注意到其他应用程序也存在此问题。
你可能安装了调试 DirectX 运行时。 运行时的调试版本使用静态填充缓冲区,以帮助开发人员使用未初始化的缓冲区捕获 bug。 创建后无法保证 DirectSound 缓冲区的内容;具体而言,不能假定有归零的缓冲区。
为什么我在更改效果参数和听到结果之间遇到延迟?
效果参数的更改并不总是在 DirectX 8 上立即发生。 为了提高效率,DirectSound 在缓冲区中处理 100 毫秒的声音数据,从播放光标开始,然后播放缓冲区。 在以下所有调用之后,会发生此预处理:
IDirectSoundBuffer8::SetCurrentPosition
IDirectSoundBuffer8::SetFX
IDirectSoundBuffer8::Stop
IDirectSoundBuffer8::Unlock
从 DirectX 9 起,一种新的处理实时影响的 FX 处理算法用于此问题,并降低了延迟。 该算法已添加到 IDirectSoundBuffer8::Play() 调用中,并添加了一个在写入游标之前处理效果的其他线程。 因此,可以随时设置参数,它们将按预期工作。 但是,请注意,在播放缓冲区上,在听到参数更改之前,会有一个小延迟(通常为 100 毫秒),因为播放和写入游标之间的音频(以及更多填充)当时已处理。
如何检测到是否安装了 DSound?
如果不需要使用 DirectSoundEnumerate() 列出可用的 DSound 设备,请不要将应用程序与 dsound.lib 链接,而是通过 COMs CoCreateInstance(CLSID_DirectSound...) 使用它,然后使用 Initialize(NULL) 初始化 DSound 对象。 如果需要使用 DirectSoundEnumerate(),则可以使用 LoadLibrary("dsound.dll") 动态加载 dsound.dll;并使用 GetProcAddress("DirectSoundEnumerateA/W") 和 GetProcAddress("DirectSoundCreateA/W") 等访问其方法。
如何实现使用 WAVEFORMATEXTENSIBLE 创建多通道音频?
如果在 DirectSound 帮助文件中找不到问题的解答,有一篇好文章,其中包含可在多声道音频数据和 WAVE 文件中获取的详细信息。
如何将 DirectSound 语音管理器与 EAX 等属性集配合使用?
在 DirectSound 9.0 中复制缓冲区时,现在可以在重复缓冲区上获取 IDirectSoundBuffer8 接口,这样就可以访问 AcquireResources 方法。 这样,就可以将缓冲区与硬件资源的 DSBCAPS_LOCDEFER 标志相关联。 然后,可以在此缓冲区上设置 EAX 参数,然后才能调用 Play()。
使用游标位置通知时,我遇到不可靠的行为问题。 如何获取更准确的信息?
各种版本的 DirectSound、核心 Windows 音频堆栈和音频驱动程序存在一些微妙的 bug,这些 bug 使游标位置通知不可靠。 除非以已知通知行为良好的 HW/SW 配置为目标,否则请避免游标位置通知。 对于位置跟踪,GetCurrentPosition() 是一种更安全的技术。
使用 GetCurrentPosition 时,我遇到性能下降的问题。 如何提高性能?
每个缓冲区上的每个 GetCurrentPosition() 调用都会导致系统调用,并且系统调用应最小化,因为它们是 DSound CPU 占用的很大组成部分。 在 NT(Win2K 和 XP)上,SW 缓冲区(和某些设备上的 HW 缓冲区)中的游标以 10 毫秒的增量移动,因此每 10 毫秒调用 GetCurrentPosition() 是理想的。 调用频率高于每 5 毫秒会导致一些性能下降。
我的 DirectSound 应用程序占用过多的 CPU 时间或性能缓慢。 是否可以执行任何操作来优化代码?
可以执行多项操作来提高音频代码的性能:
不要过于频繁地调用 GetCurrentPosition。 每个缓冲区上的每个 GetCurrentPosition() 调用都会导致系统调用,并且系统调用应最小化,因为它们是 DSound CPU 占用的很大组成部分。 在 NT(Win2K 和 XP)上,SW 缓冲区(和某些设备上的 HW 缓冲区)中的游标以 10 毫秒的增量移动,因此每 10 毫秒调用 GetCurrentPosition() 是理想的。 调用频率高于每 5 毫秒会导致一些性能下降。
对音频使用单独的较低帧速率。 如今,许多 Windows 游戏每秒可以超过 100 帧,在大多数情况下,不必以相同的帧速率更新 3D 音频参数。 每秒或第三个图形帧(或每 30 毫秒左右)处理音频可以显著减少整个应用程序中的音频呼叫数,而不会降低音频质量。
对 3D 对象使用 DS3D_DEFERRED。 大多数声卡会立即响应参数更改,在单个帧中可能会发生很大变化,尤其是在更改侦听器的位置或方向时。 这会导致声卡/CPU 执行许多不必要的计算,因此另一个快速和通用的优化是延迟某些参数更改,并在帧末尾提交它们。
或至少使用 SetAllParameters,而不是缓冲区上的单个 Set3DParamX 调用。
同样,应该至少在 3D 缓冲区上使用 SetAllParamenters 调用,而不是单独的 Set3DParamX 调用。 只需尽量减少系统调用。
不要进行冗余调用;存储和排序播放调用的列表。 通常,在一个音频更新帧中,有 2 个请求播放新声音。 如果请求在到达时得到处理,则可以启动第一个新声音,然后立即替换第二个请求的声音。 这会导致冗余计算、不必要的播放调用和不必要的停止调用。 最好存储要播放的新声音的请求列表,以便对列表进行排序,并且实际上只会播放那些应该开始播放的语音。
此外,还应为每个声音源存储 3D 和 EAX 参数的本地副本。 如果请求将参数设置为特定值,则可以查看该值是否与最后一个值集实际不同。 如果不是,则无需进行调用。
尽管声音卡驱动程序可能会检测到此方案,并且不会再次执行(相同)计算,但音频呼叫必须到达音频驱动程序(通过环形转换),这已经是一个缓慢的操作。
流式传输缓冲区时,它往往会出现故障,性能不佳。 流式传输缓冲区的最佳方式是什么?
将音频流式传输到缓冲区时,有两种基本算法:“写后游标” (AWC) 和“播放前游标” (BPC)。 AWC 以故障为代价将延迟降到最低,而 BPC 则相反。 由于通常不会对流式传输的声音进行交互式更改,因此对于游戏和类似的应用程序来说,这种延迟很少出现问题,因此 BPC 是更合适的算法。 在 AWC 中,每次流式处理线程在循环缓冲区中“充值”数据时,其写入游标(通常为 N=40 左右,允许 Windows 计划抖动)中的数据最多为 N 毫秒。 在 BPC 中,始终尽可能多地将数据写入缓冲区,将其填满到播放游标(或可能为 32 字节之前,允许错误地报告其播放游标进度的驱动程序)。
使用 BPC 模拟故障,并使用缓冲区 100 毫秒或更大的缓冲区,即使游戏在测试硬件上没有故障,它也会在一些计算机上出现故障。
我经常一遍又一遍地播放相同的声音,有时不能正常播放,或者 Play() 呼叫需要很长时间。 应采取何种操作?
启动延迟(与上面提及流式处理延迟不同)可能是某些硬件的问题(Play() 调用有时在某些声音卡上花费很长时间)。 如果真的想降低这种延迟,对于 twitch 声音(枪声、脚步声等),一个方便的技巧是保持一些缓冲区始终循环和播放沉默。 当需要播放 twitch 声音时,请选择一个可用缓冲区,查看其写入光标的位置,并将声音放入缓冲区中,仅超出写入光标。 某些声音卡对于我知道它们支持的延迟属性的 QuerySupport 失败。 有解决方法吗? 只能 QuerySupport 属性的非延迟版本,并使用延迟设置。 最新的声音卡驱动程序也可能解决此问题。
如何实现将 WAV 文件编码为 WMA?
请参阅 Windows Media Encoder 上的文档:Windows Media Encoder 9 系列。
如何使用 DirectSound 解码 MP3 文件?
DirectSound 本身不支持 MP3 解码。 可以提前解码文件(使用 DirectShow 筛选器的 ACM 编解码器),或者只需使用 DirectShow 本身即可为你执行解码;然后可以将生成的 PCM 音频数据复制到 DirectSound 缓冲区中。
别名 Maya 的 DirectX 扩展
为什么我的 NURBS 未显示?
不支持 NURBS。 可以将它们转换为多边形网格。
为什么我的 SUBD 未显示?
不支持 SUBD。 可以将它们转换为多边形网格。
为什么 X 文件中的动画与预览窗口中的动画不同?
预览窗口在最严格的意义上不进行动画处理。 它不是播放动画,而是同步到 Maya 场景的最新状态。 导出动画时,每个转换处的矩阵将分解为缩放、旋转(四元数)和翻译组件(通常称为 SRT)。 SRT 比矩阵更理想,因为它们内插良好,提供更紧凑的数据形式,并且可以独立压缩。 并非所有矩阵都可以分解为 SRT。 如果它们无法分解,生成的 SRT 将未知,因此可能会检测到动画中的小错误。 Maya 的两个特征最常导致分解过程中的问题,它们是剪裁和中心外旋转或刻度。 如果遇到此问题,因为使用的是中心外旋转或缩放,请考虑添加增加层次结构级别的其他转换。
D3DX 动画支持 SRT 的情况如下所示:
[S]x[R]x[T]
Maya 的矩阵要复杂得多,需要大量的附加过程,如下所示:
[SpInv]x[S]x[Sh]x[Sp]x[St]x[RpInv]x[Ro]x[R]x[Rp]x[Rt]x[T]
我用 RigidSkin 设置了我的网格,但网格(或部分)没有移动。 为什么?
Maya 的刚性皮肤目前不受支持。 请使用平滑皮肤。
我的 IK 在 X 文件中的哪个位置?
X 文件不支持 IK。 相反,IK 解决方案将烘焙到存储在 X 文件中的帧中。
为什么除了 DirectXShaders 之外,我的材料颜色都不显示?
Maya 的 DirectX 扩展目前仅支持用于预览和导出的 DirectXShader 材料。 在将来的版本中,其他材料可能受支持。
XInput 问题
是否可以使用 DirectInput 读取触发器?
是的,但它们充当同一轴。 因此不能使用 DirectInput 独立读取触发器。 使用 XInput,触发器返回单独的值。
有关 DirectInput 为何将触发器解读为一个轴的详细信息,请参阅将控制器与 DirectInput 配合使用。
XInput 支持多少个控制器?
XInput 支持一次插入 4 个控制器。
XInput 是否支持非通用控制器?
不,不行。
常见控制器是否可通过 DirectInput 使用?
是的,可以通过 DirectInput 访问常见控制器。
如何获取有关常见控制器的强制反馈?
使用 XInputSetState 函数。
为什么我的默认音频设备会更改?
连接头戴显示设备时,控制器的耳机充当标准 USB 音频设备,因此当设备连接时,Windows 会自动更改为将此 USB 音频设备用作默认设备。 由于用户可能不希望所有音频都通过耳机,因此他们需要手动将其调整回原始设置。
如何控制控制器上的灯?
控制器上的灯由操作系统预先确定,无法更改。
如何访问应用程序中的 Xbox 360 按钮?
很抱歉,此按钮保留供将来使用。
在哪里获取驱动程序?
驱动程序将通过Windows 更新提供。
控制器 ID 如何确定?
在 XInput 启动时,ID 由 XInput 引擎和插入的控制器非确定性地确定。 如果在 XInput 应用程序运行时插入控制器,系统将为新控制器分配最低可用数量。 如果控制器断开连接,则其编号将再次可用。
如何实现获取控制器的音频设备?
使用 XInputGetDSoundAudioDeviceGuids 函数。 有关详细信息,请参阅 AudioController 示例。
当控制器被拔下时,我该怎么办?
如果控制器由玩家使用,则应暂停游戏,直到控制器重新连接,并且玩家按下一个按钮来指示他们已准备好取消暂停。