[PATCH 2/6] d3d9/tests: Add visual tests for locking vertexbuffers

Patrick Rudolph siro at das-labor.org
Tue Jul 26 12:09:07 CDT 2016


Test flags D3DLOCK_NOOVERWRITE and D3DLOCK_DISCARD on POOL_DEFAULT
vertexbuffers. After submitting a draw call the used vertexbuffer
is mapped and modified. The visual result changes depending on used
flags. As this test depends on race conditions the buffer size
is calibrated first to allow the test to pass on fast and slow
machines.

The results have been verified on Windows 7 and
Tests on Windows 7 and AMD Radeon Mobility HD4300 Series and
Windows 7 and Nvidia ION showed that given both
D3DLOCK_NOOVERWRITE and D3DLOCK_DISCARD the buffer is mapped
unsyncronized.

Tests on Nvidia showed that D3DLOCK_NOOVERWRITE has effect on dynamic
buffers only.

Signed-off-by: Patrick Rudolph <siro at das-labor.org>
---
 dlls/d3d9/tests/visual.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 199 insertions(+)

diff --git a/dlls/d3d9/tests/visual.c b/dlls/d3d9/tests/visual.c
index a2adc54..0a5e8cb 100644
--- a/dlls/d3d9/tests/visual.c
+++ b/dlls/d3d9/tests/visual.c
@@ -4309,6 +4309,204 @@ done:
     DestroyWindow(window);
 }
 
+static void test_vb_lock_flags(void)
+{
+    ULONG refcount, timestamp1, timestamp2;
+    IDirect3DVertexBuffer9 *buffer;
+    IDirect3DDevice9 *device;
+    DWORD i, j, tri, size;
+    BOOL unsyncronized;
+    IDirect3D9 *d3d9;
+    D3DCOLOR color;
+    D3DCAPS9 caps;
+    HWND window;
+    HRESULT hr;
+
+    static const struct
+    {
+        const char *flags_s;
+        unsigned int flags;
+        DWORD usage;
+        BOOL unsyncronized;
+        BOOL todo;
+        BOOL broken;
+    }
+    /* Nvidia ignores D3DLOCK_NOOVERWRITE on non dynamic buffers,
+     * while AMD always accepts D3DLOCK_NOOVERWRITE on all buffers. */
+    tests[] =
+    {
+        {"0", 0, 0, FALSE, FALSE, FALSE},
+        {"0", 0, D3DUSAGE_DYNAMIC, FALSE, FALSE, FALSE},
+        {"D3DLOCK_NOOVERWRITE", D3DLOCK_NOOVERWRITE, 0, FALSE, FALSE, TRUE},
+        {"D3DLOCK_NOOVERWRITE", D3DLOCK_NOOVERWRITE, D3DUSAGE_DYNAMIC, TRUE, FALSE, FALSE},
+        {"D3DLOCK_DISCARD", D3DLOCK_DISCARD, 0, FALSE, FALSE, FALSE},
+        {"D3DLOCK_DISCARD", D3DLOCK_DISCARD, D3DUSAGE_DYNAMIC, FALSE, FALSE, FALSE},
+        {"D3DLOCK_NOOVERWRITE | D3DLOCK_DISCARD",
+                D3DLOCK_NOOVERWRITE | D3DLOCK_DISCARD, 0, FALSE, FALSE, TRUE},
+        {"D3DLOCK_NOOVERWRITE | D3DLOCK_DISCARD",
+                D3DLOCK_NOOVERWRITE | D3DLOCK_DISCARD, D3DUSAGE_DYNAMIC, TRUE, TRUE, TRUE},
+        {"D3DLOCK_NOOVERWRITE | D3DLOCK_READONLY",
+                D3DLOCK_NOOVERWRITE | D3DLOCK_READONLY, 0, FALSE, FALSE, TRUE},
+        {"D3DLOCK_NOOVERWRITE | D3DLOCK_READONLY",
+                D3DLOCK_NOOVERWRITE | D3DLOCK_READONLY, D3DUSAGE_DYNAMIC, TRUE, TRUE, TRUE},
+    };
+
+    struct quad
+    {
+        struct
+        {
+            struct vec3 position;
+            DWORD diffuse;
+        } strip[4];
+    };
+    static const struct quad quad1 =
+    {
+        {
+            {{-1.0f, -1.0f, 0.0f}, 0xffff0000},
+            {{-1.0f,  1.0f, 0.0f}, 0xff00ff00},
+            {{ 1.0f, -1.0f, 0.0f}, 0xff0000ff},
+            {{ 1.0f,  1.0f, 0.0f}, 0xffffffff},
+        }
+    }, quad2 =
+    {
+        {
+            {{-1.0f, -1.0f, 0.0f}, 0xffffff00},
+            {{-1.0f,  1.0f, 0.0f}, 0xffffff00},
+            {{ 1.0f, -1.0f, 0.0f}, 0xffffff00},
+            {{ 1.0f,  1.0f, 0.0f}, 0xffffff00},
+        }
+    };
+    struct quad *quads;
+
+    window = CreateWindowA("static", "d3d9_test", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+            0, 0, 640, 480, NULL, NULL, NULL, NULL);
+    ok(!!window, "Failed to create a window.\n");
+
+    d3d9 = Direct3DCreate9(D3D_SDK_VERSION);
+    ok(!!d3d9, "Failed to create a D3D object.\n");
+    if (!(device = create_device(d3d9, window, window, TRUE)))
+    {
+        skip("Failed to create a D3D device, skipping tests.\n");
+        IDirect3D9_Release(d3d9);
+        DestroyWindow(window);
+        return;
+    }
+
+    hr = IDirect3DDevice9_GetDeviceCaps(device, &caps);
+    ok(SUCCEEDED(hr), "Failed to get device caps, hr %#x.\n", hr);
+
+    size = 0x1000;
+    /* calculate triangle count */
+    tri = (size / sizeof(quad1.strip[0])) - 2;
+    if (tri > caps.MaxPrimitiveCount)
+        return;
+
+    hr = IDirect3DDevice9_SetRenderState(device, D3DRS_LIGHTING, FALSE);
+    ok(hr == D3D_OK, "IDirect3DDevice9_SetRenderState returned %08x\n", hr);
+
+    hr = IDirect3DDevice9_SetFVF(device, D3DFVF_XYZ | D3DFVF_DIFFUSE);
+    ok(hr == D3D_OK, "IDirect3DDevice9_SetFVF failed with %08x\n", hr);
+
+    /* calibrate vertexbuffer size to allow the test to pass on slow and fast systems */
+    hr = IDirect3DDevice9_CreateVertexBuffer(device, size, 0, 0, D3DPOOL_DEFAULT, &buffer, NULL);
+    ok(SUCCEEDED(hr), "Failed to create vertex buffer, hr %#x.\n", hr);
+
+    /* place quads */
+    hr = IDirect3DVertexBuffer9_Lock(buffer, 0, size, (void **)&quads, 0);
+    ok(SUCCEEDED(hr), "Failed to lock vertex buffer, hr %#x.\n", hr);
+    for (j = 0; j < size / sizeof(quad1); j++)
+        quads[j] = quad1;
+    hr = IDirect3DVertexBuffer9_Unlock(buffer);
+    ok(SUCCEEDED(hr), "Failed to unlock vertex buffer, hr %#x.\n", hr);
+
+    hr = IDirect3DDevice9_SetStreamSource(device, 0, buffer, 0, sizeof(quad1.strip[0]));
+    ok(hr == D3D_OK, "Failed to set stream source, hr %#x.\n", hr);
+
+    timestamp1 = GetTickCount();
+
+    hr = IDirect3DDevice9_BeginScene(device);
+    ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr);
+    hr = IDirect3DDevice9_DrawPrimitive(device, D3DPT_TRIANGLESTRIP, 0, tri);
+    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);
+
+    /* dummy read to wait for GPU being done */
+    color = getPixelColor(device, 160, 360);
+
+    timestamp2 = GetTickCount();
+
+    /* Adjust buffer size for 1 second draw call duration */
+    size = (size * 1000UL) / (timestamp2 - timestamp1 + 1);
+
+    IDirect3DVertexBuffer9_Release(buffer);
+
+    /* recalculate triangle count */
+    tri = (size / sizeof(quad1.strip[0])) - 2;
+    if (tri > caps.MaxPrimitiveCount)
+        return;
+
+    for (i = 0; i < sizeof(tests)/sizeof(tests[0]); ++i)
+    {
+        hr = IDirect3DDevice9_CreateVertexBuffer(device, size, tests[i].usage, 0, D3DPOOL_DEFAULT, &buffer, NULL);
+        ok(SUCCEEDED(hr), "Failed to create vertex buffer, hr %#x.\n", hr);
+
+        /* place quads */
+        hr = IDirect3DVertexBuffer9_Lock(buffer, 0, size, (void **)&quads, 0);
+        ok(SUCCEEDED(hr), "Failed to lock vertex buffer, hr %#x.\n", hr);
+
+        for (j = 0; j < size / sizeof(quad1); j++)
+            quads[j] = quad1;
+
+        hr = IDirect3DVertexBuffer9_Unlock(buffer);
+        ok(SUCCEEDED(hr), "Failed to unlock vertex buffer, hr %#x.\n", hr);
+
+        hr = IDirect3DDevice9_SetStreamSource(device, 0, buffer, 0, sizeof(quad1.strip[0]));
+        ok(SUCCEEDED(hr), "Failed to set stream source, hr %#x.\n", hr);
+
+        hr = IDirect3DDevice9_Clear(device, 0, NULL, D3DCLEAR_TARGET, 0, 0, 0);
+        ok(SUCCEEDED(hr), "IDirect3DDevice9_Clear failed with %08x\n", hr);
+
+        /* draw a quad to make use of the bound buffer */
+        hr = IDirect3DDevice9_BeginScene(device);
+        ok(SUCCEEDED(hr), "Failed to begin scene, hr %#x.\n", hr);
+        hr = IDirect3DDevice9_DrawPrimitive(device, D3DPT_TRIANGLESTRIP, 0, tri);
+        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);
+
+        /* Map the last quad while draw is in progress */
+        hr = IDirect3DVertexBuffer9_Lock(buffer, (size / sizeof(quad1) - 1) * sizeof(quad1),
+                sizeof(quad1), (void **)&quads, tests[i].flags);
+        ok(SUCCEEDED(hr), "Failed to lock vertex buffer, hr %#x.\n", hr);
+        /* Replace last quad with yellow quad */
+        quads[0] = quad2;
+        hr = IDirect3DVertexBuffer9_Unlock(buffer);
+        ok(SUCCEEDED(hr), "Failed to unlock vertex buffer, hr %#x.\n", hr);
+
+        /* Test if last triangle has different color */
+        color = getPixelColor(device, 160, 360);
+        unsyncronized = color_match(color, D3DCOLOR_ARGB(0x00, 0xff, 0xff, 0x00), 1);
+
+        IDirect3DVertexBuffer9_Release(buffer);
+
+        hr = IDirect3DDevice9_Present(device, NULL, NULL, NULL, NULL);
+        ok(SUCCEEDED(hr), "Failed to present, hr %#x.\n", hr);
+
+        todo_wine_if (tests[i].todo)
+        {
+            ok(tests[i].unsyncronized == unsyncronized || broken(tests[i].broken),
+                    "Expected buffer mapped %s. Flags = %s. Usage = %#x.\n",
+                    tests[i].unsyncronized ? "unsyncronized" : "syncronized", tests[i].flags_s, tests[i].usage);
+        }
+    }
+
+    refcount = IDirect3DDevice9_Release(device);
+    ok(!refcount, "Device has %u references left.\n", refcount);
+    IDirect3D9_Release(d3d9);
+    DestroyWindow(window);
+}
+
 static void release_buffer_test(void)
 {
     IDirect3DVertexBuffer9 *vb;
@@ -21794,4 +21992,5 @@ START_TEST(visual)
     test_multisample_init();
     test_texture_blending();
     test_color_clamping();
+    test_vb_lock_flags();
 }
-- 
2.7.4




More information about the wine-patches mailing list