Basically, this is the tests from https://gitlab.winehq.org/wine/wine/-/merge_requests/3420 with fixes applied where they originate, rather than later in the series.
-- v2: ddraw/tests: Test Pick() and GetPickRecords().
From: Jeff Smith whydoubt@gmail.com
Inspired by patches from Matthew Wong itsmattkc@gmail.com --- dlls/ddraw/tests/ddraw1.c | 354 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 354 insertions(+)
diff --git a/dlls/ddraw/tests/ddraw1.c b/dlls/ddraw/tests/ddraw1.c index d8fcb188c40..469bfee0a6c 100644 --- a/dlls/ddraw/tests/ddraw1.c +++ b/dlls/ddraw/tests/ddraw1.c @@ -15530,6 +15530,359 @@ static void test_pinned_sysmem(void) DestroyWindow(window); }
+static DWORD get_pickrecords_(int line, IDirect3DDevice *device, IDirect3DExecuteBuffer *execute_buffer, + IDirect3DViewport *viewport, D3DRECT *pick_rect, DWORD expect_count, D3DPICKRECORD *record) +{ + DWORD record_count = ~0; + HRESULT hr; + + hr = IDirect3DDevice_Pick(device, execute_buffer, viewport, 0, pick_rect); + ok_(__FILE__, line)(SUCCEEDED(hr), "Failed to perform pick, hr %#lx.\n", hr); + hr = IDirect3DDevice_GetPickRecords(device, &record_count, NULL); + ok_(__FILE__, line)(SUCCEEDED(hr), "Failed to get pick record count, hr %#lx.\n", hr); + todo_wine + ok_(__FILE__, line)( + record_count == expect_count, + "Got incorrect number of pick records (expected %lu): %lu.\n", + expect_count, record_count + ); + if (expect_count != 0 && record_count == expect_count) + { + memset(record, 0, sizeof(*record) * expect_count); + hr = IDirect3DDevice_GetPickRecords(device, &record_count, record); + ok_(__FILE__, line)(SUCCEEDED(hr), "Failed to get pick records, hr %#lx.\n", hr); + } + + return record_count; +} +#define get_pickrecords(a, b, c, d, e, f) get_pickrecords_(__LINE__, a, b, c, d, e, f) + +static void check_pickrecord_(int line, D3DPICKRECORD *record, BYTE opcode, DWORD offset, D3DVALUE z) +{ + ok_(__FILE__, line)(record->bOpcode == opcode, "Got incorrect bOpcode: %i.\n", record->bOpcode); + ok_(__FILE__, line)(record->bPad == 0, "Got incorrect bPad: %i.\n", record->bPad); + ok_(__FILE__, line)(record->dwOffset == offset, "Got incorrect dwOffset: %lu.\n", record->dwOffset); + ok_(__FILE__, line)(compare_float(record->dvZ, z, 4096), "Got incorrect dvZ: %.8e.\n", record->dvZ); +} +#define check_pickrecord(a, b, c, d) check_pickrecord_(__LINE__, a, b, c, d) + +static void test_pick(const GUID *device_guid) +{ + static D3DTLVERTEX tquad[] = + { + /* Left half of viewport */ + {{320.0f}, {480.0f}, { 7.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ 0.0f}, {480.0f}, {-2.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{320.0f}, { 0.0f}, { 7.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ 0.0f}, { 0.0f}, {-2.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + /* Lower-left quarter of viewport */ + {{320.0f}, {480.0f}, {-1.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ 0.0f}, {480.0f}, { 8.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{320.0f}, {240.0f}, {-1.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ 0.0f}, {240.0f}, { 8.5f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + /* Left half of lower-left quarter of viewport */ + {{160.0f}, {480.0f}, {-3.0f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ 0.0f}, {480.0f}, {-3.0f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{160.0f}, {240.0f}, {-3.0f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ 0.0f}, {240.0f}, {-3.0f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + /* Outside of viewport */ + {{ 0.0f}, { 0.0f}, { 9.0f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ -5.0f}, { 0.0f}, { 9.0f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ 0.0f}, { -5.0f}, { 9.0f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ -5.0f}, { -5.0f}, { 9.0f}, {1.0f}, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + }; + + static D3DLVERTEX quad[] = + { + /* Left half of viewport */ + {{ 0.0f}, { -1.0f}, { 1.5f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ -1.0f}, { -1.0f}, {-0.5f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ 0.0f}, { 1.0f}, { 1.5f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ -1.0f}, { 1.0f}, {-0.5f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + /* Lower-left quarter of viewport */ + {{ 0.0f}, { -1.0f}, {-0.5f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ -1.0f}, { -1.0f}, { 1.5f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ 0.0f}, { 0.0f}, {-0.5f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ -1.0f}, { 0.0f}, { 1.5f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + /* Left half of lower-left quarter of viewport */ + {{ -0.5f}, { -1.0f}, { 1.0f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ -1.0f}, { -1.0f}, {-1.0f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ -0.5f}, { 0.0f}, { 1.0f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ -1.0f}, { 0.0f}, {-1.0f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + /* Outside of viewport */ + {{ -1.0f}, { 1.0f}, { 0.2f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ -1.5f}, { 1.0f}, { 0.2f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ -1.0f}, { 1.5f}, { 0.2f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + {{ -1.5f}, { 1.5f}, { 0.2f}, 0, {0xff00ff00}, {0x00000000}, {0.0f}, {0.0f}}, + }; + + IDirect3DExecuteBuffer *execute_buffer; + const D3DPICKRECORD empty_record = {0}; + D3DEXECUTEBUFFERDESC exec_desc; + IDirect3DViewport *viewport; + IDirect3DDevice *device; + D3DPICKRECORD record[3]; + UINT hit, miss, fails; + void *ptr, *ptr_inst; + IDirectDraw *ddraw; + DWORD record_count; + D3DRECT pick_rect; + UINT quad_offset; + HWND window; + HRESULT hr; + LONG i, j; + + static const UINT vp_width = 640, vp_height = 480; + D3DRECT clear_rect = {{0}, {0}, {vp_width}, {vp_height}}; + + window = create_window(); + ddraw = create_ddraw(); + ok(!!ddraw, "Failed to create a ddraw object.\n"); + if (!(device = create_device_ex(ddraw, window, DDSCL_NORMAL, device_guid))) + { + skip("Failed to create a 3D device, skipping test.\n"); + IDirectDraw_Release(ddraw); + DestroyWindow(window); + return; + } + + viewport = create_viewport(device, 0, 0, vp_width, vp_height); + + + /* Set up execute buffer - copy transformed/lit vertices */ + 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 %#lx.\n", hr); + hr = IDirect3DExecuteBuffer_Lock(execute_buffer, &exec_desc); + ok(SUCCEEDED(hr), "Failed to lock execute buffer, hr %#lx.\n", hr); + memcpy(exec_desc.lpData, tquad, sizeof(tquad)); + ptr = ptr_inst = ((BYTE *)exec_desc.lpData) + sizeof(tquad); + emit_process_vertices(&ptr, D3DPROCESSVERTICES_COPY, 0, 16); + emit_set_rs(&ptr, D3DRENDERSTATE_ZENABLE, TRUE); + quad_offset = (BYTE *)ptr - (BYTE *)ptr_inst; + emit_tquad(&ptr, 0); + emit_tquad(&ptr, 4); + emit_tquad(&ptr, 8); + emit_tquad(&ptr, 12); + emit_end(&ptr); + hr = IDirect3DExecuteBuffer_Unlock(execute_buffer); + ok(SUCCEEDED(hr), "Failed to unlock execute buffer, hr %#lx.\n", hr); + set_execute_data(execute_buffer, 16, sizeof(tquad), (BYTE *)ptr - (BYTE *)ptr_inst); + + hr = IDirect3DViewport_Clear(viewport, 1, &clear_rect, D3DCLEAR_ZBUFFER); + ok(hr == DD_OK, "Got unexpected hr %#lx.\n", hr); + + /* Pick a pixel where no quads are present */ + pick_rect.x1 = pick_rect.x2 = 480; + pick_rect.y1 = pick_rect.y2 = 360; + get_pickrecords(device, execute_buffer, viewport, &pick_rect, 0, NULL); + + /* Pick a pixel where one quad is present */ + pick_rect.x1 = pick_rect.x2 = 240; + pick_rect.y1 = pick_rect.y2 = 120; + record_count = get_pickrecords(device, execute_buffer, viewport, &pick_rect, 1, &record[0]); + if (record_count == 1) + check_pickrecord(&record[0], D3DOP_TRIANGLE, quad_offset + 4, 5.0f); + + /* Pick a pixel where multiple quads are present */ + pick_rect.x1 = pick_rect.x2 = 64; + pick_rect.y1 = pick_rect.y2 = 360; + record_count = get_pickrecords(device, execute_buffer, viewport, &pick_rect, 3, &record[0]); + if (record_count == 3) + { + /* Documentation states this list is z-ordered, but it appears that it is not. */ + check_pickrecord(&record[0], D3DOP_TRIANGLE, quad_offset + 12, -0.5f); + check_pickrecord(&record[1], D3DOP_TRIANGLE, quad_offset + 32, 6.5f); + check_pickrecord(&record[2], D3DOP_TRIANGLE, quad_offset + 52, -3.0f); + + /* If the count is wrong, do not populate any records */ + memset(&record, 0, sizeof(record)); + record_count = 1; + hr = IDirect3DDevice_GetPickRecords(device, &record_count, &record[0]); + ok(SUCCEEDED(hr), "Failed to get pick records, hr %#lx.\n", hr); + ok(record_count == 3, "Got incorrect number of pick records (expected 3): %lu.\n", record_count); + ok(!memcmp(&record[0], &empty_record, sizeof(empty_record)), "Got unexpected pick record.\n"); + ok(!memcmp(&record[1], &empty_record, sizeof(empty_record)), "Got unexpected pick record.\n"); + ok(!memcmp(&record[2], &empty_record, sizeof(empty_record)), "Got unexpected pick record.\n"); + } + + /* Pick a pixel outside of viewport */ + pick_rect.x1 = pick_rect.x2 = -1; + pick_rect.y1 = pick_rect.y2 = -2; + record_count = get_pickrecords(device, execute_buffer, viewport, &pick_rect, 1, &record[0]); + if (record_count == 1) + check_pickrecord(&record[0], D3DOP_TRIANGLE, quad_offset + 64, 9.0f); + + /* Pick a rectange, though it appears that only the upper-left corner is checked. */ + pick_rect.x1 = 240; + pick_rect.y1 = 120; + pick_rect.x2 = vp_width - 1; + pick_rect.y2 = vp_height - 1; + record_count = get_pickrecords(device, execute_buffer, viewport, &pick_rect, 1, &record[0]); + if (record_count == 1) + check_pickrecord(&record[0], D3DOP_TRIANGLE, quad_offset + 4, 5.0f); + + /* Perform a number of picks, we should have a specific amount by the end. + * We should get precisely equal numbers of hits and no hits since our quad + * covers exactly half the screen. */ + fails = miss = hit = 0; + for (i = 0; i < vp_width; i += 40) + { + for (j = 0; j < vp_height; j += 30) + { + pick_rect.x1 = pick_rect.x2 = i; + pick_rect.y1 = pick_rect.y2 = j; + + IDirect3DDevice_Pick(device, execute_buffer, viewport, 0, &pick_rect); + record_count = ~0; + IDirect3DDevice_GetPickRecords(device, &record_count, NULL); + if (record_count == ~0) + fails++; + else if (record_count == 0) + miss++; + else + hit++; + } + } + + todo_wine ok(fails == 0, "Getting pick records failed to set a value %i times.\n", fails); + ok(hit == miss, "Got an unexpected ratio of pick hits/misses: %i/%i.\n", hit, miss); + + IDirect3DExecuteBuffer_Release(execute_buffer); + + + /* Set up execute buffer - transform untransformed/lit vertices */ + 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 %#lx.\n", hr); + hr = IDirect3DExecuteBuffer_Lock(execute_buffer, &exec_desc); + ok(SUCCEEDED(hr), "Failed to lock execute buffer, hr %#lx.\n", hr); + memcpy(exec_desc.lpData, quad, sizeof(quad)); + ptr = ptr_inst = ((BYTE *)exec_desc.lpData) + sizeof(quad); + emit_process_vertices(&ptr, D3DPROCESSVERTICES_TRANSFORM, 0, 16); + emit_set_rs(&ptr, D3DRENDERSTATE_ZENABLE, TRUE); + quad_offset = (BYTE *)ptr - (BYTE *)ptr_inst; + emit_tquad(&ptr, 0); + emit_tquad(&ptr, 4); + emit_tquad(&ptr, 8); + emit_tquad(&ptr, 12); + emit_end(&ptr); + hr = IDirect3DExecuteBuffer_Unlock(execute_buffer); + ok(SUCCEEDED(hr), "Failed to unlock execute buffer, hr %#lx.\n", hr); + set_execute_data(execute_buffer, 16, sizeof(quad), (BYTE *)ptr - (BYTE *)ptr_inst); + + hr = IDirect3DViewport_Clear(viewport, 1, &clear_rect, D3DCLEAR_ZBUFFER); + ok(hr == DD_OK, "Got unexpected hr %#lx.\n", hr); + + /* Pick a pixel where no quads are present */ + pick_rect.x1 = pick_rect.x2 = 480; + pick_rect.y1 = pick_rect.y2 = 360; + get_pickrecords(device, execute_buffer, viewport, &pick_rect, 0, NULL); + + /* Pick a pixel where one quad is present */ + pick_rect.x1 = pick_rect.x2 = 120; + pick_rect.y1 = pick_rect.y2 = 120; + record_count = get_pickrecords(device, execute_buffer, viewport, &pick_rect, 1, &record[0]); + if (record_count == 1) + check_pickrecord(&record[0], D3DOP_TRIANGLE, quad_offset + 12, 0.25f); + + /* Pick a pixel where quad is clipped */ + pick_rect.x1 = pick_rect.x2 = 40; + pick_rect.y1 = pick_rect.y2 = 120; + get_pickrecords(device, execute_buffer, viewport, &pick_rect, 0, NULL); + + /* Pick a pixel where multiple quads are present */ + pick_rect.x1 = pick_rect.x2 = 120; + pick_rect.y1 = pick_rect.y2 = 360; + record_count = get_pickrecords(device, execute_buffer, viewport, &pick_rect, 3, &record[0]); + if (record_count == 3) + { + /* Documentation states this list is z-ordered, but it appears that it is not. */ + check_pickrecord(&record[0], D3DOP_TRIANGLE, quad_offset + 4, 0.25f); + check_pickrecord(&record[1], D3DOP_TRIANGLE, quad_offset + 32, 0.75f); + check_pickrecord(&record[2], D3DOP_TRIANGLE, quad_offset + 44, 0.5f); + } + + /* Pick a pixel outside of viewport */ + pick_rect.x1 = pick_rect.x2 = -8; + pick_rect.y1 = pick_rect.y2 = -8; + record_count = get_pickrecords(device, execute_buffer, viewport, &pick_rect, 1, &record[0]); + if (record_count == 1) + check_pickrecord(&record[0], D3DOP_TRIANGLE, quad_offset + 64, 0.2f); + + /* Pick a rectange, though it appears that only the upper-left corner is checked. */ + pick_rect.x1 = 120; + pick_rect.y1 = 120; + pick_rect.x2 = vp_width - 1; + pick_rect.y2 = vp_height - 1; + record_count = get_pickrecords(device, execute_buffer, viewport, &pick_rect, 1, &record[0]); + if (record_count == 1) + check_pickrecord(&record[0], D3DOP_TRIANGLE, quad_offset + 12, 0.25f); + + /* Perform a number of picks, we should have a specific amount by the end. + * One quarter should be hits and three quarters should be no hits, since + * our quads cover half the screen, but 50% should be clipped. */ + fails = miss = hit = 0; + for (i = 0; i < vp_width; i += 40) + { + for (j = 0; j < vp_height; j += 30) + { + pick_rect.x1 = pick_rect.x2 = i; + pick_rect.y1 = pick_rect.y2 = j; + + IDirect3DDevice_Pick(device, execute_buffer, viewport, 0, &pick_rect); + record_count = ~0; + IDirect3DDevice_GetPickRecords(device, &record_count, NULL); + if (record_count == ~0) + fails++; + else if (record_count == 0) + miss++; + else + hit++; + } + } + + todo_wine ok(fails == 0, "Getting pick records failed to set a value %i times.\n", fails); + ok(hit * 3 == miss, "Got an unexpected ratio of pick hits/misses: %i/%i.\n", hit, miss); + + if (0) /* These crash on Windows. */ + { + IDirect3DDevice_GetPickRecords(device, NULL, NULL); + IDirect3DDevice_GetPickRecords(device, NULL, &record[0]); + } + + IDirect3DExecuteBuffer_Release(execute_buffer); + + + /* Attempting to Pick with locked execute buffer should fail */ + 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; + IDirect3DDevice_CreateExecuteBuffer(device, &exec_desc, &execute_buffer, NULL); + IDirect3DExecuteBuffer_Lock(execute_buffer, &exec_desc); + hr = IDirect3DDevice_Pick(device, execute_buffer, viewport, 0, &pick_rect); + todo_wine ok(hr == D3DERR_EXECUTE_LOCKED, "Unexpected result of pick on locked execute buffer, hr %#lx.\n", hr); + hr = IDirect3DExecuteBuffer_Unlock(execute_buffer); + ok(SUCCEEDED(hr), "Failed to unlock execute buffer, hr %#lx.\n", hr); + IDirect3DExecuteBuffer_Release(execute_buffer); + + + destroy_viewport(device, viewport); + IDirect3DDevice_Release(device); + IDirectDraw_Release(ddraw); + DestroyWindow(window); +} + START_TEST(ddraw1) { DDDEVICEIDENTIFIER identifier; @@ -15650,4 +16003,5 @@ START_TEST(ddraw1) test_filling_convention(); test_enum_devices(); test_pinned_sysmem(); + run_for_each_device_type(test_pick); }
Hi, I am on vacation right now, but I'll try to review the changes later. Ping me again if you don't hear by the end of February.
Yeah, pretransformed vertices bypass the entire vertex pipeline, that's what they do.
Re depth testing, indeed IDirect3DViewport::Clear doesn't allow you to pass a depth value (that was added with IDirect3DViewport3::Clear2). IDirectDrawSurface::Blt with DDBLTFX_DEPTHFILL could do it, but the meaning of the bitmask is driver dependent, except for zero.
@stefan Thanks, sounds good.
FWIW, the original author's work only tested pre-transformed vertices, and when I did the initial rework for this MR, I didn't pay much attention that aspect. Now I have some tests with un-transformed vertices as well.