[PATCH 2/5] wined3d: Handle fog_start==fog_end with table fog(try 2)

Stefan Dösinger stefan at codeweavers.com
Mon Jun 17 15:53:22 CDT 2013


Wizard101 triggers this when running on a GPU that does not support
shader model 3.

try 2: Make the fogend increase code a quirk rather than using it
unconditionally. GLSL doesn't need it because it implements the fog
equation itself, and some OSX drivers handle fog_start == fog_end
correctly with fixed function fog. Tested as usual on Geforce9 (OSX,
Linux), Geforce7 (Linux), r300g, r600g, i965 (OSX), r200.

The follow-up tests pass on all GPUs except r200. The fog table works
correctly in the dx7 sdk mfcfog example on r200, and I didn't spend a
lot of time trying to find out why my test is broken on that old card.
---
 dlls/wined3d/arb_program_shader.c    |  8 ++--
 dlls/wined3d/ati_fragment_shader.c   |  3 +-
 dlls/wined3d/directx.c               | 86 ++++++++++++++++++++++++++++++++++++
 dlls/wined3d/glsl_shader.c           |  4 +-
 dlls/wined3d/nvidia_texture_shader.c |  2 +-
 dlls/wined3d/state.c                 | 47 +++++++++++++++++---
 dlls/wined3d/wined3d_private.h       |  4 ++
 7 files changed, 143 insertions(+), 11 deletions(-)

diff --git a/dlls/wined3d/arb_program_shader.c b/dlls/wined3d/arb_program_shader.c
index 5ff5e98..a8bb7d5 100644
--- a/dlls/wined3d/arb_program_shader.c
+++ b/dlls/wined3d/arb_program_shader.c
@@ -5085,7 +5085,7 @@ static void shader_arb_get_caps(const struct wined3d_gl_info *gl_info, struct sh
         caps->ps_1x_max_value = 0.0f;
     }
 
-    caps->wined3d_caps = WINED3D_SHADER_CAP_SRGB_WRITE;
+    caps->wined3d_caps = WINED3D_SHADER_CAP_SRGB_WRITE | WINED3D_SHADER_CAP_FFP_FOG;
     if (use_nv_clip(gl_info))
         caps->wined3d_caps |= WINED3D_SHADER_CAP_VS_CLIPPING;
 }
@@ -5763,7 +5763,7 @@ static void arbfp_free(struct wined3d_device *device)
 static void arbfp_get_caps(const struct wined3d_gl_info *gl_info, struct fragment_caps *caps)
 {
     caps->wined3d_caps = WINED3D_FRAGMENT_CAP_PROJ_CONTROL
-            | WINED3D_FRAGMENT_CAP_SRGB_WRITE;
+            | WINED3D_FRAGMENT_CAP_SRGB_WRITE | WINED3D_FRAGMENT_CAP_FFP_FOG;
     caps->PrimitiveMiscCaps = WINED3DPMISCCAPS_TSSARGTEMP;
     caps->TextureOpCaps =  WINED3DTEXOPCAPS_DISABLE                     |
                            WINED3DTEXOPCAPS_SELECTARG1                  |
@@ -6514,6 +6514,8 @@ static void fragment_prog_arbfp(struct wined3d_context *context, const struct wi
 static void state_arbfp_fog(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id)
 {
     enum fogsource new_source;
+    DWORD fogstart = state->render_states[WINED3D_RS_FOGSTART];
+    DWORD fogend = state->render_states[WINED3D_RS_FOGEND];
 
     TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id);
 
@@ -6542,7 +6544,7 @@ static void state_arbfp_fog(struct wined3d_context *context, const struct wined3
         new_source = FOGSOURCE_FFP;
     }
 
-    if (new_source != context->fog_source)
+    if (new_source != context->fog_source || fogstart == fogend)
     {
         context->fog_source = new_source;
         state_fogstartend(context, state, STATE_RENDER(WINED3D_RS_FOGSTART));
diff --git a/dlls/wined3d/ati_fragment_shader.c b/dlls/wined3d/ati_fragment_shader.c
index 980f657..d860653 100644
--- a/dlls/wined3d/ati_fragment_shader.c
+++ b/dlls/wined3d/ati_fragment_shader.c
@@ -1103,7 +1103,8 @@ static void atifs_enable(const struct wined3d_gl_info *gl_info, BOOL enable)
 
 static void atifs_get_caps(const struct wined3d_gl_info *gl_info, struct fragment_caps *caps)
 {
-    caps->wined3d_caps = WINED3D_FRAGMENT_CAP_PROJ_CONTROL;
+    caps->wined3d_caps = WINED3D_FRAGMENT_CAP_PROJ_CONTROL
+            | WINED3D_FRAGMENT_CAP_FFP_FOG;
     caps->PrimitiveMiscCaps = WINED3DPMISCCAPS_TSSARGTEMP;
     caps->TextureOpCaps =  WINED3DTEXOPCAPS_DISABLE                     |
                            WINED3DTEXOPCAPS_SELECTARG1                  |
diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c
index 2993542..5cd3731 100644
--- a/dlls/wined3d/directx.c
+++ b/dlls/wined3d/directx.c
@@ -791,6 +791,74 @@ static BOOL match_r200(const struct wined3d_gl_info *gl_info, const char *gl_ren
     return FALSE;
 }
 
+static BOOL match_broken_ffp_fog(const struct wined3d_gl_info *gl_info, const char *gl_renderer,
+        enum wined3d_gl_vendor gl_vendor, enum wined3d_pci_vendor card_vendor, enum wined3d_pci_device device)
+{
+    DWORD data[4];
+    GLuint tex, fbo;
+    GLenum status;
+    float color[4] = {0.0f, 1.0f, 0.0f, 0.0f};
+
+    if (wined3d_settings.offscreen_rendering_mode != ORM_FBO) return FALSE;
+
+    gl_info->gl_ops.gl.p_glGenTextures(1, &tex);
+    gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, tex);
+    gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, 4, 1, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL);
+    checkGLcall("glTexImage2D");
+
+    gl_info->fbo_ops.glGenFramebuffers(1, &fbo);
+    gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+    gl_info->fbo_ops.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
+    checkGLcall("glFramebufferTexture2D");
+
+    status = gl_info->fbo_ops.glCheckFramebufferStatus(GL_FRAMEBUFFER);
+    if (status != GL_FRAMEBUFFER_COMPLETE) ERR("FBO status %#x\n", status);
+    checkGLcall("glCheckFramebufferStatus");
+
+    gl_info->gl_ops.gl.p_glClearColor(0.0f, 0.0f, 1.0f, 0.0f);
+    gl_info->gl_ops.gl.p_glClear(GL_COLOR_BUFFER_BIT);
+    gl_info->gl_ops.gl.p_glViewport(0, 0, 4, 1);
+
+    gl_info->gl_ops.gl.p_glEnable(GL_FOG);
+    gl_info->gl_ops.gl.p_glFogf(GL_FOG_START, 0.5f);
+    gl_info->gl_ops.gl.p_glFogf(GL_FOG_END, 0.5f);
+    gl_info->gl_ops.gl.p_glFogi(GL_FOG_MODE, GL_LINEAR);
+    gl_info->gl_ops.gl.p_glHint(GL_FOG_HINT, GL_NICEST);
+    gl_info->gl_ops.gl.p_glFogfv(GL_FOG_COLOR, color);
+    checkGLcall("fog setup");
+
+    gl_info->gl_ops.gl.p_glColor3f(1.0f, 0.0f, 0.0f);
+    gl_info->gl_ops.gl.p_glBegin(GL_TRIANGLE_STRIP);
+    gl_info->gl_ops.gl.p_glVertex3f(-1.0f, -1.0f,  0.0f);
+    gl_info->gl_ops.gl.p_glVertex3f( 1.0f, -1.0f,  1.0f);
+    gl_info->gl_ops.gl.p_glVertex3f(-1.0f,  1.0f,  0.0f);
+    gl_info->gl_ops.gl.p_glVertex3f( 1.0f,  1.0f,  1.0f);
+    gl_info->gl_ops.gl.p_glEnd();
+    checkGLcall("draw");
+
+    gl_info->gl_ops.gl.p_glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, data);
+    checkGLcall("glGetTexImage");
+    data[0] &= 0x00ffffff;
+    data[1] &= 0x00ffffff;
+    data[2] &= 0x00ffffff;
+    data[3] &= 0x00ffffff;
+
+    gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, 0);
+    gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, 0);
+    checkGLcall("glBindTexture");
+
+    gl_info->fbo_ops.glDeleteFramebuffers(1, &fbo);
+    gl_info->gl_ops.gl.p_glDeleteTextures(1, &tex);
+    checkGLcall("glDeleteTextures");
+    gl_info->gl_ops.gl.p_glDisable(GL_FOG);
+    checkGLcall("glDisable(GL_FOG)");
+
+    TRACE("Fog test data: %08x %08x %08x %08x\n", data[0], data[1], data[2], data[3]);
+    return data[0] != 0x00ff0000 || data[3] != 0x0000ff00;
+}
+
 static void quirk_apple_glsl_constants(struct wined3d_gl_info *gl_info)
 {
     /* MacOS needs uniforms for relative addressing offsets. This can accumulate to quite a few uniforms.
@@ -914,6 +982,11 @@ static void quirk_r200_constants(struct wined3d_gl_info *gl_info)
     gl_info->reserved_arb_constants = max(gl_info->reserved_arb_constants, 1);
 }
 
+static void quirk_broken_ffp_fog(struct wined3d_gl_info *gl_info)
+{
+    gl_info->quirks |= WINED3D_QUIRK_BROKEN_FFP_FOG;
+}
+
 struct driver_quirk
 {
     BOOL (*match)(const struct wined3d_gl_info *gl_info, const char *gl_renderer,
@@ -999,6 +1072,11 @@ static const struct driver_quirk quirk_table[] =
         quirk_r200_constants,
         "r200 vertex shader constants"
     },
+    {
+        match_broken_ffp_fog,
+        quirk_broken_ffp_fog,
+        "fogstart == fogend workaround"
+    },
 };
 
 /* Certain applications (Steam) complain if we report an outdated driver version. In general,
@@ -2967,6 +3045,14 @@ static BOOL wined3d_adapter_init_gl_caps(struct wined3d_adapter *adapter)
     init_driver_info(driver_info, card_vendor, device);
     add_gl_compat_wrappers(gl_info);
 
+    if (gl_info->quirks & WINED3D_QUIRK_BROKEN_FFP_FOG
+            && (fragment_caps.wined3d_caps & WINED3D_FRAGMENT_CAP_FFP_FOG
+            || shader_caps.wined3d_caps & WINED3D_SHADER_CAP_FFP_FOG))
+    {
+        TRACE("Activating fogstart == fogend workaround\n");
+        adapter->d3d_info.flags |= WINED3D_ADAPTER_CAP_BROKEN_FOG;
+    }
+
     return TRUE;
 }
 
diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c
index e14d7ab..ad9be6a 100644
--- a/dlls/wined3d/glsl_shader.c
+++ b/dlls/wined3d/glsl_shader.c
@@ -7086,6 +7086,8 @@ static void glsl_fragment_pipe_fog(struct wined3d_context *context,
 {
     BOOL use_vshader = use_vs(state);
     enum fogsource new_source;
+    DWORD fogstart = state->render_states[WINED3D_RS_FOGSTART];
+    DWORD fogend = state->render_states[WINED3D_RS_FOGEND];
 
     context->select_shader = 1;
     context->load_constants = 1;
@@ -7107,7 +7109,7 @@ static void glsl_fragment_pipe_fog(struct wined3d_context *context,
         new_source = FOGSOURCE_FFP;
     }
 
-    if (new_source != context->fog_source)
+    if (new_source != context->fog_source || fogstart == fogend)
     {
         context->fog_source = new_source;
         state_fogstartend(context, state, STATE_RENDER(WINED3D_RS_FOGSTART));
diff --git a/dlls/wined3d/nvidia_texture_shader.c b/dlls/wined3d/nvidia_texture_shader.c
index 648b1e3..08d45a0 100644
--- a/dlls/wined3d/nvidia_texture_shader.c
+++ b/dlls/wined3d/nvidia_texture_shader.c
@@ -670,7 +670,7 @@ static void nvts_enable(const struct wined3d_gl_info *gl_info, BOOL enable)
 
 static void nvrc_fragment_get_caps(const struct wined3d_gl_info *gl_info, struct fragment_caps *caps)
 {
-    caps->wined3d_caps = 0;
+    caps->wined3d_caps = WINED3D_FRAGMENT_CAP_FFP_FOG;
     caps->PrimitiveMiscCaps = WINED3DPMISCCAPS_TSSARGTEMP;
 
     /* The caps below can be supported but aren't handled yet in utils.c
diff --git a/dlls/wined3d/state.c b/dlls/wined3d/state.c
index 87c2585..bae96dd 100644
--- a/dlls/wined3d/state.c
+++ b/dlls/wined3d/state.c
@@ -1043,10 +1043,45 @@ void state_fogstartend(struct wined3d_context *context, const struct wined3d_sta
             fogstart = tmpvalue.f;
             tmpvalue.d = state->render_states[WINED3D_RS_FOGEND];
             fogend = tmpvalue.f;
-            /* In GL, fogstart == fogend disables fog, in D3D everything's fogged.*/
-            if(fogstart == fogend) {
-                fogstart = -INFINITY;
-                fogend = 0.0f;
+            /* Special handling for fogstart == fogend. In d3d with vertex
+             * fog, everything is fogged. With table fog, everything with
+             * fog_coord < fog_start is unfogged, and fog_coord > fog_start
+             * is fogged. Windows drivers disagree when fog_coord == fog_start.
+             *
+             * GL's linear fog equation is supposed to behave like D3D's table
+             * fog, but because a division by zero is involved, most drivers
+             * don't get it right. If this is the case, increase fogend a bit
+             * to avoid the corner case. */
+            if (fogstart == fogend)
+            {
+                if (state->render_states[WINED3D_RS_FOGTABLEMODE] == WINED3D_FOG_NONE)
+                {
+                    fogstart = -INFINITY;
+                    fogend = 0.0f;
+                }
+                else if (context->d3d_info->flags & WINED3D_ADAPTER_CAP_BROKEN_FOG)
+                {
+                    /* Float has 32 bit precision, while FPU registers
+                     * have much more(80 bit and more). Thus make sure
+                     * that the stored float fogend is > fogstart, not
+                     * the fpu register, hence the volatile. */
+                    if (isnan(fogstart) || isinf(fogstart) || isinf(-fogstart))
+                    {
+                        FIXME("Both fogstart and fogend are NaN, INF or -INF\n");
+                    }
+                    else
+                    {
+                        float increase = eps;
+                        volatile float fogend2 = fogend;
+                        do
+                        {
+                            fogend2 += increase;
+                            increase *= 2.0f;
+                        }
+                        while (fogstart == fogend2);
+                        fogend = fogend2;
+                    }
+                }
             }
             break;
 
@@ -1072,6 +1107,8 @@ void state_fog_fragpart(struct wined3d_context *context, const struct wined3d_st
 {
     const struct wined3d_gl_info *gl_info = context->gl_info;
     enum fogsource new_source;
+    DWORD fogstart = state->render_states[WINED3D_RS_FOGSTART];
+    DWORD fogend = state->render_states[WINED3D_RS_FOGEND];
 
     TRACE("context %p, state %p, state_id %#x.\n", context, state, state_id);
 
@@ -1217,7 +1254,7 @@ void state_fog_fragpart(struct wined3d_context *context, const struct wined3d_st
 
     glEnableWINE(GL_FOG);
     checkGLcall("glEnable GL_FOG");
-    if (new_source != context->fog_source)
+    if (new_source != context->fog_source || fogstart == fogend)
     {
         context->fog_source = new_source;
         state_fogstartend(context, state, STATE_RENDER(WINED3D_RS_FOGSTART));
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index 9305c8f..67dae20 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -62,6 +62,7 @@
 #define WINED3D_QUIRK_BROKEN_RGBA16             0x00000040
 #define WINED3D_QUIRK_INFO_LOG_SPAM             0x00000080
 #define WINED3D_QUIRK_LIMITED_TEX_FILTERING     0x00000100
+#define WINED3D_QUIRK_BROKEN_FFP_FOG            0x00000200
 
 /* Texture format fixups */
 
@@ -702,6 +703,7 @@ typedef void (*SHADER_HANDLER)(const struct wined3d_shader_instruction *);
 
 #define WINED3D_SHADER_CAP_VS_CLIPPING      0x00000001
 #define WINED3D_SHADER_CAP_SRGB_WRITE       0x00000002
+#define WINED3D_SHADER_CAP_FFP_FOG          0x00000004
 
 struct shader_caps
 {
@@ -1171,6 +1173,7 @@ struct StateEntryTemplate
 
 #define WINED3D_FRAGMENT_CAP_PROJ_CONTROL   0x00000001
 #define WINED3D_FRAGMENT_CAP_SRGB_WRITE     0x00000002
+#define WINED3D_FRAGMENT_CAP_FFP_FOG        0x00000004
 
 struct fragment_caps
 {
@@ -1608,6 +1611,7 @@ struct wined3d_d3d_limits
 
 #define WINED3D_ADAPTER_CAP_XYZRHW              0x00000001
 #define WINED3D_ADAPTER_CAP_VS_CLIPPING         0x00000002
+#define WINED3D_ADAPTER_CAP_BROKEN_FOG          0x00000008
 
 struct wined3d_d3d_info
 {
-- 
1.8.1.5




More information about the wine-patches mailing list