[2/13] WineD3D: Light parameter fixes

Stefan Dösinger stefan at codeweavers.com
Tue Feb 20 15:43:13 CST 2007


-------------- next part --------------
From e4433c23fb5ace59b4cffd6284d440b7eecaab85 Mon Sep 17 00:00:00 2001
From: Stefan Doesinger <stefan at codeweavers.com>
Date: Tue, 20 Feb 2007 21:05:41 +0100
Subject: [PATCH] WineD3D: Light parameter fixes

Need for speed most wanted seems to use bad light paramters which seem
to cause crashes in opengl drivers. This patch extends the light test to
test invalid light types and NULL lights and adds an input value check to
wined3d to catch bad values and return an error.

Furthermore attenuation may not be < 0.0 for light types that use it, but it
may be NaN. Lights which do not use the attenuation value ignore it when
setting, so do not apply it to gl. Need for speed most wanted sets some junk
values which cause invalid floating point operations in libGL.so otherwise.
---
 dlls/ddraw/tests/d3d.c |   59 ++++++++++++++++++++++++++++++++++++++++++++++++
 dlls/wined3d/device.c  |   31 +++++++++++++++++++++++++
 dlls/wined3d/state.c   |   29 ++++++++++++++++-------
 3 files changed, 110 insertions(+), 9 deletions(-)

diff --git a/dlls/ddraw/tests/d3d.c b/dlls/ddraw/tests/d3d.c
index bb37f08..1125875 100644
--- a/dlls/ddraw/tests/d3d.c
+++ b/dlls/ddraw/tests/d3d.c
@@ -218,6 +218,65 @@ static void LightTest(void)
     /* Light 23 has not been set */
     rc = IDirect3DDevice7_GetLightEnable(lpD3DDevice, 23, &bEnabled );
     ok(rc==DDERR_INVALIDPARAMS, "GetLightEnable returned: %x\n", rc);
+
+    /* Set some lights with invalid parameters */
+    memset(&light, 0, sizeof(D3DLIGHT7));
+    light.dltType = 0;
+    U1(light.dcvDiffuse).r = 1.f;
+    U2(light.dcvDiffuse).g = 1.f;
+    U3(light.dcvDiffuse).b = 1.f;
+    U3(light.dvDirection).z = 1.f;
+    rc = IDirect3DDevice7_SetLight(lpD3DDevice, 100, &light);
+    ok(rc==DDERR_INVALIDPARAMS, "SetLight returned: %x\n", rc);
+
+    memset(&light, 0, sizeof(D3DLIGHT7));
+    light.dltType = 12345;
+    U1(light.dcvDiffuse).r = 1.f;
+    U2(light.dcvDiffuse).g = 1.f;
+    U3(light.dcvDiffuse).b = 1.f;
+    U3(light.dvDirection).z = 1.f;
+    rc = IDirect3DDevice7_SetLight(lpD3DDevice, 101, &light);
+    ok(rc==DDERR_INVALIDPARAMS, "SetLight returned: %x\n", rc);
+
+    rc = IDirect3DDevice7_SetLight(lpD3DDevice, 102, NULL);
+    ok(rc==DDERR_INVALIDPARAMS, "SetLight returned: %x\n", rc);
+
+    memset(&light, 0, sizeof(D3DLIGHT7));
+    light.dltType = D3DLIGHT_SPOT;
+    U1(light.dcvDiffuse).r = 1.f;
+    U2(light.dcvDiffuse).g = 1.f;
+    U3(light.dcvDiffuse).b = 1.f;
+    U3(light.dvDirection).z = 1.f;
+
+    U3(light.dvAttenuation0) = -1.0 / 0.0; /* -INFINITY */
+    rc = IDirect3DDevice7_SetLight(lpD3DDevice, 103, &light);
+    ok(rc==DDERR_INVALIDPARAMS, "SetLight returned: %x\n", rc);
+
+    U3(light.dvAttenuation0) = -1.0;
+    rc = IDirect3DDevice7_SetLight(lpD3DDevice, 103, &light);
+    ok(rc==DDERR_INVALIDPARAMS, "SetLight returned: %x\n", rc);
+
+    U3(light.dvAttenuation0) = 0.0;
+    rc = IDirect3DDevice7_SetLight(lpD3DDevice, 103, &light);
+    ok(rc==D3D_OK, "SetLight returned: %x\n", rc);
+
+    U3(light.dvAttenuation0) = 1.0;
+    rc = IDirect3DDevice7_SetLight(lpD3DDevice, 103, &light);
+    ok(rc==D3D_OK, "SetLight returned: %x\n", rc);
+
+    U3(light.dvAttenuation0) = 1.0 / 0.0; /* +INFINITY */
+    rc = IDirect3DDevice7_SetLight(lpD3DDevice, 103, &light);
+    ok(rc==D3D_OK, "SetLight returned: %x\n", rc);
+
+    U3(light.dvAttenuation0) = 0.0 / 0.0; /* NaN */
+    rc = IDirect3DDevice7_SetLight(lpD3DDevice, 103, &light);
+    ok(rc==D3D_OK, "SetLight returned: %x\n", rc);
+
+    /* Directional light ignores attenuation */
+    light.dltType = D3DLIGHT_DIRECTIONAL;
+    U3(light.dvAttenuation0) = -1.0;
+    rc = IDirect3DDevice7_SetLight(lpD3DDevice, 103, &light);
+    ok(rc==D3D_OK, "SetLight returned: %x\n", rc);
 }
 
 static void ProcessVerticesTest(void)
diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index af0e96a..05b4852 100644
--- a/dlls/wined3d/device.c
+++ b/dlls/wined3d/device.c
@@ -2199,6 +2199,37 @@ static HRESULT WINAPI IWineD3DDeviceImpl_SetLight(IWineD3DDevice *iface, DWORD I
     IWineD3DDeviceImpl *This = (IWineD3DDeviceImpl *)iface;
     TRACE("(%p) : Idx(%d), pLight(%p). Hash index is %d\n", This, Index, pLight, Hi);
 
+    /* Check the parameter range. Need for speed most wanted sets junk lights which confuse
+     * the gl driver.
+     */
+    if(!pLight) {
+        WARN("Light pointer = NULL, returning WINED3DERR_INVALIDCALL\n");
+        return WINED3DERR_INVALIDCALL;
+    }
+
+    switch(pLight->Type) {
+        case WINED3DLIGHT_POINT:
+        case WINED3DLIGHT_SPOT:
+        case WINED3DLIGHT_PARALLELPOINT:
+        case WINED3DLIGHT_GLSPOT:
+            /* Incorrect attenuation values can cause the gl driver to crash. Happens with Need for speed
+             * most wanted
+             */
+            if(pLight->Attenuation0 < 0.0 || pLight->Attenuation1 < 0.0 || pLight->Attenuation2 < 0.0) {
+                WARN("Attenuation is negative, returning WINED3DERR_INVALIDCALL\n");
+                return WINED3DERR_INVALIDCALL;
+            }
+            break;
+
+        case WINED3DLIGHT_DIRECTIONAL:
+            /* Ignores attenuation */
+            break;
+
+        default:
+        WARN("Light type out of range, returning WINED3DERR_INVALIDCALL\n");
+        return WINED3DERR_INVALIDCALL;
+    }
+
     LIST_FOR_EACH(e, &This->updateStateBlock->lightMap[Hi]) {
         object = LIST_ENTRY(e, PLIGHTINFOEL, entry);
         if(object->OriginalIndex == Index) break;
diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c
index f78720a..15a8a71 100644
--- a/dlls/wined3d/state.c
+++ b/dlls/wined3d/state.c
@@ -2981,21 +2981,16 @@ static void light(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContex
         glLightfv(GL_LIGHT0 + Index, GL_AMBIENT, colRGBA);
         checkGLcall("glLightfv");
 
-        /* Attenuation - Are these right? guessing... */
-        glLightf(GL_LIGHT0 + Index, GL_CONSTANT_ATTENUATION,  lightInfo->OriginalParms.Attenuation0);
-        checkGLcall("glLightf");
-        glLightf(GL_LIGHT0 + Index, GL_LINEAR_ATTENUATION,    lightInfo->OriginalParms.Attenuation1);
-        checkGLcall("glLightf");
-
         if ((lightInfo->OriginalParms.Range *lightInfo->OriginalParms.Range) >= FLT_MIN) {
             quad_att = 1.4/(lightInfo->OriginalParms.Range *lightInfo->OriginalParms.Range);
         } else {
             quad_att = 0; /*  0 or  MAX?  (0 seems to be ok) */
         }
 
-        if (quad_att < lightInfo->OriginalParms.Attenuation2) quad_att = lightInfo->OriginalParms.Attenuation2;
-        glLightf(GL_LIGHT0 + Index, GL_QUADRATIC_ATTENUATION, quad_att);
-        checkGLcall("glLightf");
+        /* Do not assign attenuation values for lights that do not use them. D3D apps are free to pass any junk,
+         * but gl drivers use them and may crash due to bad Attenuation values. Need for Speed most wanted sets
+         * Attenuation0 to NaN and crashes in the gl lib
+         */
 
         switch (lightInfo->OriginalParms.Type) {
             case WINED3DLIGHT_POINT:
@@ -3004,6 +2999,14 @@ static void light(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContex
                 checkGLcall("glLightfv");
                 glLightf(GL_LIGHT0 + Index, GL_SPOT_CUTOFF, lightInfo->cutoff);
                 checkGLcall("glLightf");
+                /* Attenuation - Are these right? guessing... */
+                glLightf(GL_LIGHT0 + Index, GL_CONSTANT_ATTENUATION,  lightInfo->OriginalParms.Attenuation0);
+                checkGLcall("glLightf");
+                glLightf(GL_LIGHT0 + Index, GL_LINEAR_ATTENUATION,    lightInfo->OriginalParms.Attenuation1);
+                checkGLcall("glLightf");
+                if (quad_att < lightInfo->OriginalParms.Attenuation2) quad_att = lightInfo->OriginalParms.Attenuation2;
+                glLightf(GL_LIGHT0 + Index, GL_QUADRATIC_ATTENUATION, quad_att);
+                checkGLcall("glLightf");
                 /* FIXME: Range */
                 break;
 
@@ -3018,6 +3021,14 @@ static void light(DWORD state, IWineD3DStateBlockImpl *stateblock, WineD3DContex
                 checkGLcall("glLightf");
                 glLightf(GL_LIGHT0 + Index, GL_SPOT_CUTOFF, lightInfo->cutoff);
                 checkGLcall("glLightf");
+                /* Attenuation - Are these right? guessing... */
+                glLightf(GL_LIGHT0 + Index, GL_CONSTANT_ATTENUATION,  lightInfo->OriginalParms.Attenuation0);
+                checkGLcall("glLightf");
+                glLightf(GL_LIGHT0 + Index, GL_LINEAR_ATTENUATION,    lightInfo->OriginalParms.Attenuation1);
+                checkGLcall("glLightf");
+                if (quad_att < lightInfo->OriginalParms.Attenuation2) quad_att = lightInfo->OriginalParms.Attenuation2;
+                glLightf(GL_LIGHT0 + Index, GL_QUADRATIC_ATTENUATION, quad_att);
+                checkGLcall("glLightf");
                 /* FIXME: Range */
                 break;
 
-- 
1.4.4.3



More information about the wine-patches mailing list