Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=44863
The bug has been fixed already by moving ddraw4 vertex buffers into system memory. Changing this value makes the game create a smaller buffer, which makes the game fast on video memory buffers. I think we should stay close to what Windows drivers report even though we mitigated the original issue in a different way.
From: Stefan Dösinger stefan@codeweavers.com
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=44863
The bug has been fixed already by moving ddraw4 vertex buffers into system memory. Changing this value makes the game create a smaller buffer, which makes the game fast on video memory buffers. I think we should stay close to what Windows drivers report even though we mitigated the original issue in a different way. --- dlls/ddraw/ddraw.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/dlls/ddraw/ddraw.c b/dlls/ddraw/ddraw.c index 5cea4677439..150a2d48613 100644 --- a/dlls/ddraw/ddraw.c +++ b/dlls/ddraw/ddraw.c @@ -1231,8 +1231,32 @@ void ddraw_d3dcaps1_from_7(D3DDEVICEDESC *caps1, D3DDEVICEDESC7 *caps7) caps1->dpcTriCaps = caps7->dpcTriCaps; caps1->dwDeviceRenderBitDepth = caps7->dwDeviceRenderBitDepth; caps1->dwDeviceZBufferBitDepth = caps7->dwDeviceZBufferBitDepth; + + /* This value is zero on all Windows drivers we have seen so far. */ caps1->dwMaxBufferSize = 0; - caps1->dwMaxVertexCount = 65536; + + /* Windows XP and newer report 1024 for HAL devices. Windows 9x + * drivers report higher values - we have seen 2048 on AMD cards + * and 32768 on Nvidia. The API defined limit is 65536 because + * ddraw only supports 16 bit indices. + * + * Some games, for example Prince of Persia 3D (bug 44863), create a + * vertex buffer to hold as many vertices as we report here. The + * game requests a video memory buffer, maps it, writes a handful + * of vertices and draws. Because IDirect3DVertexBuffer::Lock does + * not allow passing a range, and prior to d3d7 doesn't support + * NOOVERWRITE/DISCARD, the upload becomes slow if the buffer is too + * large. So we don't want to report a value that's too high here. + * + * Regardless of this value, we put ddraw4 vertex buffers into + * system memory because games are using them poorly. This mitigates + * negative effects of a large vertex count. + * + * Windows doesn't enforce this limit. A larger vertex buffer can + * be created and used in draws. More vertices than dwMaxVertexCount + * can be rendered in one draw call. */ + caps1->dwMaxVertexCount = 2048; + caps1->dwMinTextureWidth = caps7->dwMinTextureWidth; caps1->dwMinTextureHeight = caps7->dwMinTextureHeight; caps1->dwMaxTextureWidth = caps7->dwMaxTextureWidth;
From: Stefan Dösinger stefan@codeweavers.com
I am not adding tests for creating larger buffers or drawing more vertices because the behavior of my XP machine doesn't make much sense. It allows larger buffers and larger draws, but if I draw from a large buffer I get E_OUTOFMEMORY, even if I draw only 4 vertices. The limit is slightly above 1024 vertices, so I don't think it has a causal relationship with dwMaxVertexCount. --- dlls/ddraw/tests/ddraw1.c | 23 +++++++++++++++++++++++ dlls/ddraw/tests/ddraw2.c | 23 +++++++++++++++++++++++ dlls/ddraw/tests/ddraw4.c | 23 +++++++++++++++++++++++ 3 files changed, 69 insertions(+)
diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c index 96047744635..0b99b65cc4a 100644 --- a/dlls/ddraw/tests/ddraw1.c +++ b/dlls/ddraw/tests/ddraw1.c @@ -15336,6 +15336,13 @@ static HRESULT WINAPI test_enum_devices_caps_callback(GUID *guid, char *device_d "RGB Device hal device caps has D3DDEVCAPS_HWRASTERIZATION set\n"); ok((hel->dwDevCaps & D3DDEVCAPS_HWRASTERIZATION) == 0, "RGB Device hel device caps has D3DDEVCAPS_HWRASTERIZATION set\n"); + + ok(!hal->dwMaxBufferSize, "RGB Device hal caps has dwMaxBufferSize %#lx\n", hal->dwMaxBufferSize); + ok(!hel->dwMaxBufferSize, "RGB Device hel caps has dwMaxBufferSize %#lx\n", hel->dwMaxBufferSize); + todo_wine ok(!hal->dwMaxVertexCount, "RGB Device hal caps has dwMaxVertexCount %#lx\n", + hal->dwMaxVertexCount); + todo_wine ok(hel->dwMaxVertexCount == 2021, "RGB Device hel caps has dwMaxVertexCount %#lx\n", + hel->dwMaxBufferSize); } else if(IsEqualGUID(&IID_IDirect3DHALDevice, guid)) { @@ -15353,6 +15360,15 @@ static HRESULT WINAPI test_enum_devices_caps_callback(GUID *guid, char *device_d "HAL Device hal device caps does not have D3DDEVCAPS_DRAWPRIMITIVES2EX set\n"); ok((hel->dwDevCaps & D3DDEVCAPS_DRAWPRIMITIVES2EX) == 0, "RGB Device hel device caps has D3DDEVCAPS_DRAWPRIMITIVES2EX set\n"); + + ok(!hal->dwMaxBufferSize, "HAL Device hal caps has dwMaxBufferSize %#lx\n", hal->dwMaxBufferSize); + ok(!hel->dwMaxBufferSize, "HAL Device hel caps has dwMaxBufferSize %#lx\n", hel->dwMaxBufferSize); + /* D3DDD_MAXVERTEXCOUNT is not set in dwFlags despite dwMaxVertexCount containing a sensible + * value. */ + ok(hal->dwMaxVertexCount >= 1024 && hal->dwMaxVertexCount <= 32768, + "HAL Device hal caps has dwMaxVertexCount %#lx\n", hal->dwMaxVertexCount); + ok(hel->dwMaxVertexCount >= 1024 && hel->dwMaxVertexCount <= 32768, + "HAL Device hel caps has dwMaxVertexCount %#lx\n", hel->dwMaxVertexCount); } else if(IsEqualGUID(&IID_IDirect3DRefDevice, guid)) { @@ -15418,6 +15434,13 @@ static HRESULT WINAPI test_enum_devices_caps_callback(GUID *guid, char *device_d "Ramp Device hal device caps has D3DDEVCAPS_DRAWPRIMITIVES2EX set\n"); ok((hel->dwDevCaps & D3DDEVCAPS_DRAWPRIMITIVES2EX) == 0, "Ramp Device hel device caps has D3DDEVCAPS_DRAWPRIMITIVES2EX set\n"); + + ok(!hal->dwMaxBufferSize, "Ramp Device hal caps has dwMaxBufferSize %#lx\n", hal->dwMaxBufferSize); + ok(!hel->dwMaxBufferSize, "Ramp Device hel caps has dwMaxBufferSize %#lx\n", hel->dwMaxBufferSize); + todo_wine ok(!hal->dwMaxVertexCount, "Ramp Device hal caps has dwMaxVertexCount %#lx\n", + hal->dwMaxVertexCount); + todo_wine ok(hel->dwMaxVertexCount == 2021, "Ramp Device hel caps has dwMaxVertexCount %#lx\n", + hel->dwMaxBufferSize); } else if(IsEqualGUID(&IID_IDirect3DMMXDevice, guid)) { diff --git a/dlls/ddraw/tests/ddraw2.c b/dlls/ddraw/tests/ddraw2.c index 3d4a0a26522..36acf14c4a9 100644 --- a/dlls/ddraw/tests/ddraw2.c +++ b/dlls/ddraw/tests/ddraw2.c @@ -16316,6 +16316,13 @@ static HRESULT WINAPI test_enum_devices_caps_callback(GUID *guid, char *device_d "RGB Device hal device caps has D3DDEVCAPS_HWRASTERIZATION set\n"); ok((hel->dwDevCaps & D3DDEVCAPS_HWRASTERIZATION) == 0, "RGB Device hel device caps has D3DDEVCAPS_HWRASTERIZATION set\n"); + + ok(!hal->dwMaxBufferSize, "RGB Device hal caps has dwMaxBufferSize %#lx\n", hal->dwMaxBufferSize); + ok(!hel->dwMaxBufferSize, "RGB Device hel caps has dwMaxBufferSize %#lx\n", hel->dwMaxBufferSize); + todo_wine ok(!hal->dwMaxVertexCount, "RGB Device hal caps has dwMaxVertexCount %#lx\n", + hal->dwMaxVertexCount); + todo_wine ok(hel->dwMaxVertexCount == 2021, "RGB Device hel caps has dwMaxVertexCount %#lx\n", + hel->dwMaxBufferSize); } else if(IsEqualGUID(&IID_IDirect3DHALDevice, guid)) { @@ -16333,6 +16340,15 @@ static HRESULT WINAPI test_enum_devices_caps_callback(GUID *guid, char *device_d "HAL Device hal device caps does not have D3DDEVCAPS_DRAWPRIMITIVES2EX set\n"); ok((hel->dwDevCaps & D3DDEVCAPS_DRAWPRIMITIVES2EX) == 0, "RGB Device hel device caps has D3DDEVCAPS_DRAWPRIMITIVES2EX set\n"); + + ok(!hal->dwMaxBufferSize, "HAL Device hal caps has dwMaxBufferSize %#lx\n", hal->dwMaxBufferSize); + ok(!hel->dwMaxBufferSize, "HAL Device hel caps has dwMaxBufferSize %#lx\n", hel->dwMaxBufferSize); + /* D3DDD_MAXVERTEXCOUNT is not set in dwFlags despite dwMaxVertexCount containing a sensible + * value. */ + ok(hal->dwMaxVertexCount >= 1024 && hal->dwMaxVertexCount <= 32768, + "HAL Device hal caps has dwMaxVertexCount %#lx\n", hal->dwMaxVertexCount); + ok(hel->dwMaxVertexCount >= 1024 && hel->dwMaxVertexCount <= 32768, + "HAL Device hel caps has dwMaxVertexCount %#lx\n", hel->dwMaxVertexCount); } else if(IsEqualGUID(&IID_IDirect3DRefDevice, guid)) { @@ -16398,6 +16414,13 @@ static HRESULT WINAPI test_enum_devices_caps_callback(GUID *guid, char *device_d "Ramp Device hal device caps has D3DDEVCAPS_DRAWPRIMITIVES2EX set\n"); ok((hel->dwDevCaps & D3DDEVCAPS_DRAWPRIMITIVES2EX) == 0, "Ramp Device hel device caps has D3DDEVCAPS_DRAWPRIMITIVES2EX set\n"); + + ok(!hal->dwMaxBufferSize, "Ramp Device hal caps has dwMaxBufferSize %#lx\n", hal->dwMaxBufferSize); + ok(!hel->dwMaxBufferSize, "Ramp Device hel caps has dwMaxBufferSize %#lx\n", hel->dwMaxBufferSize); + todo_wine ok(!hal->dwMaxVertexCount, "Ramp Device hal caps has dwMaxVertexCount %#lx\n", + hal->dwMaxVertexCount); + todo_wine ok(hel->dwMaxVertexCount == 2021, "Ramp Device hel caps has dwMaxVertexCount %#lx\n", + hel->dwMaxBufferSize); } else if(IsEqualGUID(&IID_IDirect3DMMXDevice, guid)) { diff --git a/dlls/ddraw/tests/ddraw4.c b/dlls/ddraw/tests/ddraw4.c index a0e4fe18c31..cf7812e54ab 100644 --- a/dlls/ddraw/tests/ddraw4.c +++ b/dlls/ddraw/tests/ddraw4.c @@ -19389,6 +19389,13 @@ static HRESULT WINAPI test_enum_devices_caps_callback(GUID *guid, char *device_d "RGB Device hal device caps has D3DDEVCAPS_HWRASTERIZATION set\n"); ok((hel->dwDevCaps & D3DDEVCAPS_HWRASTERIZATION) == 0, "RGB Device hel device caps has D3DDEVCAPS_HWRASTERIZATION set\n"); + + ok(!hal->dwMaxBufferSize, "RGB Device hal caps has dwMaxBufferSize %#lx\n", hal->dwMaxBufferSize); + ok(!hel->dwMaxBufferSize, "RGB Device hel caps has dwMaxBufferSize %#lx\n", hel->dwMaxBufferSize); + todo_wine ok(!hal->dwMaxVertexCount, "RGB Device hal caps has dwMaxVertexCount %#lx\n", + hal->dwMaxVertexCount); + todo_wine ok(hel->dwMaxVertexCount == 2021, "RGB Device hel caps has dwMaxVertexCount %#lx\n", + hel->dwMaxBufferSize); } else if(IsEqualGUID(&IID_IDirect3DHALDevice, guid)) { @@ -19406,6 +19413,15 @@ static HRESULT WINAPI test_enum_devices_caps_callback(GUID *guid, char *device_d "HAL Device hal device caps does not have D3DDEVCAPS_DRAWPRIMITIVES2EX set\n"); ok((hel->dwDevCaps & D3DDEVCAPS_DRAWPRIMITIVES2EX) == 0, "RGB Device hel device caps has D3DDEVCAPS_DRAWPRIMITIVES2EX set\n"); + + ok(!hal->dwMaxBufferSize, "HAL Device hal caps has dwMaxBufferSize %#lx\n", hal->dwMaxBufferSize); + ok(!hel->dwMaxBufferSize, "HAL Device hel caps has dwMaxBufferSize %#lx\n", hel->dwMaxBufferSize); + /* D3DDD_MAXVERTEXCOUNT is not set in dwFlags despite dwMaxVertexCount containing a sensible + * value. */ + ok(hal->dwMaxVertexCount >= 1024 && hal->dwMaxVertexCount <= 32768, + "HAL Device hal caps has dwMaxVertexCount %#lx\n", hal->dwMaxVertexCount); + ok(hel->dwMaxVertexCount >= 1024 && hel->dwMaxVertexCount <= 32768, + "HAL Device hel caps has dwMaxVertexCount %#lx\n", hel->dwMaxVertexCount); } else if(IsEqualGUID(&IID_IDirect3DRefDevice, guid)) { @@ -19471,6 +19487,13 @@ static HRESULT WINAPI test_enum_devices_caps_callback(GUID *guid, char *device_d "Ramp Device hal device caps has D3DDEVCAPS_DRAWPRIMITIVES2EX set\n"); ok((hel->dwDevCaps & D3DDEVCAPS_DRAWPRIMITIVES2EX) == 0, "Ramp Device hel device caps has D3DDEVCAPS_DRAWPRIMITIVES2EX set\n"); + + ok(!hal->dwMaxBufferSize, "Ramp Device hal caps has dwMaxBufferSize %#lx\n", hal->dwMaxBufferSize); + ok(!hel->dwMaxBufferSize, "Ramp Device hel caps has dwMaxBufferSize %#lx\n", hel->dwMaxBufferSize); + todo_wine ok(!hal->dwMaxVertexCount, "Ramp Device hal caps has dwMaxVertexCount %#lx\n", + hal->dwMaxVertexCount); + todo_wine ok(hel->dwMaxVertexCount == 2021, "Ramp Device hel caps has dwMaxVertexCount %#lx\n", + hel->dwMaxBufferSize); } else if(IsEqualGUID(&IID_IDirect3DMMXDevice, guid)) {
Except that, as mentioned in 2404, I do have a Geforce 4 MX (NV17) which reports 32768 here. Granted, I haven't actually tried a draw that large...
Yeah but I don't think we need to report the highest value found. It seems Windows gets away with 1024, but to play it safe I've picked the lowest on contemporaneous hardware, which is still higher than what Windows uses since 20 years.
I wonder if the value Windows reports has any correlation to D3DDEVCAPS_DRAWPRIMITIVES2EX or D3DDEVCAPS_DRAWPRIMITIVES2. It might be that the 1024 comes from a runtime provided emulation of old draws based on the newer driver interfaces. There's a test in test_enum_devices_caps_callback ("HAL Device hal device caps does not have D3DDEVCAPS_DRAWPRIMITIVES2EX set") - does it fail on HW that reports something bigger than 1024?
It is also possible that this value only has a meaning for execute buffers, i.e. D3DEXECUTEDATA::dwVertexCount.
Hmm, fair enough, though it's also worth pointing out that that's the only card I have that supports hardware T&L. I don't know what the other caps on those cards are (I don't have access to the machine at this moment but I can check in a couple weeks).
If I remember right you reported that the game ran poorly on that Nvidia card - though I am not sure if you linked that to the driver's handling of ddraw4 buffers. If my memory is correct here I wager that it has more to do with the fact that the game creates a 1 MB buffer than how the driver handles the buffer.
If I remember right you reported that the game ran poorly on that Nvidia card - though I am not sure if you linked that to the driver's handling of ddraw4 buffers. If my memory is correct here I wager that it has more to do with the fact that the game creates a 1 MB buffer than how the driver handles the buffer.
I believe the opposite is correct actually. The standalone tests I ran for VB performance used a fixed size buffer of 800 vertices, and that also performed very badly with the NVidia card when using the same pattern as the game did (namely, no discard/nooverwrite, since ddraw4.) So even if the driver reported a lower dwMaxVertexCount value, the game would still perform badly.