[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