【问题标题】:Converting a 32bit directx9 app to be large address aware将 32 位 directx9 应用程序转换为大地址感知
【发布时间】:2021-01-26 20:00:26
【问题描述】:

当内存接近 2GB 时,我们遇到了旧的闭源游戏引擎无法编译着色器的问题。

问题通常出在D3DXCreateEffect。通常它会返回“内存不足”的 HResult,有时d3dx9_25.dll 在弹出窗口中打印随机错误,或者它只是彻底的段错误。

我认为问题在于缺乏大地址意识:我注意到其中一个 d3dx9_25.dll 崩溃正在做一些暗示这样的事情。它采用了一个看起来像 0x8xxxxxx3 的有效指针,检查了位 0x80000003 是否被点亮,如果是,它位反转指针并取消引用它。结果指针指向未分配的内存。在编译之前强制引擎 malloc 2GB 使得着色器每次都无法编译。

不幸的是,我们对 DX9 的了解非常有限,我看到 DX9 有一个标志 D3DXCONSTTABLE_LARGEADDRESSAWARE,但我不确定它到底应该去哪里。我能找到的游戏使用的唯一 API 调用依赖于它是 D3DXGetShaderConstantTable,但问题发生在它被调用之前。将标志(1 << 17) = 0x20000 注入D3DXCreateEffect 会使着色器以另一种方式编译失败。

  1. D3DXCreateEffect 是否应该接受大地址感知标志?我发现了一个wine test 使用它,但是深入研究DX9 程序集,它抛出的错误是由一个内部函数在设置标志中FFFFF800 中的任何位时返回HResult Invalid Call 引起的,这让我相信CreateEffect不应该接受这个标志。

  2. 在此之前我还应该在其他任何地方注入大地址感知标志吗?我知道需要修复对D3DXGetShaderConstantTable 的调用才能使用D3DXGetShaderConstantTableEx,但它甚至还没有到达。

【问题讨论】:

  • 您考虑过迁移吗?听起来您已经到了引擎可以合理支持的极限了。
  • 我认为你搞错了。默认情况下,Windows 会安全播放它,并且不会将 >2GB 的指针传递给旧应用程序。这样,像指针否定之类的技巧的旧应用程序将继续工作。 “大地址感知”是一个标志,告诉 Windows“我没有做任何奇怪的事情,我可以处理 >2GB”。您可以分配 2GB 的事实意味着您的应用声称它是 LAA。
  • 另外,我认为您可能会忽略0x80000003 中的“3”。这暗示了一个未对齐的指针。否定它不会使其对齐,但反转所有位会。
  • x86 neg 是 2 的补码否定(从 0 中减去),C 一元 -。 x86 not 是 1 的补码否定,翻转所有位,C ~。我们称之为按位非,而不是否定,以区别于数学 / 2 的补码否定。
  • 你说得对,我记错了。我再次检查,指令确实是not

标签: c++ x86 direct3d 32-bit directx-9


【解决方案1】:

LargeAddressAware 有点小技巧,所以它可能对您的情况有所帮助,也可能无济于事。只有当您的应用程序需要更多接近 2GB VA 的空间时,它才真正有帮助,如果需要更多空间,则无济于事。

旧版 DirectX SDK Direct3D 9 时代效果系统的一个关键问题是它假定效果“句柄”的高位是空闲的,因此它可以使用它,而没有该位的句柄是一个字符串的地址.对于 LargeAddressAware,这个假设不成立。

要启用此功能,请在包含 d3dx9.h 标头之前定义 D3DXFX_LARGEADDRESS_HANDLE。然后,您必须在创建所有效果时使用D3DXFX_LARGEADDRESSAWARE 标志。您必须不要使用别名技巧,您可以在所有效果方法上使用“字符串名称”而不是“句柄”。相反,您必须使用 GetParameterByName 来获取句柄并使用它。

我不记得什么时候 LAA 标志被添加到 Direct3D 9 的效果中。

如果您使用的是d3dx9_25.dll,那么这就是 2005 年 4 月发布的 DirectX SDK。如果您使用的是“Pixel Shader Model 1.x”,那么您不能使用比d3dx9_31.dll(2006 年 10 月)更新的任何版本--更高版本的 DirectX SDK 允许您使用刚刚通过着色器编译的 D3DXSHADER_USE_LEGACY_D3DX9_31_DLL此场景的旧版本。

许多 32 位游戏失败并在启用 LAA 的情况下运行的一个关键原因是虚拟内存碎片。改进您的 VA 内存布局可以使您的分配更加统一也有帮助。

【讨论】:

  • LargeAddressAware 将在 x64 上为您提供大约 4GB 的空间,这在当今很常见。
  • 是的,我知道 LAA 可以让您在 Windows x64 上分配多达 4 GB 的 VA——FWIW 我在 2004-2008 年的大部分时间里都在帮助游戏开发者做到这一点。这并不意味着这些传统 32 位游戏中的大多数都能在此附近稳定运行。 Windows x64 上的 LAA 帮助了许多卡在 32 位的游戏,并且只需要在它们达到约 1.7 GB 的峰值时不崩溃。
  • 据我从标题中了解到,D3DXFX_LARGEADDRESS_HANDLE 仅帮助您确保在编译时您没有使用别名技巧,对吗?
  • 是的,D3DXFX_LARGEADDRESS_HANDLE 是在编译时强制执行该行为。
【解决方案2】:

事后看来,CreateEffect 不接受 LargeAddressAware 标志的问题非常明显,引擎使用的 dx9 版本 (d3dx9_25.dll) 根本没有此功能。

我们的选项,除了优化我们的内存使用是:

  1. 将我们所有的像素着色器 1.x 转换为 2.0 并强制引擎加载更新版本的 d3dx9,希望引擎不依赖于 d3dx9_25.dll 的错误或别名技巧,然后在那里注入 LargeAddressAware 标志位。

  2. 包装 malloc,或者避免给句柄大地址(我不确定这是否也需要在 dll 中)或者在大地址中粘贴足够的 other 数据,这样 dx9 相关的 malloc 就不会到达它。

【讨论】:

  • 第三种选择是找到一个不会过时的游戏引擎并将所有内容移植过来;最初听起来可能需要做很多工作;但可以大大改善游戏(性能和功能),并且从长远来看可以节省时间(因为真的,一旦你找到解决这个问题的快速解决方法,你就会遇到另一个你会赢的问题'不能这么容易地修复)。
  • 这不是商业项目。移植价值超过 15 年的内容,由数百名志愿者制作,由少数剩下的人(其中可能是三个软件人员)制作,这对我们来说不是一个选择。
  • 是否可以通过提示将其他内容分配到高 2GiB 的虚拟地址空间中,将低 2GiB 留给 dx9 需要使用的内存?如果您的流程中有任何不需要兼容 DX9 的地址的大型分配,这可能会有所帮助。 (如果 Windows 分配器有任何方法可以做到这一点;在 Linux 上,您将使用 mmap(0x8000000, ... MAP_ANONYMOUS) without MAP_FIXED。但是必须为每次分配手动管理提示地址,没有“高位的任何地方”一半”的提示。
  • 是的,我们已经有了 malloc 的包装器,而且我确实有一个可以解决问题的 POC。目前这个问题只影响极少数人(可能是那些使用不寻常的驱动程序/反病毒软件需要额外内存的人),所以我们正在检查我们是否可以首先欺骗引擎以更动态地加载东西,因为这是一个不那么老套的解决方案。
猜你喜欢
  • 1970-01-01
  • 2010-11-23
  • 2020-09-25
  • 2011-10-27
  • 1970-01-01
  • 2010-09-29
  • 2015-04-05
  • 2011-03-07
  • 2012-10-26
相关资源
最近更新 更多