From baab5a869af359a3407075c8329c4b00bb12ccba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20D=C3=B6singer?= Date: Sun, 23 Aug 2015 21:59:34 +0200 Subject: [PATCH] ddraw/tests: Test color keying precision. This brute force tests all values in 4, 5, 6 and 8 bit channels. --- dlls/ddraw/tests/ddraw1.c | 335 ++++++++++++++++++++++++++++++++++++++ dlls/ddraw/tests/ddraw2.c | 308 +++++++++++++++++++++++++++++++++++ dlls/ddraw/tests/ddraw4.c | 318 ++++++++++++++++++++++++++++++++++++ dlls/ddraw/tests/ddraw7.c | 302 ++++++++++++++++++++++++++++++++++ 4 files changed, 1263 insertions(+) diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c index f97b9643ea0..676d3576d0f 100644 --- a/dlls/ddraw/tests/ddraw1.c +++ b/dlls/ddraw/tests/ddraw1.c @@ -7279,6 +7279,340 @@ done: DestroyWindow(window); } +static void test_colorkey_precision(void) +{ + static D3DTLVERTEX quad[] = + { + {{ 0.0f}, {480.0f}, {0.0f}, {1.0f}, {0x00000000}, {0x00000000}, {0.0f}, {1.0f}}, + {{ 0.0f}, { 0.0f}, {0.0f}, {1.0f}, {0x00000000}, {0x00000000}, {0.0f}, {0.0f}}, + {{640.0f}, {480.0f}, {0.0f}, {1.0f}, {0x00000000}, {0x00000000}, {1.0f}, {1.0f}}, + {{640.0f}, { 0.0f}, {0.0f}, {1.0f}, {0x00000000}, {0x00000000}, {1.0f}, {0.0f}}, + }; + static D3DRECT clear_rect = {{0}, {0}, {640}, {480}}; + IDirect3DDevice *device; + IDirectDraw *ddraw; + IDirectDrawSurface *rt; + IDirect3DViewport *viewport; + IDirect3DExecuteBuffer *execute_buffer; + D3DEXECUTEBUFFERDESC exec_desc; + UINT inst_length; + void *ptr; + HWND window; + HRESULT hr; + IDirectDrawSurface *src, *dst, *texture; + D3DTEXTUREHANDLE handle; + IDirect3DTexture *d3d_texture; + IDirect3DMaterial *green; + DDSURFACEDESC surface_desc, lock_desc; + ULONG refcount; + D3DCOLOR color; + unsigned int t, c; + DDCOLORKEY ckey; + DDBLTFX fx; + DWORD data[4] = {0}, color_mask; + D3DDEVICEDESC device_desc, hel_desc; + BOOL warp; + static const struct + { + unsigned int max, shift, bpp, clear; + const char *name; + DDPIXELFORMAT fmt; + } + tests[] = + { + { + 255, 0, 4, 0x00345678, "D3DFMT_X8R8G8B8", + { + sizeof(DDPIXELFORMAT), DDPF_RGB, 0, + {32}, {0x00ff0000}, {0x0000ff00}, {0x000000ff}, {0x00000000} + } + + }, + { + 63, 5, 2, 0x5678, "D3DFMT_R5G6B5, G channel", + { + sizeof(DDPIXELFORMAT), DDPF_RGB, 0, + {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000} + } + + }, + { + 31, 0, 2, 0x5678, "D3DFMT_R5G6B5, B channel", + { + sizeof(DDPIXELFORMAT), DDPF_RGB, 0, + {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000} + } + + }, + { + 15, 0, 2, 0x0678, "D3DFMT_A4R4G4B4", + { + sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_ALPHAPIXELS, 0, + {16}, {0x0f00}, {0x00f0}, {0x000f}, {0xf000} + } + + }, + }; + + window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW, + 0, 0, 640, 480, 0, 0, 0, 0); + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + if (!(device = create_device(ddraw, window, DDSCL_NORMAL))) + { + skip("Failed to create a 3D device, skipping test.\n"); + DestroyWindow(window); + IDirectDraw_Release(ddraw); + return; + } + hr = IDirect3DDevice_QueryInterface(device, &IID_IDirectDrawSurface, (void **)&rt); + ok(SUCCEEDED(hr), "Failed to get render target, hr %#x.\n", hr); + + /* The Windows 8 WARP driver has plenty of false negatives in X8R8G8B8 + * (color key doesn't match although the values are equal), and a false + * positive when the color key is 0 and the texture contains the value 1. + * I don't want to mark this broken unconditionally since this would + * essentially disable the test on Windows. Try to detect WARP (and I + * guess mismatch other SW renderers) by its ability to texture from + * system memory. Also on random occasions 254 == 255 and 255 != 255.*/ + memset(&device_desc, 0, sizeof(device_desc)); + device_desc.dwSize = sizeof(device_desc); + memset(&hel_desc, 0, sizeof(hel_desc)); + hel_desc.dwSize = sizeof(hel_desc); + hr = IDirect3DDevice_GetCaps(device, &device_desc, &hel_desc); + ok(SUCCEEDED(hr), "Failed to get device caps, hr %#x.\n", hr); + warp = !!(device_desc.dwDevCaps & D3DDEVCAPS_TEXTURESYSTEMMEMORY); + + green = create_diffuse_material(device, 0.0f, 1.0f, 0.0f, 0.0f); + viewport = create_viewport(device, 0, 0, 640, 480); + viewport_set_background(device, viewport, green); + + memset(&exec_desc, 0, sizeof(exec_desc)); + exec_desc.dwSize = sizeof(exec_desc); + exec_desc.dwFlags = D3DDEB_BUFSIZE | D3DDEB_CAPS; + exec_desc.dwBufferSize = 1024; + exec_desc.dwCaps = D3DDEBCAPS_SYSTEMMEMORY; + hr = IDirect3DDevice_CreateExecuteBuffer(device, &exec_desc, &execute_buffer, NULL); + ok(SUCCEEDED(hr), "Failed to create execute buffer, hr %#x.\n", hr); + + memset(&fx, 0, sizeof(fx)); + fx.dwSize = sizeof(fx); + memset(&lock_desc, 0, sizeof(lock_desc)); + lock_desc.dwSize = sizeof(lock_desc); + + for (t = 0; t < sizeof(tests) / sizeof(*tests); ++t) + { + memset(&surface_desc, 0, sizeof(surface_desc)); + surface_desc.dwSize = sizeof(surface_desc); + surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + surface_desc.dwWidth = 4; + surface_desc.dwHeight = 1; + surface_desc.ddpfPixelFormat = tests[t].fmt; + /* Windows XP (at least with the r200 driver, other drivers untested) produces + * garbage when doing color keyed texture->texture blits. */ + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &src, NULL); + ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr); + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &dst, NULL); + ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr); + + fx.dwFillColor = tests[t].clear; + /* On the w8 testbot (WARP driver) the blit result has different values in the + * X channel. */ + color_mask = U2(tests[t].fmt).dwRBitMask + | U3(tests[t].fmt).dwGBitMask + | U4(tests[t].fmt).dwBBitMask; + + for (c = 0; c <= tests[t].max; ++c) + { + /* The idiotic Nvidia Windows driver can't change the color key on a d3d + * texture after it has been set once... */ + surface_desc.dwFlags |= DDSD_CKSRCBLT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE; + surface_desc.ddckCKSrcBlt.dwColorSpaceLowValue = c << tests[t].shift; + surface_desc.ddckCKSrcBlt.dwColorSpaceHighValue = c << tests[t].shift; + hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &texture, NULL); + ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr); + + hr = IDirectDrawSurface_QueryInterface(texture, &IID_IDirect3DTexture, (void **)&d3d_texture); + ok(SUCCEEDED(hr), "Failed to get texture interface, hr %#x.\n", hr); + hr = IDirect3DTexture_GetHandle(d3d_texture, device, &handle); + ok(SUCCEEDED(hr), "Failed to get texture handle, hr %#x.\n", hr); + IDirect3DTexture_Release(d3d_texture); + + hr = IDirect3DExecuteBuffer_Lock(execute_buffer, &exec_desc); + ok(SUCCEEDED(hr), "Failed to lock execute buffer, hr %#x.\n", hr); + + memcpy(exec_desc.lpData, quad, sizeof(quad)); + + ptr = ((BYTE *)exec_desc.lpData) + sizeof(quad); + emit_process_vertices(&ptr, D3DPROCESSVERTICES_COPY, 0, 8); + emit_set_rs(&ptr, D3DRENDERSTATE_ZENABLE, D3DZB_FALSE); + emit_set_rs(&ptr, D3DRENDERSTATE_TEXTUREHANDLE, handle); + emit_set_rs(&ptr, D3DRENDERSTATE_TEXTUREMAPBLEND, D3DTBLEND_MODULATEALPHA); + /* D3DRENDERSTATE_COLORKEYENABLE is supposed to be on by default on version + * 1 devices, but for some reason it randomly defaults to FALSE on the W8 + * testbot. This is either the fault of Windows 8 or the WARP driver. + * Also D3DRENDERSTATE_COLORKEYENABLE was introduced in D3D 5 aka version 2 + * devices only, which might imply this doesn't actually do anything on + * WARP. */ + emit_set_rs(&ptr, D3DRENDERSTATE_COLORKEYENABLE, TRUE); + + emit_tquad(&ptr, 0); + emit_tquad(&ptr, 4); + emit_set_rs(&ptr, D3DRENDERSTATE_TEXTUREHANDLE, 0); + emit_end(&ptr); + + inst_length = (BYTE *)ptr - (BYTE *)exec_desc.lpData; + inst_length -= sizeof(quad); + hr = IDirect3DExecuteBuffer_Unlock(execute_buffer); + ok(SUCCEEDED(hr), "Failed to unlock execute buffer, hr %#x.\n", hr); + set_execute_data(execute_buffer, 8, sizeof(quad), inst_length); + + hr = IDirectDrawSurface_Blt(dst, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx); + ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr); + + hr = IDirectDrawSurface_Lock(src, NULL, &lock_desc, DDLOCK_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr); + switch (tests[t].bpp) + { + case 4: + ((DWORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift; + ((DWORD *)lock_desc.lpSurface)[1] = c << tests[t].shift; + ((DWORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift; + ((DWORD *)lock_desc.lpSurface)[3] = 0xffffffff; + break; + + case 2: + ((WORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift; + ((WORD *)lock_desc.lpSurface)[1] = c << tests[t].shift; + ((WORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift; + ((WORD *)lock_desc.lpSurface)[3] = 0xffff; + break; + } + hr = IDirectDrawSurface_Unlock(src, 0); + ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr); + hr = IDirectDrawSurface_Blt(texture, NULL, src, NULL, DDBLT_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr); + + ckey.dwColorSpaceLowValue = c << tests[t].shift; + ckey.dwColorSpaceHighValue = c << tests[t].shift; + hr = IDirectDrawSurface_SetColorKey(src, DDCKEY_SRCBLT, &ckey); + ok(SUCCEEDED(hr), "Failed to set color key, hr %#x.\n", hr); + + hr = IDirectDrawSurface_Blt(dst, NULL, src, NULL, DDBLT_KEYSRC | DDBLT_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr); + + /* Don't make this read only, it somehow breaks the detection of the Nvidia bug below. */ + hr = IDirectDrawSurface_Lock(dst, NULL, &lock_desc, DDLOCK_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr); + switch (tests[t].bpp) + { + case 4: + data[0] = ((DWORD *)lock_desc.lpSurface)[0] & color_mask; + data[1] = ((DWORD *)lock_desc.lpSurface)[1] & color_mask; + data[2] = ((DWORD *)lock_desc.lpSurface)[2] & color_mask; + data[3] = ((DWORD *)lock_desc.lpSurface)[3] & color_mask; + break; + + case 2: + data[0] = ((WORD *)lock_desc.lpSurface)[0] & color_mask; + data[1] = ((WORD *)lock_desc.lpSurface)[1] & color_mask; + data[2] = ((WORD *)lock_desc.lpSurface)[2] & color_mask; + data[3] = ((WORD *)lock_desc.lpSurface)[3] & color_mask; + break; + } + hr = IDirectDrawSurface_Unlock(dst, 0); + ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr); + + if (!c) + { + ok(data[0] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + tests[t].clear, data[0], tests[t].name, c); + + if (data[3] == tests[t].clear) + { + /* My Geforce GTX 460 on Windows 7 misbehaves when A4R4G4B4 is blitted with color + * keying: The blit takes ~0.5 seconds, and subsequent color keying draws are broken, + * even when a different surface is used. The blit itself doesn't draw anything, + * so we can detect the bug by looking at the otherwise unused 4th texel. It should + * never be masked out by the key. + * + * Also appears to affect the testbot in some way with R5G6B5. Color keying is + * terrible on WARP. */ + skip("Nvidia A4R4G4B4 color keying blit bug detected, skipping.\n"); + IDirectDrawSurface_Release(texture); + IDirectDrawSurface_Release(src); + IDirectDrawSurface_Release(dst); + goto done; + } + } + else + ok(data[0] == (c - 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + (c - 1) << tests[t].shift, data[0], tests[t].name, c); + + ok(data[1] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + tests[t].clear, data[1], tests[t].name, c); + + if (c == tests[t].max) + ok(data[2] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + tests[t].clear, data[2], tests[t].name, c); + else + ok(data[2] == (c + 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + (c + 1) << tests[t].shift, data[2], tests[t].name, c); + + hr = IDirect3DViewport_Clear(viewport, 1, &clear_rect, D3DCLEAR_TARGET); + ok(SUCCEEDED(hr), "Failed to clear, hr %#x.\n", hr); + + hr = IDirect3DDevice_BeginScene(device); + ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr); + hr = IDirect3DDevice_Execute(device, execute_buffer, viewport, D3DEXECUTE_UNCLIPPED); + ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr); + hr = IDirect3DDevice_EndScene(device); + ok(SUCCEEDED(hr), "Failed to end scene, hr %#x.\n", hr); + + color = get_surface_color(rt, 80, 240); + if (!c) + ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + else + ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + + color = get_surface_color(rt, 240, 240); + ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + + color = get_surface_color(rt, 400, 240); + if (c == tests[t].max) + ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + else + ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + + IDirectDrawSurface_Release(texture); + } + IDirectDrawSurface_Release(src); + IDirectDrawSurface_Release(dst); + } +done: + + destroy_viewport(device, viewport); + destroy_material(green); + IDirectDrawSurface_Release(rt); + IDirect3DExecuteBuffer_Release(execute_buffer); + IDirect3DDevice_Release(device); + refcount = IDirectDraw_Release(ddraw); + ok(refcount == 0, "Ddraw object not properly released, refcount %u.\n", refcount); + DestroyWindow(window); +} + START_TEST(ddraw1) { IDirectDraw *ddraw; @@ -7348,4 +7682,5 @@ START_TEST(ddraw1) test_texturemapblend(); test_viewport_clear_rect(); test_color_fill(); + test_colorkey_precision(); } diff --git a/dlls/ddraw/tests/ddraw2.c b/dlls/ddraw/tests/ddraw2.c index 0cb1ca4b0b6..d1f94fca93b 100644 --- a/dlls/ddraw/tests/ddraw2.c +++ b/dlls/ddraw/tests/ddraw2.c @@ -8435,6 +8435,313 @@ done: DestroyWindow(window); } +static void test_colorkey_precision(void) +{ + static D3DLVERTEX quad[] = + { + {{-1.0f}, {-1.0f}, {0.0f}, 0, {0xff000000}, {0}, {0.0f}, {0.0f}}, + {{-1.0f}, { 1.0f}, {0.0f}, 0, {0xff000000}, {0}, {0.0f}, {1.0f}}, + {{ 1.0f}, {-1.0f}, {0.0f}, 0, {0xff000000}, {0}, {1.0f}, {0.0f}}, + {{ 1.0f}, { 1.0f}, {0.0f}, 0, {0xff000000}, {0}, {1.0f}, {1.0f}}, + }; + static D3DRECT clear_rect = {{0}, {0}, {640}, {480}}; + IDirect3DDevice2 *device; + IDirectDraw2 *ddraw; + IDirectDrawSurface *rt; + IDirect3DViewport2 *viewport; + HWND window; + HRESULT hr; + IDirectDrawSurface *src, *dst, *texture; + D3DTEXTUREHANDLE handle; + IDirect3DTexture2 *d3d_texture; + IDirect3DMaterial2 *green; + DDSURFACEDESC surface_desc, lock_desc; + ULONG refcount; + D3DCOLOR color; + unsigned int t, c; + DDCOLORKEY ckey; + DDBLTFX fx; + DWORD data[4] = {0}, color_mask; + D3DDEVICEDESC device_desc, hel_desc; + BOOL warp; + static const struct + { + unsigned int max, shift, bpp, clear; + const char *name; + DDPIXELFORMAT fmt; + } + tests[] = + { + { + 255, 0, 4, 0x00345678, "D3DFMT_X8R8G8B8", + { + sizeof(DDPIXELFORMAT), DDPF_RGB, 0, + {32}, {0x00ff0000}, {0x0000ff00}, {0x000000ff}, {0x00000000} + } + + }, + { + 63, 5, 2, 0x5678, "D3DFMT_R5G6B5, G channel", + { + sizeof(DDPIXELFORMAT), DDPF_RGB, 0, + {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000} + } + + }, + { + 31, 0, 2, 0x5678, "D3DFMT_R5G6B5, B channel", + { + sizeof(DDPIXELFORMAT), DDPF_RGB, 0, + {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000} + } + + }, + { + 15, 0, 2, 0x0678, "D3DFMT_A4R4G4B4", + { + sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_ALPHAPIXELS, 0, + {16}, {0x0f00}, {0x00f0}, {0x000f}, {0xf000} + } + + }, + }; + + window = CreateWindowA("static", "ddraw_test", WS_OVERLAPPEDWINDOW, + 0, 0, 640, 480, 0, 0, 0, 0); + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + if (!(device = create_device(ddraw, window, DDSCL_NORMAL))) + { + skip("Failed to create a 3D device, skipping test.\n"); + DestroyWindow(window); + IDirectDraw2_Release(ddraw); + return; + } + hr = IDirect3DDevice2_GetRenderTarget(device, &rt); + ok(SUCCEEDED(hr), "Failed to get render target, hr %#x.\n", hr); + + /* The Windows 8 WARP driver has plenty of false negatives in X8R8G8B8 + * (color key doesn't match although the values are equal), and a false + * positive when the color key is 0 and the texture contains the value 1. + * I don't want to mark this broken unconditionally since this would + * essentially disable the test on Windows. Try to detect WARP (and I + * guess mismatch other SW renderers) by its ability to texture from + * system memory. Also on random occasions 254 == 255 and 255 != 255.*/ + memset(&device_desc, 0, sizeof(device_desc)); + device_desc.dwSize = sizeof(device_desc); + memset(&hel_desc, 0, sizeof(hel_desc)); + hel_desc.dwSize = sizeof(hel_desc); + hr = IDirect3DDevice2_GetCaps(device, &device_desc, &hel_desc); + ok(SUCCEEDED(hr), "Failed to get device caps, hr %#x.\n", hr); + warp = !!(device_desc.dwDevCaps & D3DDEVCAPS_TEXTURESYSTEMMEMORY); + + green = create_diffuse_material(device, 0.0f, 1.0f, 0.0f, 0.0f); + viewport = create_viewport(device, 0, 0, 640, 480); + viewport_set_background(device, viewport, green); + hr = IDirect3DDevice2_SetCurrentViewport(device, viewport); + ok(SUCCEEDED(hr), "Failed to activate the viewport, hr %#x.\n", hr); + + hr = IDirect3DDevice2_SetRenderState(device, D3DRENDERSTATE_ZENABLE, D3DZB_FALSE); + ok(SUCCEEDED(hr), "Failed to disable z-buffering, hr %#x.\n", hr); + hr = IDirect3DDevice2_SetRenderState(device, D3DRENDERSTATE_COLORKEYENABLE, TRUE); + ok(SUCCEEDED(hr), "Failed to enable color keying, hr %#x.\n", hr); + /* There's no way to ignore the texture color in d3d2, so multiply the texture color + * with a black vertex color. */ + hr = IDirect3DDevice2_SetRenderState(device, D3DRENDERSTATE_TEXTUREMAPBLEND, D3DTBLEND_MODULATEALPHA); + ok(SUCCEEDED(hr), "Failed to set render state, hr %#x.\n", hr); + + memset(&fx, 0, sizeof(fx)); + fx.dwSize = sizeof(fx); + memset(&lock_desc, 0, sizeof(lock_desc)); + lock_desc.dwSize = sizeof(lock_desc); + + for (t = 0; t < sizeof(tests) / sizeof(*tests); ++t) + { + memset(&surface_desc, 0, sizeof(surface_desc)); + surface_desc.dwSize = sizeof(surface_desc); + surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + surface_desc.dwWidth = 4; + surface_desc.dwHeight = 1; + surface_desc.ddpfPixelFormat = tests[t].fmt; + /* Windows XP (at least with the r200 driver, other drivers untested) produces + * garbage when doing color keyed texture->texture blits. */ + hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &src, NULL); + ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr); + hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &dst, NULL); + ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr); + + fx.dwFillColor = tests[t].clear; + /* On the w8 testbot (WARP driver) the blit result has different values in the + * X channel. */ + color_mask = U2(tests[t].fmt).dwRBitMask + | U3(tests[t].fmt).dwGBitMask + | U4(tests[t].fmt).dwBBitMask; + + for (c = 0; c <= tests[t].max; ++c) + { + /* The idiotic Nvidia Windows driver can't change the color key on a d3d + * texture after it has been set once... */ + surface_desc.dwFlags |= DDSD_CKSRCBLT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE; + surface_desc.ddckCKSrcBlt.dwColorSpaceLowValue = c << tests[t].shift; + surface_desc.ddckCKSrcBlt.dwColorSpaceHighValue = c << tests[t].shift; + hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &texture, NULL); + ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr); + + hr = IDirectDrawSurface4_QueryInterface(texture, &IID_IDirect3DTexture2, (void **)&d3d_texture); + ok(SUCCEEDED(hr), "Failed to get texture interface, hr %#x.\n", hr); + hr = IDirect3DTexture2_GetHandle(d3d_texture, device, &handle); + ok(SUCCEEDED(hr), "Failed to get texture handle, hr %#x.\n", hr); + hr = IDirect3DDevice2_SetRenderState(device, D3DRENDERSTATE_TEXTUREHANDLE, handle); + ok(SUCCEEDED(hr), "Failed to set texture handle, hr %#x.\n", hr); + IDirect3DTexture2_Release(d3d_texture); + + hr = IDirectDrawSurface_Blt(dst, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx); + ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr); + + hr = IDirectDrawSurface_Lock(src, NULL, &lock_desc, DDLOCK_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr); + switch (tests[t].bpp) + { + case 4: + ((DWORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift; + ((DWORD *)lock_desc.lpSurface)[1] = c << tests[t].shift; + ((DWORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift; + ((DWORD *)lock_desc.lpSurface)[3] = 0xffffffff; + break; + + case 2: + ((WORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift; + ((WORD *)lock_desc.lpSurface)[1] = c << tests[t].shift; + ((WORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift; + ((WORD *)lock_desc.lpSurface)[3] = 0xffff; + break; + } + hr = IDirectDrawSurface_Unlock(src, 0); + ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr); + hr = IDirectDrawSurface_Blt(texture, NULL, src, NULL, DDBLT_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr); + + ckey.dwColorSpaceLowValue = c << tests[t].shift; + ckey.dwColorSpaceHighValue = c << tests[t].shift; + hr = IDirectDrawSurface_SetColorKey(src, DDCKEY_SRCBLT, &ckey); + ok(SUCCEEDED(hr), "Failed to set color key, hr %#x.\n", hr); + + hr = IDirectDrawSurface_Blt(dst, NULL, src, NULL, DDBLT_KEYSRC | DDBLT_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr); + + /* Don't make this read only, it somehow breaks the detection of the Nvidia bug below. */ + hr = IDirectDrawSurface_Lock(dst, NULL, &lock_desc, DDLOCK_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr); + switch (tests[t].bpp) + { + case 4: + data[0] = ((DWORD *)lock_desc.lpSurface)[0] & color_mask; + data[1] = ((DWORD *)lock_desc.lpSurface)[1] & color_mask; + data[2] = ((DWORD *)lock_desc.lpSurface)[2] & color_mask; + data[3] = ((DWORD *)lock_desc.lpSurface)[3] & color_mask; + break; + + case 2: + data[0] = ((WORD *)lock_desc.lpSurface)[0] & color_mask; + data[1] = ((WORD *)lock_desc.lpSurface)[1] & color_mask; + data[2] = ((WORD *)lock_desc.lpSurface)[2] & color_mask; + data[3] = ((WORD *)lock_desc.lpSurface)[3] & color_mask; + break; + } + hr = IDirectDrawSurface_Unlock(dst, 0); + ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr); + + if (!c) + { + ok(data[0] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + tests[t].clear, data[0], tests[t].name, c); + + if (data[3] == tests[t].clear) + { + /* My Geforce GTX 460 on Windows 7 misbehaves when A4R4G4B4 is blitted with color + * keying: The blit takes ~0.5 seconds, and subsequent color keying draws are broken, + * even when a different surface is used. The blit itself doesn't draw anything, + * so we can detect the bug by looking at the otherwise unused 4th texel. It should + * never be masked out by the key. + * + * Also appears to affect the testbot in some way with R5G6B5. Color keying is + * terrible on WARP. */ + skip("Nvidia A4R4G4B4 color keying blit bug detected, skipping.\n"); + IDirectDrawSurface_Release(texture); + IDirectDrawSurface_Release(src); + IDirectDrawSurface_Release(dst); + goto done; + } + } + else + ok(data[0] == (c - 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + (c - 1) << tests[t].shift, data[0], tests[t].name, c); + + ok(data[1] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + tests[t].clear, data[1], tests[t].name, c); + + if (c == tests[t].max) + ok(data[2] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + tests[t].clear, data[2], tests[t].name, c); + else + ok(data[2] == (c + 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + (c + 1) << tests[t].shift, data[2], tests[t].name, c); + + hr = IDirect3DViewport2_Clear(viewport, 1, &clear_rect, D3DCLEAR_TARGET); + ok(SUCCEEDED(hr), "Failed to clear, hr %#x.\n", hr); + + hr = IDirect3DDevice2_BeginScene(device); + ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr); + hr = IDirect3DDevice2_DrawPrimitive(device, D3DPT_TRIANGLESTRIP, D3DVT_LVERTEX, quad, 4, 0); + ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr); + hr = IDirect3DDevice2_EndScene(device); + ok(SUCCEEDED(hr), "Failed to end scene, hr %#x.\n", hr); + + color = get_surface_color(rt, 80, 240); + if (!c) + ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + else + ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + + color = get_surface_color(rt, 240, 240); + ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + + color = get_surface_color(rt, 400, 240); + if (c == tests[t].max) + ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + else + ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + + hr = IDirect3DDevice2_SetRenderState(device, D3DRENDERSTATE_TEXTUREHANDLE, 0); + ok(SUCCEEDED(hr), "Failed to set texture handle, hr %#x.\n", hr); + IDirectDrawSurface_Release(texture); + } + IDirectDrawSurface_Release(src); + IDirectDrawSurface_Release(dst); + } +done: + + destroy_viewport(device, viewport); + destroy_material(green); + IDirectDrawSurface_Release(rt); + IDirect3DDevice2_Release(device); + refcount = IDirectDraw2_Release(ddraw); + ok(refcount == 0, "Ddraw object not properly released, refcount %u.\n", refcount); + DestroyWindow(window); +} + START_TEST(ddraw2) { IDirectDraw2 *ddraw; @@ -8511,4 +8818,5 @@ START_TEST(ddraw2) test_texturemapblend(); test_viewport_clear_rect(); test_color_fill(); + test_colorkey_precision(); } diff --git a/dlls/ddraw/tests/ddraw4.c b/dlls/ddraw/tests/ddraw4.c index b1ac1f1d4ec..85c632cdcdd 100644 --- a/dlls/ddraw/tests/ddraw4.c +++ b/dlls/ddraw/tests/ddraw4.c @@ -9555,6 +9555,323 @@ static void test_texcoordindex(void) DestroyWindow(window); } +static void test_colorkey_precision(void) +{ + static struct + { + struct vec3 pos; + struct vec2 texcoord; + } + quad[] = + { + {{-1.0f, -1.0f, 0.0f}, {0.0f, 1.0f}}, + {{-1.0f, 1.0f, 0.0f}, {0.0f, 0.0f}}, + {{ 1.0f, -1.0f, 0.0f}, {1.0f, 1.0f}}, + {{ 1.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, + }; + IDirect3DDevice3 *device; + IDirect3D3 *d3d; + IDirectDraw4 *ddraw; + IDirectDrawSurface4 *rt; + IDirect3DViewport3 *viewport; + HWND window; + HRESULT hr; + IDirectDrawSurface4 *src, *dst, *texture; + IDirect3DTexture2 *d3d_texture; + DDSURFACEDESC2 surface_desc, lock_desc; + ULONG refcount; + D3DCOLOR color; + unsigned int t, c; + DDCOLORKEY ckey; + DDBLTFX fx; + DWORD data[4] = {0}, color_mask; + D3DRECT clear_rect = {{0}, {0}, {640}, {480}}; + D3DDEVICEDESC device_desc, hel_desc; + BOOL warp; + static const struct + { + unsigned int max, shift, bpp, clear; + const char *name; + DDPIXELFORMAT fmt; + } + tests[] = + { + { + 255, 0, 4, 0x00345678, "D3DFMT_X8R8G8B8", + { + sizeof(DDPIXELFORMAT), DDPF_RGB, 0, + {32}, {0x00ff0000}, {0x0000ff00}, {0x000000ff}, {0x00000000} + } + + }, + { + 63, 5, 2, 0x5678, "D3DFMT_R5G6B5, G channel", + { + sizeof(DDPIXELFORMAT), DDPF_RGB, 0, + {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000} + } + + }, + { + 31, 0, 2, 0x5678, "D3DFMT_R5G6B5, B channel", + { + sizeof(DDPIXELFORMAT), DDPF_RGB, 0, + {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000} + } + + }, + { + 15, 0, 2, 0x0678, "D3DFMT_A4R4G4B4", + { + sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_ALPHAPIXELS, 0, + {16}, {0x0f00}, {0x00f0}, {0x000f}, {0xf000} + } + + }, + }; + + 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; + } + + /* The Windows 8 WARP driver has plenty of false negatives in X8R8G8B8 + * (color key doesn't match although the values are equal), and a false + * positive when the color key is 0 and the texture contains the value 1. + * I don't want to mark this broken unconditionally since this would + * essentially disable the test on Windows. Try to detect WARP (and I + * guess mismatch other SW renderers) by its ability to texture from + * system memory. Also on random occasions 254 == 255 and 255 != 255.*/ + memset(&device_desc, 0, sizeof(device_desc)); + device_desc.dwSize = sizeof(device_desc); + memset(&hel_desc, 0, sizeof(hel_desc)); + hel_desc.dwSize = sizeof(hel_desc); + hr = IDirect3DDevice3_GetCaps(device, &device_desc, &hel_desc); + ok(SUCCEEDED(hr), "Failed to get device caps, hr %#x.\n", hr); + warp = !!(device_desc.dwDevCaps & D3DDEVCAPS_TEXTURESYSTEMMEMORY); + + hr = IDirect3DDevice3_GetDirect3D(device, &d3d); + ok(SUCCEEDED(hr), "Failed to get Direct3D3 interface, hr %#x.\n", hr); + hr = IDirect3D3_QueryInterface(d3d, &IID_IDirectDraw4, (void **)&ddraw); + ok(SUCCEEDED(hr), "Failed to get DirectDraw4 interface, hr %#x.\n", hr); + IDirect3D3_Release(d3d); + hr = IDirect3DDevice3_GetRenderTarget(device, &rt); + ok(SUCCEEDED(hr), "Failed to get render target, hr %#x.\n", hr); + + viewport = create_viewport(device, 0, 0, 640, 480); + hr = IDirect3DDevice3_SetCurrentViewport(device, viewport); + ok(SUCCEEDED(hr), "Failed to set current viewport, hr %#x.\n", hr); + + hr = IDirect3DDevice3_SetRenderState(device, D3DRENDERSTATE_LIGHTING, FALSE); + ok(SUCCEEDED(hr), "Failed to disable lighting, hr %#x.\n", hr); + hr = IDirect3DDevice3_SetRenderState(device, D3DRENDERSTATE_ZENABLE, D3DZB_FALSE); + ok(SUCCEEDED(hr), "Failed to disable z-buffering, hr %#x.\n", hr); + hr = IDirect3DDevice3_SetRenderState(device, D3DRENDERSTATE_COLORKEYENABLE, TRUE); + ok(SUCCEEDED(hr), "Failed to enable color keying, hr %#x.\n", hr); + /* Multiply the texture read result with 0, that way the result color if the key doesn't + * match is constant. In theory color keying works without reading the texture result + * (meaning we could just op=arg1, arg1=tfactor), but the Geforce7 Windows driver begs + * to differ. */ + hr = IDirect3DDevice3_SetTextureStageState(device, 0, D3DTSS_COLOROP, D3DTOP_MODULATE); + ok(SUCCEEDED(hr), "Failed to set color op, hr %#x.\n", hr); + hr = IDirect3DDevice3_SetTextureStageState(device, 0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + ok(SUCCEEDED(hr), "Failed to set color arg, hr %#x.\n", hr); + hr = IDirect3DDevice3_SetTextureStageState(device, 0, D3DTSS_COLORARG2, D3DTA_TFACTOR); + ok(SUCCEEDED(hr), "Failed to set color arg, hr %#x.\n", hr); + hr = IDirect3DDevice3_SetRenderState(device, D3DRENDERSTATE_TEXTUREFACTOR, 0x00000000); + ok(SUCCEEDED(hr), "Failed to set render state, hr %#x.\n", hr); + + memset(&fx, 0, sizeof(fx)); + fx.dwSize = sizeof(fx); + memset(&lock_desc, 0, sizeof(lock_desc)); + lock_desc.dwSize = sizeof(lock_desc); + + for (t = 0; t < sizeof(tests) / sizeof(*tests); ++t) + { + memset(&surface_desc, 0, sizeof(surface_desc)); + surface_desc.dwSize = sizeof(surface_desc); + surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + surface_desc.dwWidth = 4; + surface_desc.dwHeight = 1; + U4(surface_desc).ddpfPixelFormat = tests[t].fmt; + /* Windows XP (at least with the r200 driver, other drivers untested) produces + * garbage when doing color keyed texture->texture blits. */ + hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &src, NULL); + ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr); + hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &dst, NULL); + ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr); + + fx.dwFillColor = tests[t].clear; + /* On the w8 testbot (WARP driver) the blit result has different values in the + * X channel. */ + color_mask = U2(tests[t].fmt).dwRBitMask + | U3(tests[t].fmt).dwGBitMask + | U4(tests[t].fmt).dwBBitMask; + + for (c = 0; c <= tests[t].max; ++c) + { + /* The idiotic Nvidia Windows driver can't change the color key on a d3d + * texture after it has been set once... */ + surface_desc.dwFlags |= DDSD_CKSRCBLT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE; + surface_desc.ddckCKSrcBlt.dwColorSpaceLowValue = c << tests[t].shift; + surface_desc.ddckCKSrcBlt.dwColorSpaceHighValue = c << tests[t].shift; + hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &texture, NULL); + ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr); + hr = IDirectDrawSurface4_QueryInterface(texture, &IID_IDirect3DTexture2, (void **)&d3d_texture); + ok(SUCCEEDED(hr), "Failed to get texture interface, hr %#x.\n", hr); + hr = IDirect3DDevice3_SetTexture(device, 0, d3d_texture); + ok(SUCCEEDED(hr), "Failed to set texture, hr %#x.\n", hr); + + hr = IDirectDrawSurface4_Blt(dst, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx); + ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr); + + hr = IDirectDrawSurface4_Lock(src, NULL, &lock_desc, DDLOCK_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr); + switch (tests[t].bpp) + { + case 4: + ((DWORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift; + ((DWORD *)lock_desc.lpSurface)[1] = c << tests[t].shift; + ((DWORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift; + ((DWORD *)lock_desc.lpSurface)[3] = 0xffffffff; + break; + + case 2: + ((WORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift; + ((WORD *)lock_desc.lpSurface)[1] = c << tests[t].shift; + ((WORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift; + ((WORD *)lock_desc.lpSurface)[3] = 0xffff; + break; + } + hr = IDirectDrawSurface4_Unlock(src, 0); + ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr); + hr = IDirectDrawSurface4_Blt(texture, NULL, src, NULL, DDBLT_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr); + + ckey.dwColorSpaceLowValue = c << tests[t].shift; + ckey.dwColorSpaceHighValue = c << tests[t].shift; + hr = IDirectDrawSurface4_SetColorKey(src, DDCKEY_SRCBLT, &ckey); + ok(SUCCEEDED(hr), "Failed to set color key, hr %#x.\n", hr); + + hr = IDirectDrawSurface4_Blt(dst, NULL, src, NULL, DDBLT_KEYSRC | DDBLT_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr); + + /* Don't make this read only, it somehow breaks the detection of the Nvidia bug below. */ + hr = IDirectDrawSurface4_Lock(dst, NULL, &lock_desc, DDLOCK_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr); + switch (tests[t].bpp) + { + case 4: + data[0] = ((DWORD *)lock_desc.lpSurface)[0] & color_mask; + data[1] = ((DWORD *)lock_desc.lpSurface)[1] & color_mask; + data[2] = ((DWORD *)lock_desc.lpSurface)[2] & color_mask; + data[3] = ((DWORD *)lock_desc.lpSurface)[3] & color_mask; + break; + + case 2: + data[0] = ((WORD *)lock_desc.lpSurface)[0] & color_mask; + data[1] = ((WORD *)lock_desc.lpSurface)[1] & color_mask; + data[2] = ((WORD *)lock_desc.lpSurface)[2] & color_mask; + data[3] = ((WORD *)lock_desc.lpSurface)[3] & color_mask; + break; + } + hr = IDirectDrawSurface4_Unlock(dst, 0); + ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr); + + if (!c) + { + ok(data[0] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + tests[t].clear, data[0], tests[t].name, c); + + if (data[3] == tests[t].clear) + { + /* My Geforce GTX 460 on Windows 7 misbehaves when A4R4G4B4 is blitted with color + * keying: The blit takes ~0.5 seconds, and subsequent color keying draws are broken, + * even when a different surface is used. The blit itself doesn't draw anything, + * so we can detect the bug by looking at the otherwise unused 4th texel. It should + * never be masked out by the key. + * + * Also appears to affect the testbot in some way with R5G6B5. Color keying is + * terrible on WARP. */ + skip("Nvidia A4R4G4B4 color keying blit bug detected, skipping.\n"); + IDirect3DTexture2_Release(d3d_texture); + IDirectDrawSurface4_Release(texture); + IDirectDrawSurface4_Release(src); + IDirectDrawSurface4_Release(dst); + goto done; + } + } + else + ok(data[0] == (c - 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + (c - 1) << tests[t].shift, data[0], tests[t].name, c); + + ok(data[1] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + tests[t].clear, data[1], tests[t].name, c); + + if (c == tests[t].max) + ok(data[2] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + tests[t].clear, data[2], tests[t].name, c); + else + ok(data[2] == (c + 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + (c + 1) << tests[t].shift, data[2], tests[t].name, c); + + hr = IDirect3DViewport3_Clear2(viewport, 1, &clear_rect, D3DCLEAR_TARGET, 0x0000ff00, 1.0f, 0); + ok(SUCCEEDED(hr), "Failed to clear, hr %#x.\n", hr); + + hr = IDirect3DDevice3_BeginScene(device); + ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr); + hr = IDirect3DDevice3_DrawPrimitive(device, D3DPT_TRIANGLESTRIP, D3DFVF_XYZ | D3DFVF_TEX1, quad, 4, 0); + ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr); + hr = IDirect3DDevice3_EndScene(device); + ok(SUCCEEDED(hr), "Failed to end scene, hr %#x.\n", hr); + + color = get_surface_color(rt, 80, 240); + if (!c) + ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + else + ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + + color = get_surface_color(rt, 240, 240); + ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + + color = get_surface_color(rt, 400, 240); + if (c == tests[t].max) + ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + else + ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + + IDirect3DTexture2_Release(d3d_texture); + IDirectDrawSurface4_Release(texture); + } + IDirectDrawSurface4_Release(src); + IDirectDrawSurface4_Release(dst); + } + done: + + destroy_viewport(device, viewport); + IDirectDrawSurface4_Release(rt); + IDirectDraw4_Release(ddraw); + refcount = IDirect3DDevice3_Release(device); + ok(!refcount, "Device has %u references left.\n", refcount); + DestroyWindow(window); +} + START_TEST(ddraw4) { IDirectDraw4 *ddraw; @@ -9638,4 +9955,5 @@ START_TEST(ddraw4) test_signed_formats(); test_color_fill(); test_texcoordindex(); + test_colorkey_precision(); } diff --git a/dlls/ddraw/tests/ddraw7.c b/dlls/ddraw/tests/ddraw7.c index e6a8d15bf0d..30213c53ab4 100644 --- a/dlls/ddraw/tests/ddraw7.c +++ b/dlls/ddraw/tests/ddraw7.c @@ -9740,6 +9740,307 @@ static void test_texcoordindex(void) DestroyWindow(window); } +static void test_colorkey_precision(void) +{ + static struct + { + struct vec3 pos; + struct vec2 texcoord; + } + quad[] = + { + {{-1.0f, -1.0f, 0.0f}, {0.0f, 1.0f}}, + {{-1.0f, 1.0f, 0.0f}, {0.0f, 0.0f}}, + {{ 1.0f, -1.0f, 0.0f}, {1.0f, 1.0f}}, + {{ 1.0f, 1.0f, 0.0f}, {1.0f, 0.0f}}, + }; + IDirect3DDevice7 *device; + IDirect3D7 *d3d; + IDirectDraw7 *ddraw; + IDirectDrawSurface7 *rt; + HWND window; + HRESULT hr; + IDirectDrawSurface7 *src, *dst, *texture; + DDSURFACEDESC2 surface_desc, lock_desc; + ULONG refcount; + D3DCOLOR color; + unsigned int t, c; + DDCOLORKEY ckey; + DDBLTFX fx; + DWORD data[4] = {0}, color_mask; + D3DDEVICEDESC7 device_desc; + BOOL warp; + static const struct + { + unsigned int max, shift, bpp, clear; + const char *name; + DDPIXELFORMAT fmt; + } + tests[] = + { + { + 255, 0, 4, 0x00345678, "D3DFMT_X8R8G8B8", + { + sizeof(DDPIXELFORMAT), DDPF_RGB, 0, + {32}, {0x00ff0000}, {0x0000ff00}, {0x000000ff}, {0x00000000} + } + + }, + { + 63, 5, 2, 0x5678, "D3DFMT_R5G6B5, G channel", + { + sizeof(DDPIXELFORMAT), DDPF_RGB, 0, + {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000} + } + + }, + { + 31, 0, 2, 0x5678, "D3DFMT_R5G6B5, B channel", + { + sizeof(DDPIXELFORMAT), DDPF_RGB, 0, + {16}, {0xf800}, {0x07e0}, {0x001f}, {0x0000} + } + + }, + { + 15, 0, 2, 0x0678, "D3DFMT_A4R4G4B4", + { + sizeof(DDPIXELFORMAT), DDPF_RGB | DDPF_ALPHAPIXELS, 0, + {16}, {0x0f00}, {0x00f0}, {0x000f}, {0xf000} + } + + }, + }; + + 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; + } + + /* The Windows 8 WARP driver has plenty of false negatives in X8R8G8B8 + * (color key doesn't match although the values are equal), and a false + * positive when the color key is 0 and the texture contains the value 1. + * I don't want to mark this broken unconditionally since this would + * essentially disable the test on Windows. Try to detect WARP (and I + * guess mismatch other SW renderers) by its ability to texture from + * system memory. Also on random occasions 254 == 255 and 255 != 255.*/ + hr = IDirect3DDevice7_GetCaps(device, &device_desc); + ok(SUCCEEDED(hr), "Failed to get device caps, hr %#x.\n", hr); + warp = !!(device_desc.dwDevCaps & D3DDEVCAPS_TEXTURESYSTEMMEMORY); + + hr = IDirect3DDevice7_GetDirect3D(device, &d3d); + ok(SUCCEEDED(hr), "Failed to get Direct3D7 interface, hr %#x.\n", hr); + hr = IDirect3D7_QueryInterface(d3d, &IID_IDirectDraw7, (void **)&ddraw); + ok(SUCCEEDED(hr), "Failed to get DirectDraw7 interface, hr %#x.\n", hr); + IDirect3D7_Release(d3d); + hr = IDirect3DDevice7_GetRenderTarget(device, &rt); + ok(SUCCEEDED(hr), "Failed to get render target, 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 z-buffering, hr %#x.\n", hr); + hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_COLORKEYENABLE, TRUE); + ok(SUCCEEDED(hr), "Failed to enable color keying, hr %#x.\n", hr); + /* Multiply the texture read result with 0, that way the result color if the key doesn't + * match is constant. In theory color keying works without reading the texture result + * (meaning we could just op=arg1, arg1=tfactor), but the Geforce7 Windows driver begs + * to differ. */ + hr = IDirect3DDevice7_SetTextureStageState(device, 0, D3DTSS_COLOROP, D3DTOP_MODULATE); + ok(SUCCEEDED(hr), "Failed to set color op, hr %#x.\n", hr); + hr = IDirect3DDevice7_SetTextureStageState(device, 0, D3DTSS_COLORARG1, D3DTA_TEXTURE); + ok(SUCCEEDED(hr), "Failed to set color arg, hr %#x.\n", hr); + hr = IDirect3DDevice7_SetTextureStageState(device, 0, D3DTSS_COLORARG2, D3DTA_TFACTOR); + ok(SUCCEEDED(hr), "Failed to set color arg, hr %#x.\n", hr); + hr = IDirect3DDevice7_SetRenderState(device, D3DRENDERSTATE_TEXTUREFACTOR, 0x00000000); + ok(SUCCEEDED(hr), "Failed to set render state, hr %#x.\n", hr); + + memset(&fx, 0, sizeof(fx)); + fx.dwSize = sizeof(fx); + memset(&lock_desc, 0, sizeof(lock_desc)); + lock_desc.dwSize = sizeof(lock_desc); + + for (t = 0; t < sizeof(tests) / sizeof(*tests); ++t) + { + memset(&surface_desc, 0, sizeof(surface_desc)); + surface_desc.dwSize = sizeof(surface_desc); + surface_desc.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN; + surface_desc.dwWidth = 4; + surface_desc.dwHeight = 1; + U4(surface_desc).ddpfPixelFormat = tests[t].fmt; + /* Windows XP (at least with the r200 driver, other drivers untested) produces + * garbage when doing color keyed texture->texture blits. */ + hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &src, NULL); + ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr); + hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &dst, NULL); + ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr); + + fx.dwFillColor = tests[t].clear; + /* On the w8 testbot (WARP driver) the blit result has different values in the + * X channel. */ + color_mask = U2(tests[t].fmt).dwRBitMask + | U3(tests[t].fmt).dwGBitMask + | U4(tests[t].fmt).dwBBitMask; + + for (c = 0; c <= tests[t].max; ++c) + { + /* The idiotic Nvidia Windows driver can't change the color key on a d3d + * texture after it has been set once... */ + surface_desc.dwFlags |= DDSD_CKSRCBLT; + surface_desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE; + surface_desc.ddckCKSrcBlt.dwColorSpaceLowValue = c << tests[t].shift; + surface_desc.ddckCKSrcBlt.dwColorSpaceHighValue = c << tests[t].shift; + hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &texture, NULL); + ok(SUCCEEDED(hr), "Failed to create surface, hr %#x.\n", hr); + hr = IDirect3DDevice7_SetTexture(device, 0, texture); + ok(SUCCEEDED(hr), "Failed to set texture, hr %#x.\n", hr); + + hr = IDirectDrawSurface7_Blt(dst, NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &fx); + ok(SUCCEEDED(hr), "Failed to clear destination surface, hr %#x.\n", hr); + + hr = IDirectDrawSurface7_Lock(src, NULL, &lock_desc, DDLOCK_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr); + switch (tests[t].bpp) + { + case 4: + ((DWORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift; + ((DWORD *)lock_desc.lpSurface)[1] = c << tests[t].shift; + ((DWORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift; + ((DWORD *)lock_desc.lpSurface)[3] = 0xffffffff; + break; + + case 2: + ((WORD *)lock_desc.lpSurface)[0] = (c ? c - 1 : 0) << tests[t].shift; + ((WORD *)lock_desc.lpSurface)[1] = c << tests[t].shift; + ((WORD *)lock_desc.lpSurface)[2] = min(c + 1, tests[t].max) << tests[t].shift; + ((WORD *)lock_desc.lpSurface)[3] = 0xffff; + break; + } + hr = IDirectDrawSurface7_Unlock(src, 0); + ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr); + hr = IDirectDrawSurface7_Blt(texture, NULL, src, NULL, DDBLT_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr); + + ckey.dwColorSpaceLowValue = c << tests[t].shift; + ckey.dwColorSpaceHighValue = c << tests[t].shift; + hr = IDirectDrawSurface7_SetColorKey(src, DDCKEY_SRCBLT, &ckey); + ok(SUCCEEDED(hr), "Failed to set color key, hr %#x.\n", hr); + + hr = IDirectDrawSurface7_Blt(dst, NULL, src, NULL, DDBLT_KEYSRC | DDBLT_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to blit, hr %#x.\n", hr); + + /* Don't make this read only, it somehow breaks the detection of the Nvidia bug below. */ + hr = IDirectDrawSurface7_Lock(dst, NULL, &lock_desc, DDLOCK_WAIT, NULL); + ok(SUCCEEDED(hr), "Failed to lock surface, hr %#x.\n", hr); + switch (tests[t].bpp) + { + case 4: + data[0] = ((DWORD *)lock_desc.lpSurface)[0] & color_mask; + data[1] = ((DWORD *)lock_desc.lpSurface)[1] & color_mask; + data[2] = ((DWORD *)lock_desc.lpSurface)[2] & color_mask; + data[3] = ((DWORD *)lock_desc.lpSurface)[3] & color_mask; + break; + + case 2: + data[0] = ((WORD *)lock_desc.lpSurface)[0] & color_mask; + data[1] = ((WORD *)lock_desc.lpSurface)[1] & color_mask; + data[2] = ((WORD *)lock_desc.lpSurface)[2] & color_mask; + data[3] = ((WORD *)lock_desc.lpSurface)[3] & color_mask; + break; + } + hr = IDirectDrawSurface7_Unlock(dst, 0); + ok(SUCCEEDED(hr), "Failed to unlock surface, hr %#x.\n", hr); + + if (!c) + { + ok(data[0] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + tests[t].clear, data[0], tests[t].name, c); + + if (data[3] == tests[t].clear) + { + /* My Geforce GTX 460 on Windows 7 misbehaves when A4R4G4B4 is blitted with color + * keying: The blit takes ~0.5 seconds, and subsequent color keying draws are broken, + * even when a different surface is used. The blit itself doesn't draw anything, + * so we can detect the bug by looking at the otherwise unused 4th texel. It should + * never be masked out by the key. + * + * Also appears to affect the testbot in some way with R5G6B5. Color keying is + * terrible on WARP. */ + skip("Nvidia A4R4G4B4 color keying blit bug detected, skipping.\n"); + IDirectDrawSurface7_Release(texture); + IDirectDrawSurface7_Release(src); + IDirectDrawSurface7_Release(dst); + goto done; + } + } + else + ok(data[0] == (c - 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + (c - 1) << tests[t].shift, data[0], tests[t].name, c); + + ok(data[1] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + tests[t].clear, data[1], tests[t].name, c); + + if (c == tests[t].max) + ok(data[2] == tests[t].clear, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + tests[t].clear, data[2], tests[t].name, c); + else + ok(data[2] == (c + 1) << tests[t].shift, "Expected surface content %#x, got %#x, format %s, c=%u.\n", + (c + 1) << tests[t].shift, data[2], tests[t].name, c); + + hr = IDirect3DDevice7_Clear(device, 0, NULL, D3DCLEAR_TARGET, 0x0000ff00, 1.0f, 0); + ok(SUCCEEDED(hr), "Failed to clear, hr %#x.\n", hr); + + hr = IDirect3DDevice7_BeginScene(device); + ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr); + hr = IDirect3DDevice7_DrawPrimitive(device, D3DPT_TRIANGLESTRIP, D3DFVF_XYZ | D3DFVF_TEX1, quad, 4, 0); + ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr); + hr = IDirect3DDevice7_EndScene(device); + ok(SUCCEEDED(hr), "Failed to end scene, hr %#x.\n", hr); + + color = get_surface_color(rt, 80, 240); + if (!c) + ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + else + ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + + color = get_surface_color(rt, 240, 240); + ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + + color = get_surface_color(rt, 400, 240); + if (c == tests[t].max) + ok(compare_color(color, 0x0000ff00, 1) || broken(warp && compare_color(color, 0x00000000, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + else + ok(compare_color(color, 0x00000000, 1) || broken(warp && compare_color(color, 0x0000ff00, 1)), + "Got unexpected color 0x%08x, format %s, c=%u.\n", + color, tests[t].name, c); + + IDirectDrawSurface7_Release(texture); + } + IDirectDrawSurface7_Release(src); + IDirectDrawSurface7_Release(dst); + } +done: + + IDirectDrawSurface7_Release(rt); + IDirectDraw7_Release(ddraw); + refcount = IDirect3DDevice7_Release(device); + ok(!refcount, "Device has %u references left.\n", refcount); + DestroyWindow(window); +} + START_TEST(ddraw7) { HMODULE module = GetModuleHandleA("ddraw.dll"); @@ -9834,4 +10135,5 @@ START_TEST(ddraw7) test_signed_formats(); test_color_fill(); test_texcoordindex(); + test_colorkey_precision(); }