问题描述
我们遇到了一个问题,即旧的封闭源代码游戏引擎在内存接近2GB时无法编译着色器。
问题通常出在D3DXCreateEffect
上。通常,它会返回“内存不足”的HResult,有时d3dx9_25.dll
会在弹出窗口中打印随机错误,或者仅显示段错误。
我认为问题出在缺乏大型地址意识:我注意到d3dx9_25.dll
崩溃之一发生了某种暗示的事情。它使用了一个看起来像0x8xxxxxx3
的有效指针,检查了0x80000003
位是否点亮,如果是,则它位反转并取消引用该指针。结果指针指向未分配的内存。在编译之前强制引擎分配2GB内存会使着色器每次都无法编译。
不幸的是,我们对DX9的了解非常有限,我已经看到DX9带有标志D3DXCONSTTABLE_LARGEADDRESSAWARE
,但是我不确定它到底应该去哪里。我可以找到的唯一依赖于该游戏的API调用是D3DXGetShaderConstantTable
,但问题是在调用之前就发生了。将标志(1 << 17) = 0x20000
注入D3DXCreateEffect
会使着色器以另一种方式编译失败。
-
D3DXCreateEffect
是否应该接受大地址感知标志?我发现使用了wine test,但是深入DX9程序集时,它引发的错误是由内部函数在标志中FFFFF800
中的任何一位被置位时返回HResult Invalid Call引起的,这导致我认为CreateEffect
不应该接受此标志。 -
在此之前,我还有其他地方应该插入大地址感知标志吗?我了解对
D3DXGetShaderConstantTable
的调用需要固定才能使用D3DXGetShaderConstantTableEx
,但尚未实现。
解决方法
LargeAddressAware有点黑,因此它可能会或可能不会对您的情况有所帮助。确实只有在您的应用程序需要多一点空间来接近2GB VA时才有帮助,如果需要更多空间,则无济于事。
旧版DirectX SDK Direct3D 9时代效果系统的一个关键问题是,它假定效果“句柄”的高位是免费的,因此可以使用它,而没有一点点,句柄就是字符串的地址。对于LargeAddressAware,此假设是不正确的。
要启用此功能,请在包含D3DXFX_LARGEADDRESS_HANDLE
标头之前定义d3dx9.h
。然后,在创建所有效果时必须使用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内存布局可以使分配更均匀,也可以有所帮助。
事后看来,CreateEffect
不接受LargeAddressAware
标志的问题非常明显,引擎使用的dx9版本(d3dx9_25.dll
)尚不具备此功能
除了优化内存使用以外,我们的选择还有:
-
将所有像素着色器1.x转换为2.0,并强制引擎加载较新版本的d3dx9,希望引擎不要依赖
d3dx9_25.dll
的错误或别名技巧,然后注入LargeAddressAware标志位在那里。 -
包装malloc,要么避免给句柄提供大地址(我不确定dll中是否也需要这样做),要么将足够的 other 数据保留在大地址中,以便与dx9相关的malloc没达到。