Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/dxgi/tests/Makefile.in | 2 +- dlls/dxgi/tests/dxgi.c | 250 +++++++++++++++++++++++++++++++++++- 2 files changed, 250 insertions(+), 2 deletions(-)
diff --git a/dlls/dxgi/tests/Makefile.in b/dlls/dxgi/tests/Makefile.in index 1c99d70957..fbce9d3dd0 100644 --- a/dlls/dxgi/tests/Makefile.in +++ b/dlls/dxgi/tests/Makefile.in @@ -1,5 +1,5 @@ TESTDLL = dxgi.dll -IMPORTS = d3d10_1 dxgi user32 +IMPORTS = d3d10_1 d3d11 dxgi user32
C_SRCS = \ dxgi.c diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index 79e4bc0c15..b73f9319c6 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -40,9 +40,16 @@ static PFN_D3D12_CREATE_DEVICE pD3D12CreateDevice; static PFN_D3D12_GET_DEBUG_INTERFACE pD3D12GetDebugInterface;
static unsigned int use_adapter_idx; +static BOOL enable_debug_layer; static BOOL use_warp_adapter; static BOOL use_mt = TRUE;
+struct device_desc +{ + const D3D_FEATURE_LEVEL *feature_level; + UINT flags; +}; + static struct test_entry { void (*test)(void); @@ -539,6 +546,56 @@ success: return dxgi_device; }
+static ID3D11Device *create_d3d11_device(const struct device_desc *desc) +{ + static const D3D_FEATURE_LEVEL default_feature_level[] = + { + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + }; + const D3D_FEATURE_LEVEL *feature_level; + UINT flags = desc ? desc->flags : 0; + unsigned int feature_level_count; + IDXGIAdapter *adapter; + ID3D11Device *device; + HRESULT hr; + + if (desc && desc->feature_level) + { + feature_level = desc->feature_level; + feature_level_count = 1; + } + else + { + feature_level = default_feature_level; + feature_level_count = ARRAY_SIZE(default_feature_level); + } + + if (enable_debug_layer) + flags |= D3D11_CREATE_DEVICE_DEBUG; + + if ((adapter = create_adapter())) + { + hr = D3D11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, NULL, flags, feature_level, feature_level_count, + D3D11_SDK_VERSION, &device, NULL, NULL); + IDXGIAdapter_Release(adapter); + return SUCCEEDED(hr) ? device : NULL; + } + + if (SUCCEEDED(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, feature_level, feature_level_count, + D3D11_SDK_VERSION, &device, NULL, NULL))) + return device; + if (SUCCEEDED(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_WARP, NULL, flags, feature_level, feature_level_count, + D3D11_SDK_VERSION, &device, NULL, NULL))) + return device; + if (SUCCEEDED(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_REFERENCE, NULL, flags, feature_level, feature_level_count, + D3D11_SDK_VERSION, &device, NULL, NULL))) + return device; + + return NULL; +} + static ID3D12Device *create_d3d12_device(void) { IDXGIAdapter *adapter; @@ -4985,6 +5042,197 @@ done: ok(!refcount, "Factory has %u references left.\n", refcount); }
+static void test_window_association(void) +{ + DXGI_SWAP_CHAIN_DESC swapchain_desc; + IDXGIFactory *factory, *factory2; + IDXGIDevice *devices[2] = {0}; + ID3D11Device *d3d11_device; + IDXGISwapChain *swapchain; + IDXGIAdapter *adapter; + HWND hwnd, hwnd2; + LONG_PTR wndproc; + BOOL fullscreen; + ULONG refcount; + HRESULT hr; + INT i, j; + + static const struct + { + UINT flag; + BOOL expect_fullscreen; + BOOL broken_d3d10; + } + tests[] = + { + /* There are two reasons why VK_TAB and VK_ESC are not tested here. + * 1. Posting them to the window doesn't exit fullscreen like Alt+Enter does. + * Alt+Tab and Alt+Esc are handled somewhere else, e.g., not calling IDXGISwapChain::Present + * will break Alt+Tab and Alt+Esc while Alt+Enter will still function. + * 2. Posting them hangs the posting thread. Another thread keep sending input is needed to avoid the hang. + * The hang is not because of flush_events. */ + {0, TRUE}, + {0, FALSE}, + {DXGI_MWA_NO_WINDOW_CHANGES, FALSE}, + {DXGI_MWA_NO_WINDOW_CHANGES, FALSE}, + {DXGI_MWA_NO_ALT_ENTER, FALSE, TRUE}, + {DXGI_MWA_NO_ALT_ENTER, FALSE}, + {DXGI_MWA_NO_PRINT_SCREEN, TRUE}, + {DXGI_MWA_NO_PRINT_SCREEN, FALSE}, + {0, TRUE}, + {0, FALSE} + }; + + /* d3d10 */ + devices[0] = create_device(0); + + /* d3d11 */ + d3d11_device = create_d3d11_device(NULL); + if (d3d11_device) + { + hr = ID3D11Device_QueryInterface(d3d11_device, &IID_IDXGIDevice, (void **)&devices[1]); + ok(SUCCEEDED(hr), "Created device does not implement IDXGIDevice\n"); + ID3D11Device_Release(d3d11_device); + } + + swapchain_desc.BufferDesc.Width = 800; + swapchain_desc.BufferDesc.Height = 600; + swapchain_desc.BufferDesc.RefreshRate.Numerator = 60; + swapchain_desc.BufferDesc.RefreshRate.Denominator = 60; + swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + swapchain_desc.SampleDesc.Count = 1; + swapchain_desc.SampleDesc.Quality = 0; + swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapchain_desc.BufferCount = 1; + swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0); + swapchain_desc.Windowed = TRUE; + swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + swapchain_desc.Flags = 0; + + /* Get pointer to original WNDPROC */ + wndproc = GetWindowLongPtrW(swapchain_desc.OutputWindow, GWLP_WNDPROC); + + hwnd2 = CreateWindowA("static", "dxgi_test2", 0, 0, 0, 400, 200, 0, 0, 0, 0); + hr = CreateDXGIFactory(&IID_IDXGIFactory, (void **)&factory2); + ok(hr == S_OK, "Failed to create DXGI factory, hr %#x.\n", hr); + + for (i = 0; i < ARRAY_SIZE(devices); i++) + { + if (!devices[i]) + { + skip("Create device failed. Skipping IDXGIFactory window association test.\n"); + continue; + } + + hr = IDXGIDevice_GetAdapter(devices[i], &adapter); + ok(SUCCEEDED(hr), "GetAdapter failed, hr %#x.\n", hr); + + hr = IDXGIAdapter_GetParent(adapter, &IID_IDXGIFactory, (void **)&factory); + ok(SUCCEEDED(hr), "GetParent failed, hr %#x.\n", hr); + + /* Parameter check */ + hr = IDXGIFactory_GetWindowAssociation(factory, NULL); + ok(hr == DXGI_ERROR_INVALID_CALL, "GetWindowAssociation failed, hr %#x.\n", hr); + + for (j = 0; j <= DXGI_MWA_VALID; j++) + { + hr = IDXGIFactory_MakeWindowAssociation(factory, NULL, j); + ok(SUCCEEDED(hr), "MakeWindowAssociation failed, flags %#x, hr %#x.\n", j, hr); + + hr = IDXGIFactory_MakeWindowAssociation(factory, swapchain_desc.OutputWindow, j); + ok(SUCCEEDED(hr), "MakeWindowAssociation failed, flags %#x, hr %#x.\n", j, hr); + + /* Verify WNDPROC unmodified */ + ok(wndproc == GetWindowLongPtrW(swapchain_desc.OutputWindow, GWLP_WNDPROC), + "Expect WNDPROC not modified\n"); + + hwnd = (HWND)0xdeadbeef; + hr = IDXGIFactory_GetWindowAssociation(factory, &hwnd); + ok(SUCCEEDED(hr), "GetWindowAssociation failed, flags %#x, hr %#x.\n", j, hr); + /* Yes, GetWindowAssociation always return NULL for hwnd even though MakeWindowAssociation and + * GetWindowAssociation are successfully called. I wonder why. */ + ok(!hwnd, "Expect null associated window.\n"); + } + + hr = IDXGIFactory_MakeWindowAssociation(factory, swapchain_desc.OutputWindow, DXGI_MWA_VALID + 1); + ok(hr == DXGI_ERROR_INVALID_CALL, "MakeWindowAssociation succeeded, hr %#x.\n", hr); + + /* Alt+Enter tests */ + hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown *)devices[i], &swapchain_desc, &swapchain); + ok(SUCCEEDED(hr), "CreateSwapChain failed, hr %#x.\n", hr); + + /* Verify WNDPROC unmodified */ + ok(wndproc == GetWindowLongPtrW(swapchain_desc.OutputWindow, GWLP_WNDPROC), + "Expect WNDPROC not modified\n"); + + hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL); + ok(SUCCEEDED(hr) || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE + || broken(hr == DXGI_ERROR_UNSUPPORTED), /* Win 7 testbot */ + "SetFullscreenState failed, hr %#x.\n", hr); + if (FAILED(hr)) + skip("Could not change fullscreen state.\n"); + else + { + hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + + for (j = 0; j < ARRAY_SIZE(tests); j++) + { + /* Associate a window first with opposite flags */ + hr = IDXGIFactory_MakeWindowAssociation(factory, hwnd2, (~tests[j].flag) & DXGI_MWA_VALID); + ok(SUCCEEDED(hr), "Test %d: MakeWindowAssociation failed, hr %#x.\n", j, hr); + + /* Associate current test window */ + hwnd = tests[j].flag ? swapchain_desc.OutputWindow : NULL; + hr = IDXGIFactory_MakeWindowAssociation(factory, hwnd, tests[j].flag); + ok(SUCCEEDED(hr), "Test %d: MakeWindowAssociation failed, hr %#x.\n", j, hr); + + /* Associate a new test window doesn't override old window */ + hr = IDXGIFactory_MakeWindowAssociation(factory, hwnd2, (~tests[j].flag) & DXGI_MWA_VALID); + ok(SUCCEEDED(hr), "Test %d: MakeWindowAssociation failed, hr %#x.\n", j, hr); + + /* Wrong factory doesn't affect current test window */ + hr = IDXGIFactory_MakeWindowAssociation(factory2, hwnd, (~tests[j].flag) & DXGI_MWA_VALID); + ok(SUCCEEDED(hr), "Test %d: MakeWindowAssociation failed, hr %#x.\n", j, hr); + + /* Post synthesized Alt + VK_RETURN WM_SYSKEYDOWN */ + PostMessageA(swapchain_desc.OutputWindow, WM_SYSKEYDOWN, VK_RETURN, + (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x20000001); + flush_events(); + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(SUCCEEDED(hr), "Test %d: GetFullscreenState failed, hr %#x.\n", j, hr); + ok(fullscreen == tests[j].expect_fullscreen || broken(tests[j].broken_d3d10 && i == 0 && fullscreen), + "Test %d: got unexpected fullscreen %#x.\n", j, fullscreen); + + /* Verify WNDPROC unmodified. Currently Wine modifies WNPPROC during after fullscreen transition */ + todo_wine_if(tests[j].expect_fullscreen) + ok(wndproc == GetWindowLongPtrW(swapchain_desc.OutputWindow, GWLP_WNDPROC), + "Expect WNDPROC not modified\n"); + } + } + + /* Set to windowed mode before releasing resources, otherwise there might be unhandled exceptions */ + hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL); + ok(SUCCEEDED(hr), "SetFullscreenState failed, hr %#x.\n", hr); + + refcount = IDXGISwapChain_Release(swapchain); + ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount); + refcount = IDXGIDevice_Release(devices[i]); + ok(!refcount, "Device has %u references left.\n", refcount); + refcount = IDXGIAdapter_Release(adapter); + ok(!refcount, "IDXGIAdapter has %u references left.\n", refcount); + refcount = IDXGIFactory_Release(factory); + ok(!refcount, "Factory has %u references left.\n", refcount); + } + + refcount = IDXGIFactory_Release(factory2); + ok(!refcount, "Factory has %u references left.\n", refcount); + DestroyWindow(hwnd2); + DestroyWindow(swapchain_desc.OutputWindow); +} + static void run_on_d3d10(void (*test_func)(IUnknown *device, BOOL is_d3d12)) { IDXGIDevice *device; @@ -5029,7 +5277,6 @@ static void run_on_d3d12(void (*test_func)(IUnknown *device, BOOL is_d3d12)) START_TEST(dxgi) { HMODULE dxgi_module, d3d12_module; - BOOL enable_debug_layer = FALSE; unsigned int argc, i; ID3D12Debug *debug; char **argv; @@ -5084,6 +5331,7 @@ START_TEST(dxgi) test_swapchain_parameters(); test_swapchain_window_messages(); test_swapchain_window_styles(); + test_window_association(); run_on_d3d10(test_swapchain_resize); run_on_d3d10(test_swapchain_present); run_on_d3d10(test_swapchain_backbuffer_index);