[PATCH v2 01/10] dxgi/tests: Add IDXGIOutput ownership tests.

Zhiyi Zhang zzhang at codeweavers.com
Mon May 20 07:43:08 CDT 2019


Mostly to show that TakeOwnership and ReleaseOwnership
are based on VidPN ownership.

Signed-off-by: Zhiyi Zhang <zzhang at codeweavers.com>
---
v2: Poll results instead of using Sleep() whenever possible.
    D3DKMTOpenAdapterFromGdiDisplayName opens the adapter used
    by the swapchain. D3DKMTOpenAdapterFromLuid could not be
    used here because we need to know VidPnSourceId as well,
    which D3DKMTOpenAdapterFromLuid doesn't offer.

 dlls/dxgi/tests/dxgi.c | 224 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 223 insertions(+), 1 deletion(-)

diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c
index 79e4bc0c15..b8ab0763dd 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,22 @@ static ULONG get_refcount(void *iface)
     return IUnknown_Release(unknown);
 }
 
+#define wait_result1(func, arg1, expected, todo)                                                           \
+    do                                                                                                     \
+    {                                                                                                      \
+        DWORD total_time = 0;                                                                              \
+        typeof(expected) got;                                                                              \
+        do                                                                                                 \
+        {                                                                                                  \
+            got = func(arg1);                                                                              \
+            if (got == expected)                                                                           \
+                break;                                                                                     \
+            Sleep(wait_step);                                                                              \
+            total_time += wait_step;                                                                       \
+        } while (total_time < wait_timeout);                                                               \
+        todo_wine_if(todo) ok(got == expected, "Expect %#x, got unexpected result %#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 +5012,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
+            || !pD3DKMTCloseAdapter || !pD3DKMTOpenAdapterFromGdiDisplayName)
+    {
+        skip("Required functions are 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_result1(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_result1(pD3DKMTCheckVidPnExclusiveOwnership, &check_ownership_desc, STATUS_SUCCESS, FALSE);
+    else
+        wait_result1(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_result1(pD3DKMTCheckVidPnExclusiveOwnership, &check_ownership_desc, STATUS_SUCCESS, FALSE);
+
+    hr = IDXGIOutput_TakeOwnership(output, device, FALSE);
+    todo_wine ok(hr == (is_d3d12 ? E_NOINTERFACE : DXGI_ERROR_NOT_CURRENTLY_AVAILABLE), "Got unexpected hr %#x.\n", hr);
+    IDXGIOutput_ReleaseOwnership(output);
+
+    hr = IDXGIOutput_TakeOwnership(output, device, TRUE);
+    todo_wine ok(hr == (is_d3d12 ? E_NOINTERFACE : S_OK), "Got unexpected hr %#x.\n", hr);
+    /* So 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.
+     */
+    if (is_d3d12)
+        wait_result1(pD3DKMTCheckVidPnExclusiveOwnership, &check_ownership_desc, STATUS_SUCCESS, FALSE);
+    else
+        wait_result1(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);
+    IDXGIOutput_ReleaseOwnership(output);
+
+    /* Swapchain in windowed mode */
+    hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
+    todo_wine_if(is_d3d12) ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
+    fullscreen = TRUE;
+    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");
+    wait_result1(pD3DKMTCheckVidPnExclusiveOwnership, &check_ownership_desc, STATUS_SUCCESS, FALSE);
+
+    hr = IDXGIOutput_TakeOwnership(output, device, FALSE);
+    todo_wine ok(hr == (is_d3d12 ? E_NOINTERFACE : DXGI_ERROR_NOT_CURRENTLY_AVAILABLE), "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);
+    if (is_d3d12)
+        wait_result1(pD3DKMTCheckVidPnExclusiveOwnership, &check_ownership_desc, STATUS_SUCCESS, FALSE);
+    else
+        wait_result1(pD3DKMTCheckVidPnExclusiveOwnership, &check_ownership_desc, STATUS_GRAPHICS_PRESENT_OCCLUDED, TRUE);
+    IDXGIOutput_ReleaseOwnership(output);
+    wait_result1(pD3DKMTCheckVidPnExclusiveOwnership, &check_ownership_desc, STATUS_SUCCESS, FALSE);
+
+done:
+    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 +5243,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 +5253,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, &registry_mode), "Failed to get display mode.\n");
 
@@ -5088,6 +5308,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 +5329,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);
 }
-- 
2.20.1





More information about the wine-devel mailing list