From b0447b13d92a4af2db62d96088b68a2dafccc900 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20D=C3=B6singer?= <stefan@codeweavers.com>
Date: Wed, 11 Sep 2013 10:34:37 +0200
Subject: [PATCH 6/6] d3d9/tests: Add a test for converted formats.
Reply-To: wine-devel <wine-devel@winehq.org>

---
 dlls/d3d9/tests/visual.c | 295 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 295 insertions(+)

diff --git a/dlls/d3d9/tests/visual.c b/dlls/d3d9/tests/visual.c
index 5f64ec3..c53706e 100644
--- a/dlls/d3d9/tests/visual.c
+++ b/dlls/d3d9/tests/visual.c
@@ -17491,6 +17491,297 @@ done:
     DestroyWindow(window);
 }
 
+static void test_converted_formats(void)
+{
+    IDirect3DDevice9 *device;
+    HWND window;
+    HRESULT hr;
+    unsigned int i, j, x, y;
+    IDirect3DTexture9 *texture, *texture_sysmem;
+    IDirect3DSurface9 *src_surface, *dst_surface;
+    D3DLOCKED_RECT locked_rect;
+    IDirect3DPixelShader9 *shader;
+    IDirect3D9 *d3d;
+    D3DCOLOR color;
+    D3DCAPS9 caps;
+    ULONG refcount;
+
+    /* The input data was designed for D3DFMT_L6V5U5 and then transfered
+     * to the other formats because L6V5U5 is the lowest precision format.
+     * It tests the extreme values -1.0 (-16) and 1.0 (15) for U/V and
+     * 0.0 (0) and 1.0 (63) for L, the neutral point 0 as well as -1 and 1.
+     * Some other intermediate values are tested too. For the 8 bit formats
+     * the equivalents of -1 and 1 are -8 and 8, that's why there is no
+     * 0xffff input for these formats. However, after setting up the V8U8
+     * tests the original L6V5U5 needed some adjustments as explained in
+     * the comment below. */
+    static const USHORT content_v8u8[4][4] =
+    {
+        {0x0000, 0x7f7f, 0x8080, 0x0000},
+        {0x0080, 0x8000, 0x7f00, 0x007f},
+        {0x193b, 0xe8c8, 0x0808, 0xf8f8},
+        {0x4444, 0xc0c0, 0xa066, 0x22e0},
+    };
+    static const DWORD content_v16u16[4][4] =
+    {
+        {0x00000000, 0x7fff7fff, 0x80008000, 0x00000000},
+        {0x00008000, 0x80000000, 0x7fff0000, 0x00007fff},
+        {0x19993bbb, 0xe800c800, 0x08880888, 0xf800f800},
+        {0x44444444, 0xc000c000, 0xa0006666, 0x2222e000},
+    };
+    static const DWORD content_q8w8v8u8[4][4] =
+    {
+        {0x00000000, 0x00ff7f7f, 0x00008080, 0x00ff0000},
+        {0x00000080, 0x00008000, 0x00007f00, 0x0000007f},
+        {0x0041193b, 0x0051e8c8, 0x00040808, 0x00fff8f8},
+        {0x00824444, 0x0000c0c0, 0x00c2a066, 0x009222e0},
+    };
+    static const DWORD content_x8l8v8u8[4][4] =
+    {
+        {0x00000000, 0x00ff7f7f, 0x00008080, 0x00ff0000},
+        {0x00000080, 0x00008000, 0x00007f00, 0x0000007f},
+        {0x0041193b, 0x0051e8c8, 0x00040808, 0x00fff8f8},
+        {0x00824444, 0x0000c0c0, 0x00c2a066, 0x009222e0},
+    };
+    /* D3DFMT_L6V5U5 has poor precision on some GPUs. On a GeForce 7 the highest U and V value (15)
+     * results in the output color 0xfb, which is 4 steps away from the correct value 0xff. It is
+     * not the ~0xf0 you'd get if you blindly left-shifted the 5 bit values to form an 8 bit value
+     * though.
+     *
+     * There may also be an off-by-one bug involved: The value -7 should result in the output 0x47,
+     * but ends up as 0x4d. Likewise, -3 becomes 0x6e instead of 0x67. Those values are close to
+     * the proper results of -6 and -2. */
+    static const USHORT content_l6v5u5[4][4] =
+    {
+        {0x0000, 0xfdef, 0x0210, 0xfc00},
+        {0x0010, 0x0200, 0x01e0, 0x000f},
+        {0x4067, 0x53b9, 0x0421, 0xffff}, /* 0x4067, 0x53b9, 0x0421, 0xffff */
+        {0x8108, 0x0318, 0xc28c, 0x909c}, /* 0x8108, 0x0318, 0xc28c, 0x909c */
+    };
+    static const struct
+    {
+        D3DFORMAT format;
+        const char *name;
+        void *content;
+        SIZE_T size;
+        BOOL blue;
+        unsigned int slop, slop_broken;
+    }
+    formats[] =
+    {
+        {D3DFMT_V8U8,     "D3DFMT_V8U8",     content_v8u8,     sizeof(content_v8u8),     FALSE, 1, 0},
+        {D3DFMT_V16U16,   "D3DFMT_V16U16",   content_v16u16,   sizeof(content_v16u16),   FALSE, 1, 0},
+//         {D3DFMT_Q8W8V8U8, "D3DFMT_Q8W8V8U8", content_q8w8v8u8, sizeof(content_q8w8v8u8), TRUE,  1, 0},
+        {D3DFMT_X8L8V8U8, "D3DFMT_X8L8V8U8", content_x8l8v8u8, sizeof(content_x8l8v8u8), TRUE,  1, 0},
+        {D3DFMT_L6V5U5,   "D3DFMT_L6V5U5",   content_l6v5u5,   sizeof(content_l6v5u5),   TRUE,  1, 7},
+    };
+    static const DWORD shader_code[] =
+    {
+        0xffff0101,                                                     /* ps_1_1               */
+        0x00000051, 0xa00f0000, 0x3f000000, 0x3f000000,                 /* def c0, 0.5, 0.5,    */
+        0x3f000000, 0x3f000000,                                         /*         0.5, 0.5     */
+        0x00000042, 0xb00f0000,                                         /* tex t0               */
+        0x00000004, 0x800f0000, 0xb0e40000, 0xa0e40000, 0xa0e40000,     /* mad r0, t0, c0, c0   */
+        0x0000ffff                                                      /* end                  */
+    };
+    static const struct
+    {
+        struct vec3 position;
+        struct vec2 texcrd;
+    }
+    quad[] =
+    {
+        /* Flip the y coordinate to make the input and
+         * output arrays easier to compare. */
+        {{ -1.0f,  -1.0f,  0.0f}, { 0.0f, 1.0f}},
+        {{  1.0f,  -1.0f,  0.0f}, { 1.0f, 1.0f}},
+        {{ -1.0f,   1.0f,  0.0f}, { 0.0f, 0.0f}},
+        {{  1.0f,   1.0f,  0.0f}, { 1.0f, 0.0f}},
+    };
+    static const D3DCOLOR expected_colors[4][4] =
+    {
+        {0x00808080, 0x00fefeff, 0x00010180, 0x008080ff},
+        {0x00018080, 0x00800180, 0x0080fe80, 0x00fe8080},
+        {0x00ba98a0, 0x004767a8, 0x00878781, 0x007878ff},
+        {0x00c3c3c0, 0x003f3f80, 0x00e51fe1, 0x005fa2c8},
+    };
+    static const D3DCOLOR expected_colors2[4][4] =
+    {
+        {0x00808080, 0x00fefeff, 0x00800180, 0x008080ff},
+        {0x00018080, 0x00800180, 0x004767a8, 0x00fe8080},
+        {0x00ba98a0, 0x004767a8, 0x00878781, 0x007878ff},
+        {0x00c3c3c0, 0x003f3f80, 0x00e51fe1, 0x005fa2c8},
+    };
+    const RECT rect = {1, 1, 2, 3};
+    const POINT point = {2, 0};
+
+    window = CreateWindowA("static", "d3d9_test", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+            0, 0, 640, 480, NULL, NULL, NULL, NULL);
+    d3d = Direct3DCreate9(D3D_SDK_VERSION);
+    ok(!!d3d, "Failed to create a D3D object.\n");
+
+    if (!(device = create_device(d3d, window, window, TRUE)))
+    {
+        skip("Failed to create a D3D device, skipping tests.\n");
+        IDirect3D9_Release(d3d);
+        DestroyWindow(window);
+        return;
+    }
+
+    hr = IDirect3DDevice9_GetDeviceCaps(device, &caps);
+    ok(SUCCEEDED(hr), "Failed to get device caps, hr %#x.\n", hr);
+
+    if (caps.PixelShaderVersion < D3DPS_VERSION(1, 1))
+    {
+        skip("Pixel shaders not supported, skipping converted format test.\n");
+        goto done;
+    }
+
+    hr = IDirect3DDevice9_SetRenderState(device, D3DRS_LIGHTING, FALSE);
+    ok(SUCCEEDED(hr), "Failed to set render state, hr %#x.\n", hr);
+    hr = IDirect3DDevice9_SetRenderState(device, D3DRS_ZENABLE, D3DZB_FALSE);
+    ok(SUCCEEDED(hr), "Failed to set render state, hr %#x.\n", hr);
+    hr = IDirect3DDevice9_SetRenderState(device, D3DRS_CULLMODE, D3DCULL_NONE);
+    ok(hr == D3D_OK, "IDirect3DDevice9_SetRenderState failed with %08x\n", hr);
+
+    hr = IDirect3DDevice9_SetFVF(device, D3DFVF_XYZ | D3DFVF_TEX1);
+    ok(SUCCEEDED(hr), "Failed to set FVF, hr %#x.\n", hr);
+    hr = IDirect3DDevice9_CreatePixelShader(device, shader_code, &shader);
+    ok(SUCCEEDED(hr), "Failed to create pixel shader, hr %#x.\n", hr);
+    hr = IDirect3DDevice9_SetPixelShader(device, shader);
+    ok(SUCCEEDED(hr), "Failed to set pixel shader, hr %#x.\n", hr);
+
+    for (i = 0; i < sizeof(formats) / sizeof(*formats); i++)
+    {
+        hr = IDirect3D9_CheckDeviceFormat(d3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
+                D3DFMT_X8R8G8B8, 0, D3DRTYPE_TEXTURE, formats[i].format);
+        if (FAILED(hr))
+        {
+            skip("Format %s not supported, skipping.\n", formats[i].name);
+            continue;
+        }
+
+        for (j = 0; j < 2; j++)
+        {
+            D3DPOOL pool;
+
+            if (j)
+                pool = D3DPOOL_SYSTEMMEM;
+            else
+                pool = D3DPOOL_MANAGED;
+
+            texture_sysmem = NULL;
+            hr = IDirect3DDevice9_CreateTexture(device, 4, 4, 1, 0, formats[i].format,
+                    pool, &texture, NULL);
+            ok(SUCCEEDED(hr), "Failed to create texture, hr %#x.\n", hr);
+
+            hr = IDirect3DTexture9_LockRect(texture, 0, &locked_rect, NULL, 0);
+            ok(SUCCEEDED(hr), "Failed to lock texture, hr %#x.\n", hr);
+            memcpy(locked_rect.pBits, formats[i].content, formats[i].size);
+            hr = IDirect3DTexture9_UnlockRect(texture, 0);
+            ok(SUCCEEDED(hr), "Failed to unlock texture, hr %#x.\n", hr);
+
+            if (pool == D3DPOOL_SYSTEMMEM)
+            {
+                texture_sysmem = texture;
+                hr = IDirect3DDevice9_CreateTexture(device, 4, 4, 1, 0, formats[i].format,
+                        D3DPOOL_DEFAULT, &texture, NULL);
+                ok(SUCCEEDED(hr), "Failed to create texture, hr %#x.\n", hr);
+
+                hr = IDirect3DDevice9_UpdateTexture(device, (IDirect3DBaseTexture9 *)texture_sysmem,
+                        (IDirect3DBaseTexture9 *)texture);
+                ok(SUCCEEDED(hr), "Failed to update texture, hr %#x.\n", hr);
+            }
+
+            hr = IDirect3DDevice9_SetTexture(device, 0, (IDirect3DBaseTexture9 *)texture);
+            ok(SUCCEEDED(hr), "Failed to set texture, hr %#x.\n", hr);
+
+            hr = IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET, 0x00330033, 0.0f, 0);
+            ok(SUCCEEDED(hr), "Failed to clear, hr %#x.\n", hr);
+            hr = IDirect3DDevice9_BeginScene(device);
+            ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr);
+            hr = IDirect3DDevice9_DrawPrimitiveUP(device, D3DPT_TRIANGLESTRIP, 2, &quad[0], sizeof(*quad));
+            ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr);
+            hr = IDirect3DDevice9_EndScene(device);
+            ok(SUCCEEDED(hr), "Failed to end scene, hr %#x.\n", hr);
+
+            for (y = 0; y < 4; y++)
+            {
+                for (x = 0; x < 4; x++)
+                {
+                    D3DCOLOR expected_color = expected_colors[y][x];
+                    if (!formats[i].blue)
+                        expected_color |= 0x000000ff;
+
+                    color = getPixelColor(device, 80 + 160 * x, 60 + 120 * y);
+                    ok(color_match(color, expected_color, formats[i].slop)
+                            || color_match(color, expected_color, formats[i].slop_broken),
+                            "Expected color 0x%08x, got 0x%08x, format %s, location %ux%u.\n",
+                            expected_color, color, formats[i].name, x, y);
+                }
+            }
+            hr = IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL);
+            ok(SUCCEEDED(hr), "Failed to present, hr %#x.\n", hr);
+
+            if (pool == D3DPOOL_MANAGED)
+            {
+                IDirect3DTexture9_Release(texture);
+                continue;
+            }
+
+            hr = IDirect3DTexture9_GetSurfaceLevel(texture, 0, &dst_surface);
+            ok(SUCCEEDED(hr), "Failed to get surface, hr %#x.\n", hr);
+            IDirect3DTexture9_GetSurfaceLevel(texture_sysmem, 0, &src_surface);
+            ok(SUCCEEDED(hr), "Failed to get surface, hr %#x.\n", hr);
+
+            hr = IDirect3DDevice9_UpdateSurface(device, src_surface, &rect,
+                    dst_surface, &point);
+            ok(SUCCEEDED(hr), "Failed to update surface, hr %#x.\n", hr);
+
+            IDirect3DSurface9_Release(dst_surface);
+            IDirect3DSurface9_Release(src_surface);
+
+            hr = IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET, 0x00003300, 0.0f, 0);
+            ok(SUCCEEDED(hr), "Failed to clear, hr %#x.\n", hr);
+            hr = IDirect3DDevice9_BeginScene(device);
+            ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr);
+            hr = IDirect3DDevice9_DrawPrimitiveUP(device, D3DPT_TRIANGLESTRIP, 2, &quad[0], sizeof(*quad));
+            ok(SUCCEEDED(hr), "Failed to draw, hr %#x.\n", hr);
+            hr = IDirect3DDevice9_EndScene(device);
+            ok(SUCCEEDED(hr), "Failed to end scene, hr %#x.\n", hr);
+
+            for (y = 0; y < 4; y++)
+            {
+                for (x = 0; x < 4; x++)
+                {
+                    D3DCOLOR expected_color = expected_colors2[y][x];
+                    if (!formats[i].blue)
+                        expected_color |= 0x000000ff;
+
+                    color = getPixelColor(device, 80 + 160 * x, 60 + 120 * y);
+                    ok(color_match(color, expected_color, formats[i].slop)
+                            || color_match(color, expected_color, formats[i].slop_broken),
+                            "Expected color 0x%08x, got 0x%08x, format %s, location %ux%u.\n",
+                            expected_color, color, formats[i].name, x, y);
+                }
+            }
+            hr = IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL);
+            ok(SUCCEEDED(hr), "Failed to present, hr %#x.\n", hr);
+
+            IDirect3DTexture9_Release(texture_sysmem);
+            IDirect3DTexture9_Release(texture);
+        }
+    }
+
+    IDirect3DPixelShader9_Release(shader);
+
+done:
+    refcount = IDirect3DDevice9_Release(device);
+    ok(!refcount, "Device has %u references left.\n", refcount);
+    IDirect3D9_Release(d3d);
+    DestroyWindow(window);
+}
+
 START_TEST(visual)
 {
     D3DADAPTER_IDENTIFIER9 identifier;
@@ -17518,6 +17809,8 @@ START_TEST(visual)
 
     IDirect3D9_Release(d3d);
 
+if (0)
+{
     test_sanity();
     depth_clamp_test();
     stretchrect_test();
@@ -17604,3 +17897,5 @@ START_TEST(visual)
     test_position_index();
     test_table_fog_zw();
 }
+    test_converted_formats();
+}
-- 
2.3.0

