From 8a3ef776f4f411172c5bfcc634f358dbedbc7479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20D=C3=B6singer?= Date: Fri, 7 Mar 2008 03:13:07 +0100 Subject: [PATCH] wined3d: gl_FragCoord isn't exact. --- dlls/d3d9/tests/visual.c | 46 +++++++++++++++++++++++++++++++++++--- dlls/wined3d/glsl_shader.c | 16 ++++++++++++- 2 files changed, 58 insertions(+), 4 deletions(-) diff --git a/dlls/d3d9/tests/visual.c b/dlls/d3d9/tests/visual.c index 6b70a02ec1d..280e92def6a 100644 --- a/dlls/d3d9/tests/visual.c +++ b/dlls/d3d9/tests/visual.c @@ -6907,7 +6907,16 @@ static void vpos_register_test(IDirect3DDevice9 *device) 0x02000001, 0x800f0800, 0x80e40002, /* mov oC0, r2 */ 0x0000ffff /* end */ }; - IDirect3DPixelShader9 *shader; + const DWORD shader_frac_code[] = { + 0xffff0300, /* ps_3_0 */ + 0x05000051, 0xa00f0000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, /* def c0, 0.0, 0.0, 0.0, 0.0 */ + 0x0200001f, 0x80000000, 0x90031000, /* dcl vPos.xy */ + 0x02000001, 0x800f0000, 0xa0e40000, /* mov r0, c0 */ + 0x02000013, 0x80030000, 0x90541000, /* frc r0.xy, vPos.xy */ + 0x02000001, 0x800f0800, 0x80e40000, /* mov oC0, r0 */ + 0x0000ffff /* end */ + }; + IDirect3DPixelShader9 *shader, *shader_frac; IDirect3DSurface9 *surface = NULL, *backbuffer; const float quad[] = { -1.0, -1.0, 0.1, 0.0, 0.0, @@ -6923,6 +6932,8 @@ static void vpos_register_test(IDirect3DDevice9 *device) ok(hr == D3D_OK, "IDirect3DDevice9_Clear failed, hr=%s\n", DXGetErrorString9(hr)); hr = IDirect3DDevice9_CreatePixelShader(device, shader_code, &shader); ok(hr == D3D_OK, "IDirect3DDevice9_CreatePixelShader failed hr=%s\n", DXGetErrorString9(hr)); + hr = IDirect3DDevice9_CreatePixelShader(device, shader_frac_code, &shader_frac); + ok(hr == D3D_OK, "IDirect3DDevice9_CreatePixelShader failed hr=%s\n", DXGetErrorString9(hr)); hr = IDirect3DDevice9_SetPixelShader(device, shader); ok(hr == D3D_OK, "IDirect3DDevice9_SetPixelShader failed hr=%s\n", DXGetErrorString9(hr)); hr = IDirect3DDevice9_SetFVF(device, D3DFVF_XYZ); @@ -6967,8 +6978,6 @@ static void vpos_register_test(IDirect3DDevice9 *device) ok(hr == D3D_OK, "IDirect3DDevice9_DrawPrimitiveUP failed, hr=%s\n", DXGetErrorString9(hr)); hr = IDirect3DDevice9_EndScene(device); ok(hr == D3D_OK, "IDirect3DDevice9_EndScene failed hr=%s\n", DXGetErrorString9(hr)); - hr = IDirect3DDevice9_SetRenderTarget(device, 0, backbuffer); - ok(hr == D3D_OK, "IDirect3DDevice9_SetRenderTarget failed, hr=%s\n", DXGetErrorString9(hr)); } hr = IDirect3DSurface9_LockRect(surface, &lr, NULL, D3DLOCK_READONLY); ok(hr == D3D_OK, "IDirect3DSurface9_LockRect failed, hr=%s\n", DXGetErrorString9(hr)); @@ -6989,7 +6998,38 @@ static void vpos_register_test(IDirect3DDevice9 *device) hr = IDirect3DSurface9_UnlockRect(surface); ok(hr == D3D_OK, "IDirect3DSurface9_UnlockRect failed, hr=%s\n", DXGetErrorString9(hr)); + /* Test the fraction value of vPos. This is tested with the offscreen target and not the backbuffer to + * have full control over the multisampling setting inside this test + */ + hr = IDirect3DDevice9_SetPixelShader(device, shader_frac); + ok(hr == D3D_OK, "IDirect3DDevice9_SetPixelShader failed hr=%s\n", DXGetErrorString9(hr)); + hr = IDirect3DDevice9_BeginScene(device); + ok(hr == D3D_OK, "IDirect3DDevice9_BeginScene failed hr=%s\n", DXGetErrorString9(hr)); + if(SUCCEEDED(hr)) { + hr = IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET, 0xff0000ff, 0.0, 0); + ok(hr == D3D_OK, "IDirect3DDevice9_Clear failed, hr=%s\n", DXGetErrorString9(hr)); + hr = IDirect3DDevice9_DrawPrimitiveUP(device, D3DPT_TRIANGLESTRIP, 2, quad, sizeof(float) * 5); + ok(hr == D3D_OK, "IDirect3DDevice9_DrawPrimitiveUP failed, hr=%s\n", DXGetErrorString9(hr)); + hr = IDirect3DDevice9_EndScene(device); + ok(hr == D3D_OK, "IDirect3DDevice9_EndScene failed hr=%s\n", DXGetErrorString9(hr)); + } + hr = IDirect3DDevice9_SetRenderTarget(device, 0, backbuffer); + ok(hr == D3D_OK, "IDirect3DDevice9_SetRenderTarget failed, hr=%s\n", DXGetErrorString9(hr)); + + hr = IDirect3DSurface9_LockRect(surface, &lr, NULL, D3DLOCK_READONLY); + ok(hr == D3D_OK, "IDirect3DSurface9_LockRect failed, hr=%s\n", DXGetErrorString9(hr)); + + pos = (DWORD *) (((BYTE *) lr.pBits) + 14 * lr.Pitch + 14 * sizeof(DWORD)); + color = *pos & 0x00ffffff; + ok(color == 0x00000000, "vPos fraction test has color 0x%08x, expected 0x00000000\n", color); + + hr = IDirect3DSurface9_UnlockRect(surface); + ok(hr == D3D_OK, "IDirect3DSurface9_UnlockRect failed, hr=%s\n", DXGetErrorString9(hr)); + + hr = IDirect3DDevice9_SetPixelShader(device, NULL); + ok(hr == D3D_OK, "IDirect3DDevice9_SetPixelShader failed hr=%s\n", DXGetErrorString9(hr)); IDirect3DPixelShader9_Release(shader); + IDirect3DPixelShader9_Release(shader_frac); if(surface) IDirect3DSurface9_Release(surface); IDirect3DSurface9_Release(backbuffer); } diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c index 61b21fa2d43..5a0b7c5adf7 100644 --- a/dlls/wined3d/glsl_shader.c +++ b/dlls/wined3d/glsl_shader.c @@ -706,7 +706,21 @@ void shader_generate_glsl_declarations( /* Start the main program */ shader_addline(buffer, "void main() {\n"); if(pshader && reg_maps->vpos) { - shader_addline(buffer, "vpos = vec4(0, ycorrection[0], 0, 0) + gl_FragCoord * vec4(1, ycorrection[1], 1, 1) - 0.5;\n"); + /* DirectX apps expect integer values, while OpenGL drivers add approximately 0.5. This causes + * off-by-one problems as spotted by the vPos d3d9 visual test. Unfortunately the ATI cards do + * not add exactly 0.5, but rather something like 0.49999999 or 0.50000001, which still causes + * precision troubles when we just substract 0.5. + * + * To deal with that just floor() the position. This will eliminate the fraction on all cards. + * + * TODO: Test how that behaves with multisampling once we can enable multisampling in winex11. + * + * An advantage of floor is that it works even if the driver doesn't add 1/2. It is somewhat + * questionable if 1.5, 2.5, ... are the proper values to return in gl_FragCoord, even though + * coordinates specify the pixel centers instead of the pixel corners. This code will behave + * correctly on drivers that returns integer values. + */ + shader_addline(buffer, "vpos = floor(vec4(0, ycorrection[0], 0, 0) + gl_FragCoord * vec4(1, ycorrection[1], 1, 1));\n"); } }