wined3d: Try to detect the polygon offset scale value.

FEAR draws the same geometry twice, the second time using zfunc=equal.
In both cases it sets a huge depth bias of -0.5, presumably to get
better precision for the fragile Z comparison. The GL polygon offset we
set ends up being so large that it pulls the geometry into the negative
Z range. It isn't clipped (or no longer, older NV drivers probably had a
separate bug there), but the Z value gets clamped to 0.0 in the first
draw and doesn't match the incoming Z in the second draw.
This commit is contained in:
Stefan Dösinger 2015-07-30 18:50:15 +02:00 committed by Alexandre Julliard
parent f529a5aaae
commit 34d8b987c4
5 changed files with 119 additions and 9 deletions

View File

@ -446,7 +446,8 @@ static void wined3d_cs_exec_set_depth_stencil_view(struct wined3d_cs *cs, const
device_invalidate_state(device, STATE_RENDER(WINED3D_RS_STENCILWRITEMASK));
device_invalidate_state(device, STATE_RENDER(WINED3D_RS_DEPTHBIAS));
}
else if (prev && prev->format->depth_size != op->view->format->depth_size)
else if (prev && (prev->format_flags & WINED3DFMT_FLAG_FLOAT)
!= (op->view->format_flags & WINED3DFMT_FLAG_FLOAT))
{
device_invalidate_state(device, STATE_RENDER(WINED3D_RS_DEPTHBIAS));
}

View File

@ -5903,6 +5903,10 @@ static BOOL wined3d_adapter_init(struct wined3d_adapter *adapter, UINT ordinal)
return FALSE;
}
gl_info->fixed_polyoffset_scale = wined3d_adapter_find_polyoffset_scale(&caps_gl_ctx, GL_DEPTH_COMPONENT);
if (gl_info->supported[ARB_DEPTH_BUFFER_FLOAT])
gl_info->float_polyoffset_scale = wined3d_adapter_find_polyoffset_scale(&caps_gl_ctx, GL_DEPTH32F_STENCIL8);
adapter->vram_bytes = adapter->driver_info.vram_bytes;
adapter->vram_bytes_used = 0;
TRACE("Emulating 0x%s bytes of video ram.\n", wine_dbgstr_longlong(adapter->vram_bytes));

View File

@ -1632,11 +1632,17 @@ static void state_scissor(struct wined3d_context *context, const struct wined3d_
* OpenGL the bias is specified in units of "the smallest value that is
* guaranteed to produce a resolvable offset for a given implementation". To
* convert from D3D to GL we need to divide the D3D depth bias by that value.
* There's no practical way to retrieve that value from a given GL
* implementation, but the D3D application has essentially the same problem,
* which makes a guess of the depth buffer format's highest possible value a
* reasonable guess. Note that SLOPESCALEDEPTHBIAS is a scaling factor for the
* depth slope, and doesn't need to be scaled. */
* We try to detect the value from GL with test draws. On most drivers (r300g,
* 600g, Nvidia, i965 on Mesa) the value is 2^23 for fixed point depth buffers,
* for r200 and i965 on OSX it is 2^24, for r500 on OSX it is 2^22. For floating
* point buffers it is 2^22, 2^23 or 2^24 depending on the GPU. The value does
* not depend on the depth buffer precision on any driver.
*
* Two games that are picky regarding depth bias are Mass Effect 2 (flickering
* decals) and F.E.A.R and F.E.A.R. 2 (semi-transparent guns).
*
* Note that SLOPESCALEDEPTHBIAS is a scaling factor for the depth slope, and
* doesn't need to be scaled to account for GL vs D3D differences. */
static void state_depthbias(struct wined3d_context *context, const struct wined3d_state *state, DWORD state_id)
{
const struct wined3d_gl_info *gl_info = context->gl_info;
@ -1669,10 +1675,13 @@ static void state_depthbias(struct wined3d_context *context, const struct wined3
{
if (depth)
{
const struct wined3d_format *fmt = depth->format;
scale = powf(2, fmt->depth_size) - 1;
if (depth->format_flags & WINED3DFMT_FLAG_FLOAT)
scale = gl_info->float_polyoffset_scale;
else
scale = gl_info->fixed_polyoffset_scale;
TRACE("Depth format %s, using depthbias scale of %.8e.\n",
debug_d3dformat(fmt->id), scale);
debug_d3dformat(depth->format->id), scale);
}
else
{

View File

@ -2707,6 +2707,100 @@ fail:
return FALSE;
}
float wined3d_adapter_find_polyoffset_scale(struct wined3d_caps_gl_ctx *ctx, GLenum format)
{
const struct wined3d_gl_info *gl_info = ctx->gl_info;
static const struct wined3d_color blue = {0.0f, 0.0f, 1.0f, 1.0f};
GLuint fbo, color, depth;
unsigned int low = 0, high = 32, cur;
DWORD readback[256];
static const struct wined3d_vec3 geometry[] =
{
{-1.0f, -1.0f, -1.0f},
{ 1.0f, -1.0f, 0.0f},
{-1.0f, 1.0f, -1.0f},
{ 1.0f, 1.0f, 0.0f},
};
/* Most drivers want 2^23 for fixed point depth buffers, including r300g, r600g,
* Nvidia. Use this as a fallback if the detection fails. */
unsigned int fallback = 23;
if (wined3d_settings.offscreen_rendering_mode != ORM_FBO)
{
FIXME("No FBOs, assuming polyoffset scale of 2^%u.\n", fallback);
return (float)(1 << fallback);
}
gl_info->gl_ops.gl.p_glGenTextures(1, &color);
gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, color);
gl_info->gl_ops.gl.p_glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
gl_info->gl_ops.gl.p_glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 256, 1, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0);
gl_info->fbo_ops.glGenRenderbuffers(1, &depth);
gl_info->fbo_ops.glBindRenderbuffer(GL_RENDERBUFFER, depth);
gl_info->fbo_ops.glRenderbufferStorage(GL_RENDERBUFFER, format, 256, 1);
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, color, 0);
gl_info->fbo_ops.glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth);
checkGLcall("Setup framebuffer");
gl_info->gl_ops.gl.p_glClearColor(0.0f, 0.0f, 0.5f, 0.0f);
gl_info->gl_ops.gl.p_glClearDepth(0.5f);
gl_info->gl_ops.gl.p_glEnable(GL_DEPTH_TEST);
gl_info->gl_ops.gl.p_glEnable(GL_POLYGON_OFFSET_FILL);
gl_info->gl_ops.gl.p_glViewport(0, 0, 256, 1);
checkGLcall("Misc parameters");
for (;;)
{
if (high - low <= 1)
{
ERR("PolygonOffset scale factor detection failed, using fallback value 2^%u.\n", fallback);
cur = fallback;
break;
}
cur = (low + high) / 2;
gl_info->gl_ops.gl.p_glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
/* The post viewport transform Z of the geometry runs from 0.0 to 0.5. We want to push it another
* 0.25 so that the Z buffer content (0.5) cuts the quad off at half the screen. */
gl_info->gl_ops.gl.p_glPolygonOffset(0.0f, (float)(1 << cur) * 0.25f);
draw_test_quad(ctx, geometry, &blue);
checkGLcall("Test draw");
/* Rebinding texture to workaround a fglrx bug. */
gl_info->gl_ops.gl.p_glBindTexture(GL_TEXTURE_2D, color);
gl_info->gl_ops.gl.p_glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, readback);
checkGLcall("readback");
TRACE("low %02u, high %02u, cur %2u, 0=0x%08x, 125=0x%08x, 131=0x%08x, 255=0x%08x\n",
low, high, cur, readback[0], readback[125], readback[131], readback[255]);
if ((readback[125] & 0xff) < 0xa0)
high = cur;
else if ((readback[131] & 0xff) > 0xa0)
low = cur;
else
{
TRACE("Found scale factor 2^%u for format %x\n", cur, format);
break;
}
}
gl_info->gl_ops.gl.p_glDeleteTextures(1, &color);
gl_info->fbo_ops.glDeleteRenderbuffers(1, &depth);
gl_info->fbo_ops.glDeleteFramebuffers(1, &fbo);
gl_info->fbo_ops.glBindFramebuffer(GL_FRAMEBUFFER, 0);
checkGLcall("Delete framebuffer");
gl_info->gl_ops.gl.p_glDisable(GL_DEPTH_TEST);
gl_info->gl_ops.gl.p_glDisable(GL_POLYGON_OFFSET_FILL);
return (float)(1 << cur);
}
const struct wined3d_format *wined3d_get_format(const struct wined3d_gl_info *gl_info,
enum wined3d_format_id format_id)
{

View File

@ -1723,6 +1723,7 @@ struct wined3d_gl_info
DWORD quirks;
BOOL supported[WINED3D_GL_EXT_COUNT];
GLint wrap_lookup[WINED3D_TADDRESS_MIRROR_ONCE - WINED3D_TADDRESS_WRAP + 1];
float fixed_polyoffset_scale, float_polyoffset_scale;
HGLRC (WINAPI *p_wglCreateContextAttribsARB)(HDC dc, HGLRC share, const GLint *attribs);
struct opengl_funcs gl_ops;
@ -1812,6 +1813,7 @@ struct wined3d_caps_gl_ctx
GLuint test_program_id;
};
float wined3d_adapter_find_polyoffset_scale(struct wined3d_caps_gl_ctx *ctx, GLenum format) DECLSPEC_HIDDEN;
BOOL wined3d_adapter_init_format_info(struct wined3d_adapter *adapter,
struct wined3d_caps_gl_ctx *ctx) DECLSPEC_HIDDEN;
UINT64 adapter_adjust_memory(struct wined3d_adapter *adapter, INT64 amount) DECLSPEC_HIDDEN;