From: Joachim Priesner Subject: wined3d: Take abs() of vertex z coordinate as FFP fog coordinate and remove oFog clamp for vertex shaders Message-Id: <201410220944.06040.joachim.priesner@web.de> Date: Wed, 22 Oct 2014 09:43:59 +0200 Supersedes 107021. (The title of that patch really was not fitting any more.) The FFP fog coordinate is not vertex.z, but the absolute value of it. The patch fixes that and adds a test. The tests also suggest that the vertex shader side clamp of oFog is incorrect. The test results are clear on that for d3d9, but vary across different Windows machines for d3d8. Therefore, the d3d8 oFog clamp tests are disabled for now. Tested on openSuse 13.1 and Windows 8.1 (VMware, Wine test bot, real machine with nVidia graphics card). --- dlls/d3d8/tests/visual.c | 335 +++++++++++++++++++++++++++++++++++++ dlls/d3d9/tests/visual.c | 399 +++++++++++++++++++++++++++++++++++++++++++++ dlls/ddraw/tests/ddraw7.c | 186 +++++++++++++++++++++ dlls/wined3d/glsl_shader.c | 4 +- 4 files changed, 922 insertions(+), 2 deletions(-) diff --git a/dlls/d3d8/tests/visual.c b/dlls/d3d8/tests/visual.c index ba73bb6..42d3e97 100644 --- a/dlls/d3d8/tests/visual.c +++ b/dlls/d3d8/tests/visual.c @@ -696,6 +696,340 @@ done: DestroyWindow(window); } +/* This test tests fog in combination with negative vertex z coordinates. */ +static void fog_negative_z_test(void) +{ + enum + { + C_ALPHA_0x00 = 0x00000000, + C_CLEAR = 0xffff00ff, + C_FOGGED = 0x0000ff00, + C_HALF_FOGGED = 0x00808000, + C_UNFOGGED = 0x00ff0000, + C_IGNORE = 0xdeadbeef + }; + + /* Fill the null-shader entry with the FVF (SetVertexShader is "overloaded" on d3d8). */ + DWORD vertex_shader[3] = {D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_SPECULAR, 0, 0}; + DWORD pixel_shader[2] = {0, 0}; + IDirect3D8 *d3d; + IDirect3DDevice8 *device; + BOOL has_vs_support, has_ps_support, has_table_fog_support; + unsigned int i, ps, y; + D3DCOLOR color, expected_middle_color; + ULONG refcount; + D3DCAPS8 caps; + HWND window; + HRESULT hr; + union + { + float f; + DWORD i; + } start, end; + + /* For all vertex shaders the view matrix goes in registers c0...c3, + * the projection matrix goes in registers c4...c7. */ + + /* Basic vertex shader without fog computation ("non foggy") */ + static const DWORD vertex_shader_code1[] = + { + 0xfffe0100, /* vs_1_0 */ + /* output.Pos = mul(input.Pos, View) */ + 0x00000009, 0x80010000, 0x90e40000, 0xa0e40000, /* dp4 r0.x, v0, c0 */ + 0x00000009, 0x80020000, 0x90e40000, 0xa0e40001, /* dp4 r0.y, v0, c1 */ + 0x00000009, 0x80040000, 0x90e40000, 0xa0e40002, /* dp4 r0.z, v0, c2 */ + 0x00000009, 0x80080000, 0x90e40000, 0xa0e40003, /* dp4 r0.w, v0, c3 */ + /* output.Pos = mul(output.Pos, Projection) */ + 0x00000009, 0xc0010000, 0x80e40000, 0xa0e40004, /* dp4 oPos.x, r0, c4 */ + 0x00000009, 0xc0020000, 0x80e40000, 0xa0e40005, /* dp4 oPos.y, r0, c5 */ + 0x00000009, 0xc0040000, 0x80e40000, 0xa0e40006, /* dp4 oPos.z, r0, c6 */ + 0x00000009, 0xc0080000, 0x80e40000, 0xa0e40007, /* dp4 oPos.w, r0, c7 */ + /* output.Color = input.Color */ + 0x00000001, 0xd00f0000, 0x90e40001, /* mov oD0, v1 */ + 0x0000ffff, /* END */ + }; + + /* Basic vertex shader with fog computation ("foggy"). + * Using the vertex' x coordinate as fog coordinate so that all test cases that use vertex fog + * will have a gradient from fully fogged (green, left) to unfogged (red, right). */ + static const DWORD vertex_shader_code2[] = + { + 0xfffe0100, /* vs_1_0 */ + /* output.Pos = mul(input.Pos, View) */ + 0x00000009, 0x80010000, 0x90e40000, 0xa0e40000, /* dp4 r0.x, v0, c0 */ + 0x00000009, 0x80020000, 0x90e40000, 0xa0e40001, /* dp4 r0.y, v0, c1 */ + 0x00000009, 0x80040000, 0x90e40000, 0xa0e40002, /* dp4 r0.z, v0, c2 */ + 0x00000009, 0x80080000, 0x90e40000, 0xa0e40003, /* dp4 r0.w, v0, c3 */ + /* output.Pos = mul(output.Pos, Projection) */ + 0x00000009, 0xc0010000, 0x80e40000, 0xa0e40004, /* dp4 oPos.x, r0, c4 */ + 0x00000009, 0xc0020000, 0x80e40000, 0xa0e40005, /* dp4 oPos.y, r0, c5 */ + 0x00000009, 0xc0040000, 0x80e40000, 0xa0e40006, /* dp4 oPos.z, r0, c6 */ + 0x00000009, 0xc0080000, 0x80e40000, 0xa0e40007, /* dp4 oPos.w, r0, c7 */ + /* output.Color = input.Color */ + 0x00000001, 0xd00f0000, 0x90e40001, /* mov oD0, v1 */ + /* output.Fog = input.Pos.x */ + 0x00000001, 0xc00f0001, 0x90000000, /* mov oFog, v0.x */ + 0x0000ffff, /* END */ + }; + + /* Basic pixel shader */ + static const DWORD pixel_shader_code[] = + { + 0xffff0101, /* ps_1_1 */ + 0x00000001, 0x800f0000, 0x90e40000, /* mov r0, v0 */ + 0x0000ffff + }; + + static const struct + { + struct vec3 position; + DWORD diffuse; + DWORD specular; + } + quad1[] = + { + {{-1.0f, -1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{-1.0f, 0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 1.0f, -1.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 1.0f, 0.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + }, + quad2[] = + { + {{-1.0f, 0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{-1.0f, 1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 0.0f, 0.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 0.0f, 1.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00}, + }, + quad3[] = + { + {{ 0.0f, 0.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 0.0f, 1.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 1.0f, 0.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 1.0f, 1.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + }; + static const DWORD vertex_decl[] = + { + D3DVSD_STREAM(0), + D3DVSD_REG(0, D3DVSDT_FLOAT3), /* position, v0 */ + D3DVSD_REG(1, D3DVSDT_D3DCOLOR), /* diffuse color, v1 */ + D3DVSD_REG(2, D3DVSDT_D3DCOLOR), /* specular color, v2 */ + D3DVSD_END() + }; + static const struct test_data_t + { + int vshader; + D3DFOGMODE vfog; + D3DFOGMODE tfog; + DWORD color_left; + DWORD color_middle_top; + DWORD color_middle_bottom; + DWORD color_right; + } + test_data[] = + { + /* Using z-based fog, the bottom quad should have a gradient UNFOGGED->FOGGED + * for table fog, and be completely fogged for vertex fog. + * When the fog coordinate returned by the vertex shader is used instead, + * the result is a gradient FOGGED->UNFOGGED. */ + + /* No vertex shader */ + {0, D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {0, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {0, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_UNFOGGED, C_FOGGED, C_FOGGED}, + {0, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED}, + + /* Vertex shader without vertex fog computation */ + {1, D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {1, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {1, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED}, + {1, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED}, + + /* Vertex shader with vertex fog computation. + * Ignore the bottom middle color as test results vary on different Windows machines. */ + {2, D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {2, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {2, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_IGNORE, C_UNFOGGED}, + {2, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_IGNORE, C_UNFOGGED}, + }; + /* Affine projection matrix for z-near = -1, z-far = 1. */ + static const D3DMATRIX proj_mat = + {{{ + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.0f, + 0.0f, 0.0f, 0.5f, 1.0f + }}}; + /* Transposed version of proj_mat to pass to the vertex shaders. */ + static const D3DMATRIX proj_mat_transposed = + {{{ + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.5f, + 0.0f, 0.0f, 0.0f, 1.0f + }}}; + static const D3DMATRIX view_mat = + {{{ + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }}}; + + window = CreateWindowA("static", "d3d8_test", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 0, 0, 640, 480, NULL, NULL, NULL, NULL); + d3d = Direct3DCreate8(D3D_SDK_VERSION); + ok(!!d3d, "Failed to create a D3D object\n"); + if (!(device = create_device(d3d, window, window, TRUE))) + { + skip("Failed to create a D3D device, skipping tests\n"); + goto done; + } + + hr = IDirect3DDevice8_GetDeviceCaps(device, &caps); + ok(SUCCEEDED(hr), "Failed to get device caps (%#x)\n", hr); + + has_table_fog_support = caps.RasterCaps & D3DPRASTERCAPS_FOGTABLE; + if (!has_table_fog_support) + skip("No table fog support, skipping some fog tests\n"); + + has_ps_support = caps.PixelShaderVersion >= D3DPS_VERSION(1, 1); + if (has_ps_support) + { + hr = IDirect3DDevice8_CreatePixelShader(device, pixel_shader_code, &pixel_shader[1]); + ok(SUCCEEDED(hr), "CreatePixelShader failed (%#x)\n", hr); + } + else + { + skip("No ps_1_1 support, skipping some fog tests\n"); + } + + has_vs_support = caps.VertexShaderVersion >= D3DVS_VERSION(1, 0); + if (has_vs_support) + { + hr = IDirect3DDevice8_CreateVertexShader(device, vertex_decl, vertex_shader_code1, &vertex_shader[1], 0); + ok(SUCCEEDED(hr), "CreateVertexShader failed (%#x)\n", hr); + hr = IDirect3DDevice8_CreateVertexShader(device, vertex_decl, vertex_shader_code2, &vertex_shader[2], 0); + ok(SUCCEEDED(hr), "CreateVertexShader failed (%#x)\n", hr); + + /* Note: Shaders expect matrices in column-major form, so they have + * to be transposed if they are not symmetrical. */ + hr = IDirect3DDevice8_SetVertexShaderConstant(device, 0, &view_mat, 4); + ok(SUCCEEDED(hr), "SetVertexShaderConstant (view matrix) failed (%#x)\n", hr); + hr = IDirect3DDevice8_SetVertexShaderConstant(device, 4, &proj_mat_transposed, 4); + ok(SUCCEEDED(hr), "SetVertexShaderConstant (projection matrix) failed (%#x)\n", hr); + } + else + { + skip("No vs_1_0 support, skipping some fog tests\n"); + } + + /* Setup initial states: No depth test, no lighting, fog on, fog color */ + hr = IDirect3DDevice8_SetRenderState(device, D3DRS_ZENABLE, D3DZB_FALSE); + ok(SUCCEEDED(hr), "Turning off depth test failed (%#x)\n", hr); + hr = IDirect3DDevice8_SetRenderState(device, D3DRS_LIGHTING, FALSE); + ok(SUCCEEDED(hr), "Turning off lighting failed (%#x)\n", hr); + hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGENABLE, TRUE); + ok(SUCCEEDED(hr), "Turning on fog calculations failed (%#x)\n", hr); + hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGCOLOR, C_FOGGED); + ok(SUCCEEDED(hr), "Setting fog color failed (%#x)\n", hr); + + /* We are using an affine projection matrix, so fog start and end are in device space coords. + * This corresponds to a fog from z=-1 to z=1. */ + start.f = 0.0f; + end.f = 1.0f; + + hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGSTART, start.i); + ok(SUCCEEDED(hr), "Setting fog start failed (%#x)\n", hr); + hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGEND, end.i); + ok(SUCCEEDED(hr), "Setting fog end failed (%#x)\n", hr); + + hr = IDirect3DDevice8_SetTransform(device, D3DTS_PROJECTION, &proj_mat); + ok(SUCCEEDED(hr), "Failed to set projection transform (%#x)\n", hr); + hr = IDirect3DDevice8_SetTransform(device, D3DTS_VIEW, &view_mat); + ok(SUCCEEDED(hr), "Failed to set view transform (%#x)\n", hr); + + for (i = 0; i < sizeof(test_data)/sizeof(test_data[0]); i++) + { + if (test_data[i].vshader != 0 && !vertex_shader[test_data[i].vshader]) + continue; + if (test_data[i].tfog != D3DFOG_NONE && !has_table_fog_support) + continue; + + hr = IDirect3DDevice8_SetVertexShader(device, vertex_shader[test_data[i].vshader]); + ok(SUCCEEDED(hr), "SetVertexShader failed (%#x)\n", hr); + + for (ps = 0; ps < sizeof(pixel_shader)/sizeof(pixel_shader[0]); ps++) + { + if (ps != 0 && !pixel_shader[ps]) + continue; + + if (has_ps_support) + { + hr = IDirect3DDevice8_SetPixelShader(device, pixel_shader[ps]); + ok(SUCCEEDED(hr), "SetPixelShader failed (%#x)\n", hr); + } + + hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGVERTEXMODE, test_data[i].vfog); + ok(SUCCEEDED(hr), "Setting fog vertex mode to %d failed (%#x)\n", test_data[i].vfog, hr); + hr = IDirect3DDevice8_SetRenderState(device, D3DRS_FOGTABLEMODE, test_data[i].tfog); + ok(SUCCEEDED(hr), "Setting fog table mode to %d failed (%#x)\n", test_data[i].tfog, hr); + + hr = IDirect3DDevice8_Clear(device, 0, NULL, D3DCLEAR_TARGET, C_CLEAR, 1.0f, 0); + ok(SUCCEEDED(hr), "Clear failed (%#x)\n", hr); + hr = IDirect3DDevice8_BeginScene(device); + ok(SUCCEEDED(hr), "BeginScene failed (%#x)\n", hr); + hr = IDirect3DDevice8_DrawPrimitiveUP(device, D3DPT_TRIANGLESTRIP, 2, &quad1[0], sizeof(quad1[0])); + ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr); + hr = IDirect3DDevice8_DrawPrimitiveUP(device, D3DPT_TRIANGLESTRIP, 2, &quad2[0], sizeof(quad2[0])); + ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr); + hr = IDirect3DDevice8_DrawPrimitiveUP(device, D3DPT_TRIANGLESTRIP, 2, &quad3[0], sizeof(quad3[0])); + ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr); + hr = IDirect3DDevice8_EndScene(device); + ok(SUCCEEDED(hr), "EndScene failed (%#x)\n", hr); + + /* Use 5% tolerance on the colors since there may be a gradient + * between left and right vertices. */ + for (y = 120; y <= 360; y += 240) + { + color = getPixelColor(device, 2, y); + ok(color_match(color, test_data[i].color_left, 13), + "fog vs%i ps%i fvm%i ftm%i, y=%i: got left color %08x, expected %08x+-5%%\n", + test_data[i].vshader, ps, test_data[i].vfog, test_data[i].tfog, y, + color, test_data[i].color_left); + expected_middle_color = + (y == 120 ? test_data[i].color_middle_top : test_data[i].color_middle_bottom); + if (expected_middle_color != C_IGNORE) + { + color = getPixelColor(device, 320, y); + ok(color_match(color, expected_middle_color, 13), + "fog vs%i ps%i fvm%i ftm%i, y=%i: got middle color %08x, expected %08x+-5%%\n", + test_data[i].vshader, ps, test_data[i].vfog, test_data[i].tfog, y, + color, expected_middle_color); + } + color = getPixelColor(device, 638, y); + ok(color_match(color, test_data[i].color_right, 13), + "fog vs%i ps%i fvm%i ftm%i, y=%i: got right color %08x, expected %08x+-5%%\n", + test_data[i].vshader, ps, test_data[i].vfog, test_data[i].tfog, y, + color, test_data[i].color_right); + } + + hr = IDirect3DDevice8_Present(device, NULL, NULL, NULL, NULL); + ok(SUCCEEDED(hr), "Present failed (%#x)\n", hr); + } + } + for (i = 0; i < sizeof(vertex_shader)/sizeof(vertex_shader[0]); i++) + if (vertex_shader[i]) + IDirect3DDevice8_DeleteVertexShader(device, vertex_shader[i]); + for (i = 0; i < sizeof(pixel_shader)/sizeof(pixel_shader[0]); i++) + if (pixel_shader[i]) + IDirect3DDevice8_DeletePixelShader(device, pixel_shader[i]); + refcount = IDirect3DDevice8_Release(device); + ok(!refcount, "Device has %u references left\n", refcount); +done: + IDirect3D8_Release(d3d); + DestroyWindow(window); +} + /* This tests fog in combination with shaders. * What's tested: linear fog (vertex and table) with pixel shader * linear table fog with non foggy vertex shader @@ -5092,6 +5426,7 @@ START_TEST(visual) offscreen_test(); alpha_test(); test_scalar_instructions(); + fog_negative_z_test(); fog_with_shader_test(); cnd_test(); p8_texture_test(); diff --git a/dlls/d3d9/tests/visual.c b/dlls/d3d9/tests/visual.c index 117c8eb..421cae0 100644 --- a/dlls/d3d9/tests/visual.c +++ b/dlls/d3d9/tests/visual.c @@ -1825,6 +1825,404 @@ done: DestroyWindow(window); } +/* This test tests fog in combination with negative vertex z coordinates. */ +static void fog_negative_z_test(void) +{ + enum + { + C_ALPHA_0x00 = 0x00000000, + C_CLEAR = 0xffff00ff, + C_FOGGED = 0x0000ff00, + C_HALF_FOGGED = 0x00808000, + C_UNFOGGED = 0x00ff0000 + }; + + IDirect3DVertexShader9 *vertex_shader[4] = {NULL, NULL, NULL, NULL}; + IDirect3DPixelShader9 *pixel_shader[3] = {NULL, NULL, NULL}; + IDirect3DVertexDeclaration9 *vertex_declaration = NULL; + IDirect3DDevice9 *device; + BOOL has_vs_support, has_ps_support, has_table_fog_support; + unsigned int i, ps, y; + IDirect3D9 *d3d; + ULONG refcount; + D3DCAPS9 caps; + DWORD color, expected_middle_color; + HWND window; + HRESULT hr; + union + { + float f; + DWORD i; + } start, end; + + /* For all vertex shaders the view matrix goes in registers c0...c3, + * the projection matrix goes in registers c4...c7. */ + + /* Basic vertex shader without fog computation ("non foggy") */ + static const DWORD vertex_shader_code1[] = + { + 0xfffe0101, /* vs_1_1 */ + 0x0000001f, 0x80000000, 0x900f0000, /* dcl_position0 v0 */ + 0x0000001f, 0x8000000a, 0x900f0001, /* dcl_color0 v1 */ + /* output.Pos = mul(input.Pos, View) */ + 0x00000009, 0x80010000, 0x90e40000, 0xa0e40000, /* dp4 r0.x, v0, c0 */ + 0x00000009, 0x80020000, 0x90e40000, 0xa0e40001, /* dp4 r0.y, v0, c1 */ + 0x00000009, 0x80040000, 0x90e40000, 0xa0e40002, /* dp4 r0.z, v0, c2 */ + 0x00000009, 0x80080000, 0x90e40000, 0xa0e40003, /* dp4 r0.w, v0, c3 */ + /* output.Pos = mul(output.Pos, Projection) */ + 0x00000009, 0xc0010000, 0x80e40000, 0xa0e40004, /* dp4 oPos.x, r0, c4 */ + 0x00000009, 0xc0020000, 0x80e40000, 0xa0e40005, /* dp4 oPos.y, r0, c5 */ + 0x00000009, 0xc0040000, 0x80e40000, 0xa0e40006, /* dp4 oPos.z, r0, c6 */ + 0x00000009, 0xc0080000, 0x80e40000, 0xa0e40007, /* dp4 oPos.w, r0, c7 */ + /* output.Color = input.Color */ + 0x00000001, 0xd00f0000, 0x90e40001, /* mov oD0, v1 */ + 0x0000ffff, /* END */ + }; + + /* Basic vertex shader with fog computation ("foggy"). + * Using the vertex' x coordinate as fog coordinate so that all test cases that use vertex fog + * will have a gradient from fully fogged (green, left) to unfogged (red, right). */ + static const DWORD vertex_shader_code2[] = + { + 0xfffe0101, /* vs_1_1 */ + 0x0000001f, 0x80000000, 0x900f0000, /* dcl_position0 v0 */ + 0x0000001f, 0x8000000a, 0x900f0001, /* dcl_color0 v1 */ + /* output.Pos = mul(input.Pos, View) */ + 0x00000009, 0x80010000, 0x90e40000, 0xa0e40000, /* dp4 r0.x, v0, c0 */ + 0x00000009, 0x80020000, 0x90e40000, 0xa0e40001, /* dp4 r0.y, v0, c1 */ + 0x00000009, 0x80040000, 0x90e40000, 0xa0e40002, /* dp4 r0.z, v0, c2 */ + 0x00000009, 0x80080000, 0x90e40000, 0xa0e40003, /* dp4 r0.w, v0, c3 */ + /* output.Pos = mul(output.Pos, Projection) */ + 0x00000009, 0xc0010000, 0x80e40000, 0xa0e40004, /* dp4 oPos.x, r0, c4 */ + 0x00000009, 0xc0020000, 0x80e40000, 0xa0e40005, /* dp4 oPos.y, r0, c5 */ + 0x00000009, 0xc0040000, 0x80e40000, 0xa0e40006, /* dp4 oPos.z, r0, c6 */ + 0x00000009, 0xc0080000, 0x80e40000, 0xa0e40007, /* dp4 oPos.w, r0, c7 */ + /* output.Color = input.Color */ + 0x00000001, 0xd00f0000, 0x90e40001, /* mov oD0, v1 */ + /* output.Fog = input.Pos.x */ + 0x00000001, 0xc00f0001, 0x90000000, /* mov oFog, v0.x */ + 0x0000ffff, /* END */ + }; + + /* Basic vertex shader with fog computation ("foggy"), vs_2_0 */ + static const DWORD vertex_shader_code3[] = + { + 0xfffe0200, /* vs_2_0 */ + 0x0200001f, 0x80000000, 0x900f0000, /* dcl_position0 v0 */ + 0x0200001f, 0x8000000a, 0x900f0001, /* dcl_color0 v1 */ + /* output.Pos = mul(input.Pos, View) */ + 0x03000009, 0x80010000, 0x90e40000, 0xa0e40000, /* dp4 r0.x, v0, c0 */ + 0x03000009, 0x80020000, 0x90e40000, 0xa0e40001, /* dp4 r0.y, v0, c1 */ + 0x03000009, 0x80040000, 0x90e40000, 0xa0e40002, /* dp4 r0.z, v0, c2 */ + 0x03000009, 0x80080000, 0x90e40000, 0xa0e40003, /* dp4 r0.w, v0, c3 */ + /* output.Pos = mul(output.Pos, Projection) */ + 0x03000009, 0xc0010000, 0x80e40000, 0xa0e40004, /* dp4 oPos.x, r0, c4 */ + 0x03000009, 0xc0020000, 0x80e40000, 0xa0e40005, /* dp4 oPos.y, r0, c5 */ + 0x03000009, 0xc0040000, 0x80e40000, 0xa0e40006, /* dp4 oPos.z, r0, c6 */ + 0x03000009, 0xc0080000, 0x80e40000, 0xa0e40007, /* dp4 oPos.w, r0, c7 */ + /* output.Color = input.Color */ + 0x02000001, 0xd00f0000, 0x90e40001, /* mov oD0, v1 */ + /* output.Fog = input.Pos.x */ + 0x02000001, 0xc00f0001, 0x90000000, /* mov oFog, v0.x */ + 0x0000ffff, /* END */ + }; + + /* Basic pixel shader */ + static const DWORD pixel_shader_code[] = + { + 0xffff0101, /* ps_1_1 */ + 0x00000001, 0x800f0000, 0x90e40000, /* mov r0, v0 */ + 0x0000ffff + }; + static const DWORD pixel_shader_code2[] = + { + 0xffff0200, /* ps_2_0 */ + 0x0200001f, 0x80000000, 0x900f0000, /* dcl v0 */ + 0x02000001, 0x800f0800, 0x90e40000, /* mov oC0, v0 */ + 0x0000ffff + }; + static const struct + { + struct vec3 position; + D3DCOLOR diffuse; + D3DCOLOR specular; + } + quad1[] = + { + {{-1.0f, -1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{-1.0f, 0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 1.0f, -1.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 1.0f, 0.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + }, + quad2[] = + { + {{-1.0f, 0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{-1.0f, 1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 0.0f, 0.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 0.0f, 1.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00}, + }, + quad3[] = + { + {{ 0.0f, 0.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 0.0f, 1.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 1.0f, 0.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 1.0f, 1.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + }; + + static const D3DVERTEXELEMENT9 decl_elements[] = + { + {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, + {0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0}, /* diffuse */ + {0, 16, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 1}, /* specular */ + D3DDECL_END() + }; + static const struct test_data_t + { + int vshader; + D3DFOGMODE vfog; + D3DFOGMODE tfog; + DWORD color_left; + DWORD color_middle_top; + DWORD color_middle_bottom; + DWORD color_right; + } + test_data[] = + { + /* Using z-based fog, the bottom quad should have a gradient UNFOGGED->FOGGED + * for table fog, and be completely fogged for vertex fog. + * When the fog coordinate returned by the vertex shader is used instead, + * the result is a gradient FOGGED->UNFOGGED in the right half of the screen. */ + + /* No vertex shader */ + {0, D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {0, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {0, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_UNFOGGED, C_FOGGED, C_FOGGED}, + {0, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED}, + + /* Vertex shader without vertex fog computation */ + {1, D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {1, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {1, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED}, + {1, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED}, + + /* Vertex shader vs_1_1 with vertex fog computation */ + {2, D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {2, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {2, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_UNFOGGED}, + {2, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_UNFOGGED}, + + /* Vertex shader vs_2_0 with vertex fog computation */ + {3, D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {3, D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {3, D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_UNFOGGED}, + {3, D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_UNFOGGED}, + }; + /* Affine projection matrix for z-near = -1, z-far = 1. */ + static const D3DMATRIX proj_mat = + {{{ + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.0f, + 0.0f, 0.0f, 0.5f, 1.0f, + }}}; + /* Transposed version of proj_mat to pass to the vertex shaders. */ + static const D3DMATRIX proj_mat_transposed = + {{{ + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.5f, + 0.0f, 0.0f, 0.0f, 1.0f, + }}}; + static const D3DMATRIX view_mat = + {{{ + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, + }}}; + + window = CreateWindowA("static", "d3d9_test", WS_OVERLAPPEDWINDOW | WS_VISIBLE, + 0, 0, 640, 480, NULL, NULL, NULL, NULL); + d3d = Direct3DCreate9(D3D_SDK_VERSION); + ok(!!d3d, "Failed to create a D3D object\n"); + if (!(device = create_device(d3d, window, window, TRUE))) + { + skip("Failed to create a D3D device, skipping tests\n"); + goto done; + } + + hr = IDirect3DDevice9_GetDeviceCaps(device, &caps); + ok(SUCCEEDED(hr), "Failed to get device caps (%#x)\n", hr); + + has_table_fog_support = caps.RasterCaps & D3DPRASTERCAPS_FOGTABLE; + if (!has_table_fog_support) + skip("No table fog support, skipping some fog tests\n"); + + has_vs_support = caps.VertexShaderVersion >= D3DVS_VERSION(1, 1); + if (has_vs_support) + { + hr = IDirect3DDevice9_CreateVertexShader(device, vertex_shader_code1, &vertex_shader[1]); + ok(SUCCEEDED(hr), "CreateVertexShader failed (%#x)\n", hr); + hr = IDirect3DDevice9_CreateVertexShader(device, vertex_shader_code2, &vertex_shader[2]); + ok(SUCCEEDED(hr), "CreateVertexShader failed (%#x)\n", hr); + + if (caps.VertexShaderVersion >= D3DVS_VERSION(2, 0)) + { + hr = IDirect3DDevice9_CreateVertexShader(device, vertex_shader_code3, &vertex_shader[3]); + ok(SUCCEEDED(hr), "CreateVertexShader failed (%#x)\n", hr); + } + else + { + skip("No vs_2_0 support, skipping some fog tests\n"); + } + + /* Note: Shaders expect matrices in column-major form, so they have + * to be transposed if they are not symmetrical. */ + hr = IDirect3DDevice9_SetVertexShaderConstantF(device, 0, (const float*)&view_mat, 4); + ok(SUCCEEDED(hr), "SetVertexShaderConstantF (view matrix) failed (%#x)\n", hr); + hr = IDirect3DDevice9_SetVertexShaderConstantF(device, 4, (const float*)&proj_mat_transposed, 4); + ok(SUCCEEDED(hr), "SetVertexShaderConstantF (projection matrix) failed (%#x)\n", hr); + } + else + { + skip("No vs_1_1 support, skipping some fog tests\n"); + } + + has_ps_support = caps.PixelShaderVersion >= D3DPS_VERSION(1, 1); + if (has_ps_support) + { + hr = IDirect3DDevice9_CreatePixelShader(device, pixel_shader_code, &pixel_shader[1]); + ok(SUCCEEDED(hr), "CreatePixelShader failed (%#x)\n", hr); + + if (caps.PixelShaderVersion >= D3DPS_VERSION(2, 0)) + { + hr = IDirect3DDevice9_CreatePixelShader(device, pixel_shader_code2, &pixel_shader[2]); + ok(SUCCEEDED(hr), "CreatePixelShader failed (%#x)\n", hr); + } + else + { + skip("No ps_2_0 support, skipping some fog tests\n"); + } + } + else + { + skip("No ps_1_1 support, skipping some fog tests\n"); + } + + hr = IDirect3DDevice9_CreateVertexDeclaration(device, decl_elements, &vertex_declaration); + ok(SUCCEEDED(hr), "CreateVertexDeclaration failed (%#x)\n", hr); + + /* Setup initial states: No depth test, no lighting, fog on, fog color */ + hr = IDirect3DDevice9_SetRenderState(device, D3DRS_ZENABLE, D3DZB_FALSE); + ok(SUCCEEDED(hr), "Turning off depth test failed (%#x)\n", hr); + hr = IDirect3DDevice9_SetRenderState(device, D3DRS_LIGHTING, FALSE); + ok(SUCCEEDED(hr), "Turning off lighting failed (%#x)\n", hr); + hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGENABLE, TRUE); + ok(SUCCEEDED(hr), "Turning on fog calculations failed (%#x)\n", hr); + hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGCOLOR, C_FOGGED); + ok(SUCCEEDED(hr), "Setting fog color failed (%#x)\n", hr); + hr = IDirect3DDevice9_SetVertexDeclaration(device, vertex_declaration); + ok(SUCCEEDED(hr), "SetVertexDeclaration failed (%#x)\n", hr); + + /* We are using an affine projection matrix, so fog start and end are in device space coords. + * This corresponds to a fog from z=-1 to z=1. */ + start.f = 0.0f; + end.f = 1.0f; + + hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGSTART, start.i); + ok(SUCCEEDED(hr), "Setting fog start failed (%#x)\n", hr); + hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGEND, end.i); + ok(SUCCEEDED(hr), "Setting fog end failed (%#x)\n", hr); + + hr = IDirect3DDevice9_SetTransform(device, D3DTS_PROJECTION, &proj_mat); + ok(SUCCEEDED(hr), "Failed to set projection transform (%#x)\n", hr); + hr = IDirect3DDevice9_SetTransform(device, D3DTS_VIEW, &view_mat); + ok(SUCCEEDED(hr), "Failed to set view transform (%#x)\n", hr); + + for (i = 0; i < sizeof(test_data)/sizeof(test_data[0]); i++) + { + if (test_data[i].vshader != 0 && !vertex_shader[test_data[i].vshader]) + continue; + if (test_data[i].tfog != D3DFOG_NONE && !has_table_fog_support) + continue; + + if (has_vs_support) + { + hr = IDirect3DDevice9_SetVertexShader(device, vertex_shader[test_data[i].vshader]); + ok(SUCCEEDED(hr), "SetVertexShader failed (%#x)\n", hr); + } + + for (ps = 0; ps < sizeof(pixel_shader)/sizeof(pixel_shader[0]); ps++) + { + if (ps != 0 && !pixel_shader[ps]) + continue; + + if (has_ps_support) + { + hr = IDirect3DDevice9_SetPixelShader(device, pixel_shader[ps]); + ok(SUCCEEDED(hr), "SetPixelShader failed (%#x)\n", hr); + } + + hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGVERTEXMODE, test_data[i].vfog); + ok(SUCCEEDED(hr), "Setting fog vertex mode to %d failed (%#x)\n", test_data[i].vfog, hr); + hr = IDirect3DDevice9_SetRenderState(device, D3DRS_FOGTABLEMODE, test_data[i].tfog); + ok(SUCCEEDED(hr), "Setting fog table mode to %d failed (%#x)\n", test_data[i].tfog, hr); + + hr = IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET, C_CLEAR, 1.0f, 0); + ok(SUCCEEDED(hr), "Clear failed (%#x)\n", hr); + hr = IDirect3DDevice9_BeginScene(device); + ok(SUCCEEDED(hr), "BeginScene failed (%#x)\n", hr); + hr = IDirect3DDevice9_DrawPrimitiveUP(device, D3DPT_TRIANGLESTRIP, 2, &quad1[0], sizeof(quad1[0])); + ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr); + hr = IDirect3DDevice9_DrawPrimitiveUP(device, D3DPT_TRIANGLESTRIP, 2, &quad2[0], sizeof(quad2[0])); + ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr); + hr = IDirect3DDevice9_DrawPrimitiveUP(device, D3DPT_TRIANGLESTRIP, 2, &quad3[0], sizeof(quad3[0])); + ok(SUCCEEDED(hr), "DrawPrimitiveUP failed (%#x)\n", hr); + hr = IDirect3DDevice9_EndScene(device); + ok(SUCCEEDED(hr), "EndScene failed (%#x)\n", hr); + + /* Use 5% tolerance on the colors since there may be a gradient + * between left and right vertices. */ + for (y = 120; y <= 360; y += 240) + { + color = getPixelColor(device, 2, y); + ok(color_match(color, test_data[i].color_left, 13), + "fog vs%i ps%i fvm%i ftm%i, y=%i: got left color %08x, expected %08x+-5%%\n", + test_data[i].vshader, ps, test_data[i].vfog, test_data[i].tfog, y, + color, test_data[i].color_left); + color = getPixelColor(device, 320, y); + expected_middle_color = + (y == 120 ? test_data[i].color_middle_top : test_data[i].color_middle_bottom); + ok(color_match(color, expected_middle_color, 13), + "fog vs%i ps%i fvm%i ftm%i, y=%i: got middle color %08x, expected %08x+-5%%\n", + test_data[i].vshader, ps, test_data[i].vfog, test_data[i].tfog, y, + color, expected_middle_color); + color = getPixelColor(device, 638, y); + ok(color_match(color, test_data[i].color_right, 13), + "fog vs%i ps%i fvm%i ftm%i, y=%i: got right color %08x, expected %08x+-5%%\n", + test_data[i].vshader, ps, test_data[i].vfog, test_data[i].tfog, y, + color, test_data[i].color_right); + } + + hr = IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL); + ok(SUCCEEDED(hr), "Present failed (%#x)\n", hr); + } + } + + for (i = 0; i < sizeof(vertex_shader)/sizeof(vertex_shader[0]); i++) + if (vertex_shader[i]) + IDirect3DVertexShader9_Release(vertex_shader[i]); + for (i = 0; i < sizeof(pixel_shader)/sizeof(pixel_shader[0]); i++) + if (pixel_shader[i]) + IDirect3DPixelShader9_Release(pixel_shader[i]); + IDirect3DVertexDeclaration9_Release(vertex_declaration); + refcount = IDirect3DDevice9_Release(device); + ok(!refcount, "Device has %u references left\n", refcount); +done: + IDirect3D9_Release(d3d); + DestroyWindow(window); +} + /* This test tests fog in combination with shaders. * What's tested: linear fog (vertex and table) with pixel shader * linear table fog with non foggy vertex shader @@ -16729,6 +17127,7 @@ START_TEST(visual) test_vshader_input(); test_vshader_float16(); stream_test(); + fog_negative_z_test(); fog_with_shader_test(); texbem_test(); texdepth_test(); diff --git a/dlls/ddraw/tests/ddraw7.c b/dlls/ddraw/tests/ddraw7.c index ec50fb8..286b828 100644 --- a/dlls/ddraw/tests/ddraw7.c +++ b/dlls/ddraw/tests/ddraw7.c @@ -3384,6 +3384,191 @@ static void test_fog_special(void) DestroyWindow(window); } +/* This test tests fog in combination with negative vertex z coordinates. */ +static void test_fog_negative_z(void) +{ + enum + { + C_ALPHA_0x00 = 0x00000000, + C_CLEAR = 0xffff00ff, + C_FOGGED = 0x0000ff00, + C_HALF_FOGGED = 0x00808000, + C_UNFOGGED = 0x00ff0000 + }; + + D3DCOLOR color, expected_middle_color; + HRESULT hr; + ULONG refcount; + BOOL has_table_fog_support; + unsigned int i, y; + HWND window; + IDirect3DDevice7 *device; + IDirectDrawSurface7 *rt; + D3DDEVICEDESC7 caps; + + union + { + float f; + DWORD d; + } start, end; + + static struct + { + struct vec3 position; + D3DCOLOR diffuse; + D3DCOLOR specular; + } + quad1[] = + { + {{-1.0f, 0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 1.0f, 0.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{-1.0f, -1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 1.0f, -1.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + }, + quad2[] = + { + {{-1.0f, 1.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 0.0f, 1.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{-1.0f, 0.0f, -1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 0.0f, 0.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00}, + }, + quad3[] = + { + {{ 0.0f, 1.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 1.0f, 1.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 0.0f, 0.0f, 0.0f}, C_UNFOGGED, C_ALPHA_0x00}, + {{ 1.0f, 0.0f, 1.0f}, C_UNFOGGED, C_ALPHA_0x00}, + }; + static const struct test_data_t + { + DWORD vertexmode, tablemode; + D3DCOLOR color_left, color_middle_top, color_middle_bottom, color_right; + } + test_data[] = + { + {D3DFOG_NONE, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {D3DFOG_LINEAR, D3DFOG_LINEAR, C_UNFOGGED, C_HALF_FOGGED, C_HALF_FOGGED, C_FOGGED}, + {D3DFOG_LINEAR, D3DFOG_NONE, C_FOGGED, C_UNFOGGED, C_FOGGED, C_FOGGED}, + {D3DFOG_NONE, D3DFOG_NONE, C_FOGGED, C_FOGGED, C_FOGGED, C_FOGGED}, + }; + /* Affine projection matrix for z-near = -1, z-far = 1. */ + static D3DMATRIX proj_mat = + { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.5f, 0.0f, + 0.0f, 0.0f, 0.5f, 1.0f + }; + static D3DMATRIX view_mat = + { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW, + 0, 0, 640, 480, 0, 0, 0, 0); + + if (!(device = create_device(window, DDSCL_NORMAL))) + { + skip("Failed to create a 3D device, skipping test.\n"); + DestroyWindow(window); + return; + } + + memset(&caps, 0, sizeof(caps)); + hr = IDirect3DDevice7_GetCaps(device, &caps); + ok(SUCCEEDED(hr), "IDirect3DDevice7_GetCaps failed, hr %#x.\n", hr); + + has_table_fog_support = caps.dpcTriCaps.dwRasterCaps & D3DPRASTERCAPS_FOGTABLE; + if (!has_table_fog_support) + skip("No table fog support, skipping some fog tests.\n"); + + hr = IDirect3DDevice7_GetRenderTarget(device, &rt); + ok(SUCCEEDED(hr), "Failed to get render target, hr %#x.\n", hr); + + hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGENABLE, TRUE); + ok(SUCCEEDED(hr), "Failed to enable fog, hr %#x.\n", hr); + hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGCOLOR, C_FOGGED); + ok(SUCCEEDED(hr), "Failed to set fog color, hr %#x.\n", hr); + hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_LIGHTING, FALSE); + ok(SUCCEEDED(hr), "Failed to disable lighting, hr %#x.\n", hr); + hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_ZENABLE, D3DZB_FALSE); + ok(SUCCEEDED(hr), "Failed to disable depth test, hr %#x.\n", hr); + + /* We are using an affine projection matrix, so fog start and end are in device space coords. + * This corresponds to a fog from z=-1 to z=1. */ + start.f = 0.0f; + end.f = 1.0f; + + hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGSTART, start.d); + ok(SUCCEEDED(hr), "Failed to set fog start, hr %#x.\n", hr); + hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGEND, end.d); + ok(SUCCEEDED(hr), "Failed to set fog end, hr %#x.\n", hr); + + hr = IDirect3DDevice7_SetTransform(device, D3DTRANSFORMSTATE_PROJECTION, &proj_mat); + ok(SUCCEEDED(hr), "Failed to set projection transform, hr %#x.\n", hr); + hr = IDirect3DDevice7_SetTransform(device, D3DTRANSFORMSTATE_VIEW, &view_mat); + ok(SUCCEEDED(hr), "Failed to set view transform, hr %#x.\n", hr); + + for (i = 0; i < sizeof(test_data)/sizeof(test_data[0]); i++) + { + if (test_data[i].tablemode != D3DFOG_NONE && !has_table_fog_support) + continue; + + hr = IDirect3DDevice7_Clear(device, 0, NULL, D3DCLEAR_TARGET, C_CLEAR, 1.0f, 0); + ok(SUCCEEDED(hr), "Failed to clear render target, i=%d, hr %#x.\n", i, hr); + + hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGVERTEXMODE, test_data[i].vertexmode); + ok(SUCCEEDED(hr), "Failed to set fog vertex mode, i=%d, hr %#x.\n", i, hr); + hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_FOGTABLEMODE, test_data[i].tablemode); + ok(SUCCEEDED(hr), "Failed to set fog table mode, i=%d, hr %#x.\n", i, hr); + + hr = IDirect3DDevice7_BeginScene(device); + ok(SUCCEEDED(hr), "Failed to begin scene, i=%d, hr %#x.\n", i, hr); + hr = IDirect3DDevice7_DrawPrimitive(device, D3DPT_TRIANGLESTRIP, + D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_SPECULAR, quad1, 4, 0); + ok(SUCCEEDED(hr), "Failed to draw, i=%d, hr %#x.\n", i, hr); + hr = IDirect3DDevice7_DrawPrimitive(device, D3DPT_TRIANGLESTRIP, + D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_SPECULAR, quad2, 4, 0); + ok(SUCCEEDED(hr), "Failed to draw, i=%d, hr %#x.\n", i, hr); + hr = IDirect3DDevice7_DrawPrimitive(device, D3DPT_TRIANGLESTRIP, + D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_SPECULAR, quad3, 4, 0); + ok(SUCCEEDED(hr), "Failed to draw, i=%d, hr %#x.\n", i, hr); + hr = IDirect3DDevice7_EndScene(device); + ok(SUCCEEDED(hr), "Failed to end scene, i=%d, hr %#x.\n", i, hr); + + /* Use 5% tolerance on the colors since there may be a gradient + * between left and right vertices. */ + for (y = 120; y <= 360; y += 240) + { + color = get_surface_color(rt, 2, y); + ok(compare_color(color, test_data[i].color_left, 13), + "fog fvm%i ftm%i y=%i: got left color %08x, expected %08x+-5%%.\n", + test_data[i].vertexmode, test_data[i].tablemode, color, y, + test_data[i].color_left); + color = get_surface_color(rt, 320, y); + expected_middle_color = + (y == 120 ? test_data[i].color_middle_top : test_data[i].color_middle_bottom); + ok(compare_color(color, expected_middle_color, 13), + "fog fvm%i ftm%i y=%i: got middle color %08x, expected %08x+-5%%.\n", + test_data[i].vertexmode, test_data[i].tablemode, y, + color, expected_middle_color); + color = get_surface_color(rt, 638, y); + ok(compare_color(color, test_data[i].color_right, 13), + "fog fvm%i ftm%i y=%i: got right color %08x, expected %08x+-5%%.\n", + test_data[i].vertexmode, test_data[i].tablemode, y, + color, test_data[i].color_right); + } + } + + IDirectDrawSurface7_Release(rt); + refcount = IDirect3DDevice7_Release(device); + ok(!refcount, "Device has %u references left.\n", refcount); + DestroyWindow(window); +} + static void test_lighting_interface_versions(void) { IDirect3DDevice7 *device; @@ -7671,6 +7856,7 @@ START_TEST(ddraw7) test_clear_rect_count(); test_coop_level_versions(); test_fog_special(); + test_fog_negative_z(); test_lighting_interface_versions(); test_coop_level_activateapp(); test_texturemanage(); diff --git a/dlls/wined3d/glsl_shader.c b/dlls/wined3d/glsl_shader.c index 7379ba2..b78e59e 100644 --- a/dlls/wined3d/glsl_shader.c +++ b/dlls/wined3d/glsl_shader.c @@ -4352,7 +4352,7 @@ static GLhandleARB generate_param_reorder_function(struct wined3d_shader_buffer } else if (shader_match_semantic(semantic_name, WINED3D_DECL_USAGE_FOG)) { - shader_addline(buffer, "gl_FogFragCoord = clamp(vs_out[%u].%c, 0.0, 1.0);\n", i, reg_mask[1]); + shader_addline(buffer, "gl_FogFragCoord = vs_out[%u].%c;\n", i, reg_mask[1]); } } shader_addline(buffer, "}\n"); @@ -5005,7 +5005,7 @@ static GLhandleARB shader_glsl_generate_ffp_vertex_shader(struct wined3d_shader_ /* Need to undo the [0.0 - 1.0] -> [-1.0 - 1.0] transformation from D3D to GL coordinates. */ shader_addline(buffer, "gl_FogFragCoord = gl_Position.z * 0.5 + 0.5;\n"); else - shader_addline(buffer, "gl_FogFragCoord = ec_pos.z;\n"); + shader_addline(buffer, "gl_FogFragCoord = abs(ec_pos.z);\n"); break; default: -- 1.8.4.5