Mostly to show that TakeOwnership and ReleaseOwnership are based on VidPN ownership.
Signed-off-by: Zhiyi Zhang zzhang@codeweavers.com --- dlls/dxgi/tests/dxgi.c | 226 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 225 insertions(+), 1 deletion(-)
diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c index 79e4bc0c15..765b174af6 100644 --- a/dlls/dxgi/tests/dxgi.c +++ b/dlls/dxgi/tests/dxgi.c @@ -17,11 +17,15 @@ */
#include <assert.h> +#include "ntstatus.h" +#define WIN32_NO_STATUS #define COBJMACROS #include "initguid.h" #include "dxgi1_6.h" #include "d3d11.h" #include "d3d12.h" +#include "winternl.h" +#include "ddk/d3dkmthk.h" #include "wine/heap.h" #include "wine/test.h"
@@ -36,6 +40,10 @@ static DEVMODEW registry_mode; static HRESULT (WINAPI *pCreateDXGIFactory1)(REFIID iid, void **factory); static HRESULT (WINAPI *pCreateDXGIFactory2)(UINT flags, REFIID iid, void **factory);
+static NTSTATUS (WINAPI *pD3DKMTCheckVidPnExclusiveOwnership)(const D3DKMT_CHECKVIDPNEXCLUSIVEOWNERSHIP *); +static NTSTATUS (WINAPI *pD3DKMTCloseAdapter)(const D3DKMT_CLOSEADAPTER *); +static NTSTATUS (WINAPI *pD3DKMTOpenAdapterFromGdiDisplayName)(D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME *); + static PFN_D3D12_CREATE_DEVICE pD3D12CreateDevice; static PFN_D3D12_GET_DEBUG_INTERFACE pD3D12GetDebugInterface;
@@ -43,6 +51,9 @@ static unsigned int use_adapter_idx; static BOOL use_warp_adapter; static BOOL use_mt = TRUE;
+static const DWORD wait_timeout = 1000; +static const DWORD wait_step = 100; + static struct test_entry { void (*test)(void); @@ -113,6 +124,24 @@ static ULONG get_refcount(void *iface) return IUnknown_Release(unknown); }
+/* This is to get a result from a function that returns different result if called too early */ +#define wait_result(a, b, c, d) wait_result_(__LINE__, a, b, c, d) +#define wait_result_(line, func, arg, expected, todo) \ + do \ + { \ + DWORD total_time = 0; \ + typeof(expected) got; \ + do \ + { \ + got = func(arg); \ + if (got == expected) \ + break; \ + Sleep(wait_step); \ + total_time += wait_step; \ + } while (total_time < wait_timeout); \ + todo_wine_if(todo) ok_(__FILE__, line)(got == expected, "Expect %#x, got %#x.\n", expected, got); \ + } while (0) + #define check_interface(a, b, c, d) check_interface_(__LINE__, a, b, c, d) static HRESULT check_interface_(unsigned int line, void *iface, REFIID iid, BOOL supported, BOOL is_broken) @@ -4985,6 +5014,194 @@ done: ok(!refcount, "Factory has %u references left.\n", refcount); }
+static void test_output_ownership(IUnknown *device, BOOL is_d3d12) +{ + D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME open_adapter_gdi_desc; + D3DKMT_CHECKVIDPNEXCLUSIVEOWNERSHIP check_ownership_desc; + D3DKMT_CLOSEADAPTER close_adapter_desc; + DXGI_SWAP_CHAIN_DESC swapchain_desc; + DXGI_OUTPUT_DESC output_desc; + ID3D12Device *d3d12_device; + IDXGISwapChain *swapchain; + IDXGIFactory4 *factory4; + IDXGIFactory *factory; + IDXGIAdapter *adapter; + IDXGIOutput *output; + BOOL fullscreen; + NTSTATUS status; + ULONG refcount; + LUID luid; + HRESULT hr; + + if (!pD3DKMTCheckVidPnExclusiveOwnership || pD3DKMTCheckVidPnExclusiveOwnership(NULL) == STATUS_PROCEDURE_NOT_FOUND) + { + skip("D3DKMTCheckVidPnExclusiveOwnership() is unavailable.\n"); + return; + } + + get_factory(device, is_d3d12, &factory); + + if (is_d3d12) + { + hr = ID3D12CommandQueue_GetDevice((ID3D12CommandQueue *)device, &IID_ID3D12Device, (void **)&d3d12_device); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + luid = ID3D12Device_GetAdapterLuid(d3d12_device); + ID3D12Device_Release(d3d12_device); + hr = IDXGIFactory_QueryInterface(factory, &IID_IDXGIFactory4, (void **)&factory4); + if (hr == E_NOINTERFACE) + { + skip("DXGI 1.4 unsupported.\n"); + IDXGIFactory_Release(factory); + return; + } + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + hr = IDXGIFactory4_EnumAdapterByLuid(factory4, luid, &IID_IDXGIAdapter, (void **)&adapter); + IDXGIFactory4_Release(factory4); + if (hr == DXGI_ERROR_NOT_FOUND) + { + skip("Wine doesn't support IDXGIFactory4_EnumAdapterByLuid.\n"); + IDXGIFactory_Release(factory); + return; + } + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + } + else + { + hr = IDXGIDevice_GetAdapter((IDXGIDevice *)device, &adapter); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + } + + hr = IDXGIAdapter_EnumOutputs(adapter, 0, &output); + IDXGIAdapter_Release(adapter); + if (hr == DXGI_ERROR_NOT_FOUND) + { + skip("Adapter doesn't have any outputs.\n"); + IDXGIFactory_Release(factory); + return; + } + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + + hr = IDXGIOutput_GetDesc(output, &output_desc); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + + lstrcpyW(open_adapter_gdi_desc.DeviceName, output_desc.DeviceName); + status = pD3DKMTOpenAdapterFromGdiDisplayName(&open_adapter_gdi_desc); + ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); + + check_ownership_desc.hAdapter = open_adapter_gdi_desc.hAdapter; + check_ownership_desc.VidPnSourceId = open_adapter_gdi_desc.VidPnSourceId; + wait_result(pD3DKMTCheckVidPnExclusiveOwnership, &check_ownership_desc, STATUS_SUCCESS, FALSE); + + 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 = is_d3d12 ? 2 : 1; + swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0); + swapchain_desc.Windowed = TRUE; + swapchain_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD; + swapchain_desc.Flags = 0; + + hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain); + ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr); + + /* Swapchain in fullscreen mode */ + hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, output); + /* DXGI_ERROR_NOT_CURRENTLY_AVAILABLE on some machines. DXGI_ERROR_UNSUPPORTED on Win 7 testbot. */ + if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE || broken(hr == DXGI_ERROR_UNSUPPORTED)) + { + skip("Failed to change fullscreen state.\n"); + goto done; + } + todo_wine_if(is_d3d12) ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + fullscreen = FALSE; + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + todo_wine_if(is_d3d12) ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + todo_wine_if(is_d3d12) ok(fullscreen, "Unexpected fullscreen state.\n"); + if (is_d3d12) + wait_result(pD3DKMTCheckVidPnExclusiveOwnership, &check_ownership_desc, STATUS_SUCCESS, FALSE); + else + wait_result(pD3DKMTCheckVidPnExclusiveOwnership, &check_ownership_desc, STATUS_GRAPHICS_PRESENT_OCCLUDED, TRUE); + hr = IDXGIOutput_TakeOwnership(output, device, FALSE); + todo_wine ok(hr == (is_d3d12 ? E_NOINTERFACE : E_INVALIDARG), "Got unexpected hr %#x.\n", hr); + hr = IDXGIOutput_TakeOwnership(output, device, TRUE); + todo_wine ok(hr == (is_d3d12 ? E_NOINTERFACE : S_OK), "Got unexpected hr %#x.\n", hr); + /* Calling IDXGIOutput_ReleaseOwnership makes it unoccluded. So we can be confident about IDXGIOutput_TakeOwnership + * was called in IDXGISwapChain_SetFullscreenState */ + IDXGIOutput_ReleaseOwnership(output); + wait_result(pD3DKMTCheckVidPnExclusiveOwnership, &check_ownership_desc, STATUS_SUCCESS, FALSE); + + /* IDXGIOutput_TakeOwnership always return E_NOINTERFACE for d3d12. Tests finished */ + if (is_d3d12) + goto done; + + hr = IDXGIOutput_TakeOwnership(output, device, FALSE); + todo_wine ok(hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE, "Got unexpected hr %#x.\n", hr); + IDXGIOutput_ReleaseOwnership(output); + + hr = IDXGIOutput_TakeOwnership(output, device, TRUE); + todo_wine ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + /* The following results shows that IDXGIOutput_TakeOwnership(output, device, TRUE) is used in + * SetFullscreenState(swapchain, TRUE, NULL) + * + * This make me believe the MSDN documentation is saying the opposite about the last parameter, + * " + * HRESULT TakeOwnership(IUnknown *pDevice, BOOL Exclusive); + * Name: Exclusive Type: BOOL + * Set to TRUE to enable other threads or applications to take ownership of the device; otherwise, set to FALSE. + * " + * + * Reasons: + * 1. The parameter name is called 'Exclusive' + * 2. D3DKMTSetVidPnSourceOwner(D3DKMT_VIDPNSOURCEOWNER_EXCLUSIVE) makes D3DKMTCheckVidPnExclusiveOwnership return + * STATUS_GRAPHICS_PRESENT_OCCLUDED. And D3DKMTSetVidPnSourceOwner(D3DKMT_VIDPNSOURCEOWNER_SHARED) makes + * D3DKMTCheckVidPnExclusiveOwnership return STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE. So the opposite mapping of + * what MSDN is saying is consistent with the tests. + */ + wait_result(pD3DKMTCheckVidPnExclusiveOwnership, &check_ownership_desc, STATUS_GRAPHICS_PRESENT_OCCLUDED, TRUE); + hr = IDXGIOutput_TakeOwnership(output, device, FALSE); + todo_wine ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); + IDXGIOutput_ReleaseOwnership(output); + + /* Swapchain in windowed mode */ + hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + fullscreen = TRUE; + hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(!fullscreen, "Unexpected fullscreen state.\n"); + wait_result(pD3DKMTCheckVidPnExclusiveOwnership, &check_ownership_desc, STATUS_SUCCESS, FALSE); + + hr = IDXGIOutput_TakeOwnership(output, device, FALSE); + todo_wine ok(hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE, "Got unexpected hr %#x.\n", hr); + + hr = IDXGIOutput_TakeOwnership(output, device, TRUE); + todo_wine ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + wait_result(pD3DKMTCheckVidPnExclusiveOwnership, &check_ownership_desc, STATUS_GRAPHICS_PRESENT_OCCLUDED, TRUE); + IDXGIOutput_ReleaseOwnership(output); + wait_result(pD3DKMTCheckVidPnExclusiveOwnership, &check_ownership_desc, STATUS_SUCCESS, FALSE); + +done: + IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL); + wait_device_idle(device); + + IDXGIOutput_Release(output); + IDXGISwapChain_Release(swapchain); + DestroyWindow(swapchain_desc.OutputWindow); + refcount = IDXGIFactory_Release(factory); + ok(refcount == !is_d3d12, "Got unexpected refcount %u.\n", refcount); + + close_adapter_desc.hAdapter = open_adapter_gdi_desc.hAdapter; + status = pD3DKMTCloseAdapter(&close_adapter_desc); + ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status); +} + static void run_on_d3d10(void (*test_func)(IUnknown *device, BOOL is_d3d12)) { IDXGIDevice *device; @@ -5028,7 +5245,7 @@ static void run_on_d3d12(void (*test_func)(IUnknown *device, BOOL is_d3d12))
START_TEST(dxgi) { - HMODULE dxgi_module, d3d12_module; + HMODULE dxgi_module, d3d12_module, gdi32_module; BOOL enable_debug_layer = FALSE; unsigned int argc, i; ID3D12Debug *debug; @@ -5038,6 +5255,11 @@ START_TEST(dxgi) pCreateDXGIFactory1 = (void *)GetProcAddress(dxgi_module, "CreateDXGIFactory1"); pCreateDXGIFactory2 = (void *)GetProcAddress(dxgi_module, "CreateDXGIFactory2");
+ gdi32_module = GetModuleHandleA("gdi32.dll"); + pD3DKMTCheckVidPnExclusiveOwnership = (void *)GetProcAddress(gdi32_module, "D3DKMTCheckVidPnExclusiveOwnership"); + pD3DKMTCloseAdapter = (void *)GetProcAddress(gdi32_module, "D3DKMTCloseAdapter"); + pD3DKMTOpenAdapterFromGdiDisplayName = (void *)GetProcAddress(gdi32_module, "D3DKMTOpenAdapterFromGdiDisplayName"); + registry_mode.dmSize = sizeof(registry_mode); ok(EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, ®istry_mode), "Failed to get display mode.\n");
@@ -5088,6 +5310,7 @@ START_TEST(dxgi) run_on_d3d10(test_swapchain_present); run_on_d3d10(test_swapchain_backbuffer_index); run_on_d3d10(test_swapchain_formats); + run_on_d3d10(test_output_ownership);
if (!(d3d12_module = LoadLibraryA("d3d12.dll"))) { @@ -5108,6 +5331,7 @@ START_TEST(dxgi) run_on_d3d12(test_swapchain_present); run_on_d3d12(test_swapchain_backbuffer_index); run_on_d3d12(test_swapchain_formats); + run_on_d3d12(test_output_ownership);
FreeLibrary(d3d12_module); }