http://bugs.winehq.org/show_bug.cgi?id=35112
Bug ID: 35112 Summary: L.A. Noire 1.3 (Steam version) fails to start, claiming "DX94: Graphics card capabilities are below the minimum specifications." (wined3d clamps device caps MaxVertexIndex to 0xFFFFF) Product: Wine Version: 1.7.8 Hardware: x86 OS: Linux Status: NEW Severity: normal Priority: P2 Component: directx-d3d Assignee: wine-bugs@winehq.org Reporter: focht@gmx.net Classification: Unclassified
Hello folks,
now with bug 35109 fixed the game runs into next problem.
After the main process 'LANoire.exe' has started an error dialog window is shown, stating:
"Error: DX94: Graphics card capabilities are below the minimum specifications."
It was actually a bit of pain to diagnose/debug because of several processes involved which are (re)started in sequence with the actual timing being important. There are only very small time windows for attaching debuggers, if you miss them -> boom (avoid 'auto' debugging of childs from parent).
Additionally, these processes are a mixture of native binaries, .NET binaries and mixed-mode binaries somewhat complicating diagnosis further (unmanaged code with various managed code transitions in the same process).
Relevant part of trace log, showing processes involved:
NOTE: Don't redirect to files located within Steam folder to avoid unnecessarily triggering the directory watcher thread(s).
--- snip --- $ WINEDEBUG=+tid,+seh,+loaddll,+process,+msvcrt,+d3d9,+relay wine ./steam.exe -applaunch 110800 >>~/Downloads/trace.txt 2>&1 ... 0024:trace:process:__wine_kernel_init starting process name=L"C:\Program Files\Steam\steam.exe" argv[0]=L"C:\Program Files\Steam\steam.exe" ...
0024:Call KERNEL32.CreateProcessW(00b1ed88 L"C:\Program Files\Steam\steamerrorreporter.exe",00000000,00000000,00000000,00000000,00000000,00000000,00b1edf0 L"C:\Program Files\Steam",0033f4e8,0033f52c) ret=10001121 ... 0024:trace:process:create_process_impl starting L"C:\Program Files\Steam\steamerrorreporter.exe" as Win32 binary (0x400000-0x436000, arch 014c) ... 0024:trace:process:create_process_impl started process pid 0025 tid 0026 ...
003f:Call KERNEL32.CreateProcessW(02a54a40 L"C:\Program Files\Steam\steamapps\common\L.A.Noire\LanLauncher.exe",02a54f50 L""C:\Program Files\Steam\steamapps\common\L.A.Noire\LanLauncher.exe"",00000000,00000000,00000000,00000004,00000000,02978818 L"C:\Program Files\Steam\steamapps\common\L.A.Noire",06dcb588,06dcb604) ret=381ec242 ... 003f:trace:process:create_process_impl starting L"C:\Program Files\Steam\steamapps\common\L.A.Noire\LanLauncher.exe" as Win32 binary (0x400000-0x514000, arch 014c) ... 003f:trace:process:create_process_impl started process pid 0056 tid 0057 ...
0057:Call KERNEL32.CreateProcessW(00000000,0033de68 L"C:\Program Files\Steam\steamapps\common\L.A.Noire\LANPatcher.exe",00000000,00000000,00000000,00000404,00000000,044c8b88 L"C:\Program Files\Steam\steamapps\common\L.A.Noire",0033d520,0033d510) ret=100416f7 ... 0057:trace:process:create_process_impl starting L"C:\Program Files\Steam\steamapps\common\L.A.Noire\LANPatcher.exe" as Win32 binary (0x400000-0x4ae000, arch 014c) ... 0057:trace:process:create_process_impl started process pid 005b tid 005c ...
005c:Call KERNEL32.CreateProcessW(00000000,0033dd18 L"C:\Program Files\Steam\steamapps\common\L.A.Noire\LANLauncher.exe -SkipPatchCheck",00000000,00000000,00000000,00000404,00000000,04497e78 L"C:\Program Files\Steam\steamapps\common",0033d3d0,0033d3c0) ret=100416f7 ... 005c:trace:process:create_process_impl starting L"C:\Program Files\Steam\steamapps\common\L.A.Noire\LANLauncher.exe" as Win32 binary (0x400000-0x514000, arch 014c) ... 005c:trace:process:create_process_impl started process pid 0066 tid 0067
0067:Call KERNEL32.CreateProcessW(00000000,0033d8b8 L"C:\Program Files\Steam\steamapps\common\L.A.Noire\LANoire.exe -windowed -parentIsLauncher",00000000,00000000,00000000,00000404,00000000,04531618 L"C:\Program Files\Steam\steamapps\common\L.A.Noire",0033cf70,0033cf60) ret=100416f7 ... 0067:trace:process:create_process_impl starting L"C:\Program Files\Steam\steamapps\common\L.A.Noire\LANoire.exe" as Win32 binary (0x400000-0x184f000, arch 014c) ... 0067:trace:process:create_process_impl started process pid 0026 tid 0025
0067:Call KERNEL32.CreateProcessW(00000000,0033d8b8 L"C:\Program Files\Steam\steamapps\common\L.A.Noire\LANLauncher.exe -SplashScreen Loading",00000000,00000000,00000000,00000404,00000000,00000000,0033cf70,0033cf60) ret=100416f7 ... 0067:trace:process:create_process_impl starting L"C:\Program Files\Steam\steamapps\common\L.A.Noire\LANLauncher.exe" as Win32 binary (0x400000-0x514000, arch 014c) ... 0067:trace:process:create_process_impl started process pid 004b tid 004d
... 0064:fixme:advapi:CreateProcessAsUserW 0x44c L"C:\Program Files\Rockstar Games\Social Club\renderer.exe" L""C:\Program Files\Rockstar Games\Social Club\renderer.exe" --type=renderer --disable-logging --disable-desktop-notifications --disable-speech-input --enable-accelerated-compositing --lang=en-US --channel=38.0F96F300.97936503 /prefetch:3" (nil) (nil) 0 0x0100040c (nil) (null) 0x322ae14c 0x322ae134 - semi- stub ... 0064:trace:process:create_process_impl started process pid 006a tid 006b ... --- snip ---
After dumping, rebuilding and analysing the main executable + trace log for some hours I finally deduced the place where the problem originated. The d3d calls from trace log are register indirect calls which requires a debugger to identify the actual call site at run time. Additionally some of them are hooked at through Steam gameoverlay renderer.
--- snip --- ... 0025:trace:d3d9:d3d9_device_GetDeviceCaps iface 0x32977c18, caps 0x332a74. 0025:Call ntdll.RtlAllocateHeap(00110000,00000008,0000017c) ret=f687fa1f 0025:Ret ntdll.RtlAllocateHeap() retval=32fe6448 ret=f687fa1f 0025:Call wined3d.wined3d_mutex_lock() ret=f687fa55 0025:Ret wined3d.wined3d_mutex_lock() retval=00000000 ret=f687fa55 0025:Call wined3d.wined3d_device_get_device_caps(328e2cb8,32fe6448) ret=f687fa6a 0025:Ret wined3d.wined3d_device_get_device_caps() retval=00000000 ret=f687fa6a 0025:Call wined3d.wined3d_mutex_unlock() ret=f687fa72 0025:Ret wined3d.wined3d_mutex_unlock() retval=00000000 ret=f687fa72 0025:Call ntdll.RtlFreeHeap(00110000,00000000,32fe6448) ret=f687ff1c 0025:Ret ntdll.RtlFreeHeap() retval=00000001 ret=f687ff1c 0025:trace:d3d9:d3d9_GetAdapterDisplayMode iface 0x32977990, adapter 0, mode 0x332a64. 0025:Call wined3d.wined3d_mutex_lock() ret=f688af86 0025:Ret wined3d.wined3d_mutex_lock() retval=00000000 ret=f688af86 0025:Call wined3d.wined3d_get_adapter_display_mode(328d9f38,00000000,003329fc,00000000) ret=f688afac 0025:Call user32.EnumDisplaySettingsExW(328dcdf0 L"\\.\DISPLAY1",ffffffff,00332860,00000000) ret=f66ba608 0025:Call winex11.drv.EnumDisplaySettingsEx(328dcdf0,ffffffff,00332860,00000000) ret=7ed181c2 0025:Ret winex11.drv.EnumDisplaySettingsEx() retval=00000001 ret=7ed181c2 0025:Ret user32.EnumDisplaySettingsExW() retval=00000001 ret=f66ba608 0025:Ret wined3d.wined3d_get_adapter_display_mode() retval=00000000 ret=f688afac 0025:Call wined3d.wined3d_mutex_unlock() ret=f688afb5 0025:Ret wined3d.wined3d_mutex_unlock() retval=00000000 ret=f688afb5 0025:trace:d3d9:d3d9_CheckDeviceFormat iface 0x32977990, adapter 0, device_type 0x1, adapter_format 0x16, usage 0x1, resource_type 0x1, format 0x4c4c554e. 0025:Call wined3d.wined3d_mutex_lock() ret=f688b1b6 0025:Ret wined3d.wined3d_mutex_lock() retval=00000000 ret=f688b1b6 0025:Call wined3d.wined3d_check_device_format(328d9f38,00000000,00000001,00000073,00000001,00000001,4c4c554e) ret=f688b202 0025:Ret wined3d.wined3d_check_device_format() retval=00000000 ret=f688b202 0025:Call wined3d.wined3d_mutex_unlock() ret=f688b20b 0025:Ret wined3d.wined3d_mutex_unlock() retval=00000000 ret=f688b20b 0025:trace:d3d9:d3d9_CheckDeviceFormat iface 0x32977990, adapter 0, device_type 0x1, adapter_format 0x16, usage 0x2, resource_type 0x3, format 0x5a544e49. 0025:Call wined3d.wined3d_mutex_lock() ret=f688b1b6 0025:Ret wined3d.wined3d_mutex_lock() retval=00000000 ret=f688b1b6 0025:Call wined3d.wined3d_check_device_format(328d9f38,00000000,00000001,00000073,00000002,00000003,5a544e49) ret=f688b202 0025:Ret wined3d.wined3d_check_device_format() retval=00000000 ret=f688b202 0025:Call wined3d.wined3d_mutex_unlock() ret=f688b20b 0025:Ret wined3d.wined3d_mutex_unlock() retval=00000000 ret=f688b20b 0025:Call KERNEL32.GetLastError() ret=79e7a087 0025:Ret KERNEL32.GetLastError() retval=00000000 ret=79e7a087 ... <several transitions to managed code> ... <huge amount of unrelated trace messages later> ... 0073:Call user32.CreateWindowExW(00000200,0907369c L"WindowsForms10.EDIT.app.0.3ce0bb8",0906fedc L"DX94: Graphics card capabilities are below the minimum specifications.",560108c0,00000005,00000013,0000027d,00000015,00020184,00000000,00400000,00000000) ret=017edcbc --- snip ---
The error dialog window is created from .NET code (windows form), along with some unusable callstack display (another bug). The actual reason for the problem is not visible in trace log, one has to analyse and step the disassembly.
d3d9_device_GetDeviceCaps() returns D3D9 Device Caps structure. The engine code checks several caps struct members to decide if the card is 'good'.
My card 'NVIDIA GeForce GT 425M' was not recognized (bug 35054) hence Wine's default choice was taken: 'Graphics Card: NVIDIA GeForce 8800 GTX'.
The minimum video card requirements for this game (from website): "NVIDIA GeForce 8600 GT 512MB" ... so even with the default (incorrect) choice it should have been 'good'.
Each time a requirement is not met the game internally assigns an error code 'DX<two-digit-code>' which is translated to a more or less generic error message.
DirectX caps checks (in order):
--- snip --- VertexShaderVersion
my raw value: 0xFFFE0300 good value: >= 3.0 (masked out) failure: "DX92: Shadermodel 3.0 required" --- snip ---
--- snip --- PixelShaderVersion
my raw value: 0xFFFF0300 sanitized good value: >= 3.0 (masked out) failure: "DX93: Shadermodel 3.0 required") --- snip ---
Now the important one ...
--- snip --- MaxVertexIndex
my raw value: 0xFFFFF good value: 0x555555 (5592405 decimal) failure: "DX94: Graphics card capabilities are below the minimum specifications." --- snip ---
Wine uses a hard-coded default value.
d3d9_device_GetDeviceCaps -> wined3d_device_get_device_caps -> wined3d_get_device_caps
(filter_caps)
Source: http://source.winehq.org/git/wine.git/blob/34ad4c7014c63a4b4f96f94ba4973831b...
--- snip --- 4100 HRESULT CDECL wined3d_get_device_caps(const struct wined3d *wined3d, UINT adapter_idx, 4101 enum wined3d_device_type device_type, WINED3DCAPS *caps) 4102 { ... 4441 caps->MaxAnisotropy = gl_info->limits.anisotropy; 4442 caps->MaxPointSize = gl_info->limits.pointsize_max; 4443 4444 caps->MaxPrimitiveCount = 0xfffff; /* For now set 2^20-1 which is used by most >=Geforce3/Radeon8500 cards */ 4445 caps->MaxVertexIndex = 0xfffff; 4446 caps->MaxStreams = MAX_STREAMS; 4447 caps->MaxStreamStride = 1024; ... --- snip ---
Increasing the limit to 0x555555 made the game DirectX engine happy :) There were a few other dx caps checks following but those requirements were fulfilled by default.
***
Interesting tidbit:
There exists a kind of hack to overcome the failing dx caps check without modifying Wine code. During dumping/analysis of the main binary I found several 'goodies' -> semi-/undocumented command line parameters.
One of them is '-disablecrashhandler'. You can pass it through launcher 'command line' options dialog.
This basically skips the invocation of the .NET based error handler and lets the game engine code continue as if nothing happened.
Having this kind of 'override' in actual code is usually disastrous. Due to the special way the requirement checks were coded this 'magically' works. Each invocation of error handler leads to a no-return 'continuation' like call. The next prerequisite check immediately follows the no-return call (without branches in between). Disabling the crash handler made the no-return calls basically a no-op hence letting the execution flow 'magically' continue.
***
The game runs further until next problem ... looks like some wined3d volume texture/surface management thing (that area is currently being worked on).
$ wine --version wine-1.7.8-200-gd566292
Regards