[PATCH 1/2] d3d9/tests: Extend the FPU setup test.

Stefan Dösinger stefan at codeweavers.com
Fri Feb 27 03:48:41 CST 2015


Games created by GameMaker modify the FPU control word to enable
division by zero exceptions. The game Risk of Rain, which is written
with GameMaker, furthermore sets fogstart == fogend, which triggers an
exception when the GL driver attempts to calculate 1 / (fogend -
fogstart). This test adds some evidence that we are not supposed to
temporarily restore the FPU CW if D3DCREATE_FPU_PRESERVE is used.

It is theoretically possible that native d3d sets the FPU CW on method
enter, restores it before calling AddRef / Release, then sets it back to
a d3d-defined value when AddRef / Release returns and restores it to
whatever it was set in AddRef / Release return before returning to the
caller of SetPrivateData or Release. Given my experience with
Microsoft's code I don't think this is likely.

On Windows, a floating point exception can be triggered inside
MultiplyTransform by multiplying a matrix with [0][0] = 0.0 with a
matrix that has [0][0] = INF. Similarly an exception can be triggered
inside DrawPrimitive by setting a world and projection matrix such that
a 0.0 * INF multiplication is caused. This is further evidence that the
driver code is executed with the FPU CW set by the application.
Alexandre once said that triggering exceptions in our test is bad, so I
am not adding a test for this.

Actually testing the fogstart == fogend case with division by zero
exceptions enabled is tricky. On Windows this never triggered an
exception in my experiments, even with only one CPU enabled to disable
d3d's multithreading. On Linux it depends on the driver's behavior. The
Nvidia driver catches this specific problem (which is how the entire
issue started, now the fog special tests return the wrong color),
whereas Mesa crashes.
---
 dlls/d3d9/tests/device.c | 96 ++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 88 insertions(+), 8 deletions(-)

diff --git a/dlls/d3d9/tests/device.c b/dlls/d3d9/tests/device.c
index b11d8d45..fde2bb6 100644
--- a/dlls/d3d9/tests/device.c
+++ b/dlls/d3d9/tests/device.c
@@ -4086,6 +4086,46 @@ static inline WORD get_fpu_cw(void)
     return cw;
 }
 
+static WORD callback_cw, callback_set_cw;
+static DWORD callback_tid;
+
+static HRESULT WINAPI dummy_object_QueryInterface(IUnknown *iface, REFIID riid, void **out)
+{
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI dummy_object_AddRef(IUnknown *iface)
+{
+    callback_cw = get_fpu_cw();
+    set_fpu_cw(callback_set_cw);
+    callback_tid = GetCurrentThreadId();
+    return 2;
+}
+
+static ULONG WINAPI dummy_object_Release(IUnknown *iface)
+{
+    callback_cw = get_fpu_cw();
+    set_fpu_cw(callback_set_cw);
+    callback_tid = GetCurrentThreadId();
+    return 1;
+}
+
+static const IUnknownVtbl dummy_object_vtbl =
+{
+    dummy_object_QueryInterface,
+    dummy_object_AddRef,
+    dummy_object_Release,
+};
+
+static const GUID d3d9_private_data_test_guid =
+{
+    0xfdb37466,
+    0x428f,
+    0x4edf,
+    {0xa3,0x7f,0x9b,0x1d,0xf4,0x88,0xc5,0xfc}
+};
+
 static void test_fpu_setup(void)
 {
 #if defined(D3D9_TEST_SET_FPU_CW) && defined(D3D9_TEST_GET_FPU_CW)
@@ -4094,6 +4134,9 @@ static void test_fpu_setup(void)
     HWND window = NULL;
     IDirect3D9 *d3d9;
     WORD cw;
+    IDirect3DSurface9 *surface;
+    HRESULT hr;
+    IUnknown dummy_object = {&dummy_object_vtbl};
 
     window = CreateWindowA("d3d9_test_wc", "d3d9_test", WS_CAPTION, 0, 0,
             registry_mode.dmPelsWidth, registry_mode.dmPelsHeight, 0, 0, 0, 0);
@@ -4120,7 +4163,34 @@ static void test_fpu_setup(void)
     cw = get_fpu_cw();
     ok(cw == 0x7f, "cw is %#x, expected 0x7f.\n", cw);
 
+    hr = IDirect3DDevice9_GetRenderTarget(device, 0, &surface);
+    ok(SUCCEEDED(hr), "Failed to get render target surface, hr %#x.\n", hr);
+
+    callback_set_cw = 0xf60;
+    hr = IDirect3DSurface9_SetPrivateData(surface, &d3d9_private_data_test_guid,
+            &dummy_object, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
+    ok(SUCCEEDED(hr), "Failed to set private data, hr %#x.\n", hr);
+    ok(callback_cw == 0x7f, "Callback cw is %#x, expected 0x7f.\n", callback_cw);
+    ok(callback_tid == GetCurrentThreadId(), "Got unexpected thread id.\n");
+    cw = get_fpu_cw();
+    ok(cw == 0xf60, "cw is %#x, expected 0xf60.\n", cw);
+
+    callback_cw = 0;
+    hr = IDirect3DSurface9_SetPrivateData(surface, &d3d9_private_data_test_guid,
+            &dummy_object, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
+    ok(SUCCEEDED(hr), "Failed to set private data, hr %#x.\n", hr);
+    ok(callback_cw == 0xf60, "Callback cw is %#x, expected 0xf60.\n", callback_cw);
+    ok(callback_tid == GetCurrentThreadId(), "Got unexpected thread id.\n");
+
+    callback_set_cw = 0x7f;
+    set_fpu_cw(0x7f);
+
+    IDirect3DSurface9_Release(surface);
+
+    callback_cw = 0;
     IDirect3DDevice9_Release(device);
+    ok(callback_cw == 0x7f, "Callback cw is %#x, expected 0x7f.\n", callback_cw);
+    ok(callback_tid == GetCurrentThreadId(), "Got unexpected thread id.\n");
 
     cw = get_fpu_cw();
     ok(cw == 0x7f, "cw is %#x, expected 0x7f.\n", cw);
@@ -4134,9 +4204,26 @@ static void test_fpu_setup(void)
 
     cw = get_fpu_cw();
     ok(cw == 0xf60, "cw is %#x, expected 0xf60.\n", cw);
-    set_fpu_cw(0x37f);
 
+    hr = IDirect3DDevice9_GetRenderTarget(device, 0, &surface);
+    ok(SUCCEEDED(hr), "Failed to get render target surface, hr %#x.\n", hr);
+
+    callback_cw = 0;
+    callback_set_cw = 0x37f;
+    hr = IDirect3DSurface9_SetPrivateData(surface, &d3d9_private_data_test_guid,
+            &dummy_object, sizeof(IUnknown *), D3DSPD_IUNKNOWN);
+    ok(SUCCEEDED(hr), "Failed to set private data, hr %#x.\n", hr);
+    ok(callback_cw == 0xf60, "Callback cw is %#x, expected 0xf60.\n", callback_cw);
+    ok(callback_tid == GetCurrentThreadId(), "Got unexpected thread id.\n");
+    cw = get_fpu_cw();
+    ok(cw == 0x37f, "cw is %#x, expected 0x37f.\n", cw);
+
+    IDirect3DSurface9_Release(surface);
+
+    callback_cw = 0;
     IDirect3DDevice9_Release(device);
+    ok(callback_cw == 0x37f, "Callback cw is %#x, expected 0xf60.\n", callback_cw);
+    ok(callback_tid == GetCurrentThreadId(), "Got unexpected thread id.\n");
 
 done:
     IDirect3D9_Release(d3d9);
@@ -6976,13 +7063,6 @@ static void test_private_data(void)
     HRESULT hr;
     DWORD size;
     DWORD data[4] = {1, 2, 3, 4};
-    static const GUID d3d9_private_data_test_guid =
-    {
-        0xfdb37466,
-        0x428f,
-        0x4edf,
-        {0xa3,0x7f,0x9b,0x1d,0xf4,0x88,0xc5,0xfc}
-    };
     static const GUID d3d9_private_data_test_guid2 =
     {
         0x2e5afac2,
-- 
2.3.0




More information about the wine-patches mailing list