ddraw/tests: Test color keying precision.

This brute force tests all values in 4, 5, 6 and 8 bit channels.
This commit is contained in:
Stefan Dösinger 2015-08-23 21:59:34 +02:00 committed by Alexandre Julliard
parent 1a965811f6
commit baab5a869a
4 changed files with 1263 additions and 0 deletions

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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();
}