dxgi: Implement d3d11_swapchain_GetLastPresentCount().
[wine.git] / dlls / dxgi / tests / dxgi.c
blob1e5f9d9cb220bc81ca898145f92d4cd74a4ad7d7
1 /*
2 * Copyright 2008 Henri Verbeet for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <assert.h>
20 #include "ntstatus.h"
21 #define WIN32_NO_STATUS
22 #define COBJMACROS
23 #include "initguid.h"
24 #include "dxgi1_6.h"
25 #include "d3d11.h"
26 #include "d3d12.h"
27 #include "d3d12sdklayers.h"
28 #include "winternl.h"
29 #include "ddk/d3dkmthk.h"
30 #include "wine/heap.h"
31 #include "wine/test.h"
33 enum frame_latency
35 DEFAULT_FRAME_LATENCY = 3,
36 MAX_FRAME_LATENCY = 16,
39 static DEVMODEW registry_mode;
41 static HRESULT (WINAPI *pCreateDXGIFactory1)(REFIID iid, void **factory);
42 static HRESULT (WINAPI *pCreateDXGIFactory2)(UINT flags, REFIID iid, void **factory);
44 static NTSTATUS (WINAPI *pD3DKMTCheckVidPnExclusiveOwnership)(const D3DKMT_CHECKVIDPNEXCLUSIVEOWNERSHIP *desc);
45 static NTSTATUS (WINAPI *pD3DKMTCloseAdapter)(const D3DKMT_CLOSEADAPTER *desc);
46 static NTSTATUS (WINAPI *pD3DKMTOpenAdapterFromGdiDisplayName)(D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME *desc);
48 static HRESULT (WINAPI *pD3D11CreateDevice)(IDXGIAdapter *adapter, D3D_DRIVER_TYPE driver_type, HMODULE swrast, UINT flags,
49 const D3D_FEATURE_LEVEL *feature_levels, UINT levels, UINT sdk_version, ID3D11Device **device_out,
50 D3D_FEATURE_LEVEL *obtained_feature_level, ID3D11DeviceContext **immediate_context);
52 static PFN_D3D12_CREATE_DEVICE pD3D12CreateDevice;
53 static PFN_D3D12_GET_DEBUG_INTERFACE pD3D12GetDebugInterface;
55 static unsigned int use_adapter_idx;
56 static BOOL use_warp_adapter;
57 static BOOL use_mt = TRUE;
59 static struct test_entry
61 void (*test)(void);
62 } *mt_tests;
63 size_t mt_tests_size, mt_test_count;
65 static void queue_test(void (*test)(void))
67 if (mt_test_count >= mt_tests_size)
69 mt_tests_size = max(16, mt_tests_size * 2);
70 mt_tests = heap_realloc(mt_tests, mt_tests_size * sizeof(*mt_tests));
72 mt_tests[mt_test_count++].test = test;
75 static DWORD WINAPI thread_func(void *ctx)
77 LONG *i = ctx, j;
79 while (*i < mt_test_count)
81 j = *i;
82 if (InterlockedCompareExchange(i, j + 1, j) == j)
83 mt_tests[j].test();
86 return 0;
89 static void run_queued_tests(void)
91 unsigned int thread_count, i;
92 HANDLE *threads;
93 SYSTEM_INFO si;
94 LONG test_idx;
96 if (!use_mt)
98 for (i = 0; i < mt_test_count; ++i)
100 mt_tests[i].test();
103 return;
106 GetSystemInfo(&si);
107 thread_count = si.dwNumberOfProcessors;
108 threads = heap_calloc(thread_count, sizeof(*threads));
109 for (i = 0, test_idx = 0; i < thread_count; ++i)
111 threads[i] = CreateThread(NULL, 0, thread_func, &test_idx, 0, NULL);
112 ok(!!threads[i], "Failed to create thread %u.\n", i);
114 WaitForMultipleObjects(thread_count, threads, TRUE, INFINITE);
115 for (i = 0; i < thread_count; ++i)
117 CloseHandle(threads[i]);
119 heap_free(threads);
122 static ULONG get_refcount(void *iface)
124 IUnknown *unknown = iface;
125 IUnknown_AddRef(unknown);
126 return IUnknown_Release(unknown);
129 static void get_virtual_rect(RECT *rect)
131 rect->left = GetSystemMetrics(SM_XVIRTUALSCREEN);
132 rect->top = GetSystemMetrics(SM_YVIRTUALSCREEN);
133 rect->right = rect->left + GetSystemMetrics(SM_CXVIRTUALSCREEN);
134 rect->bottom = rect->top + GetSystemMetrics(SM_CYVIRTUALSCREEN);
137 static BOOL equal_mode_rect(const DEVMODEW *mode1, const DEVMODEW *mode2)
139 return mode1->dmPosition.x == mode2->dmPosition.x
140 && mode1->dmPosition.y == mode2->dmPosition.y
141 && mode1->dmPelsWidth == mode2->dmPelsWidth
142 && mode1->dmPelsHeight == mode2->dmPelsHeight;
145 /* Free original_modes after finished using it */
146 static BOOL save_display_modes(DEVMODEW **original_modes, unsigned int *display_count)
148 unsigned int number, size = 2, count = 0, index = 0;
149 DISPLAY_DEVICEW display_device;
150 DEVMODEW *modes, *tmp;
152 if (!(modes = heap_alloc(size * sizeof(*modes))))
153 return FALSE;
155 display_device.cb = sizeof(display_device);
156 while (EnumDisplayDevicesW(NULL, index++, &display_device, 0))
158 /* Skip software devices */
159 if (swscanf(display_device.DeviceName, L"\\\\.\\DISPLAY%u", &number) != 1)
160 continue;
162 if (!(display_device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP))
163 continue;
165 if (count >= size)
167 size *= 2;
168 if (!(tmp = heap_realloc(modes, size * sizeof(*modes))))
170 heap_free(modes);
171 return FALSE;
173 modes = tmp;
176 memset(&modes[count], 0, sizeof(modes[count]));
177 modes[count].dmSize = sizeof(modes[count]);
178 if (!EnumDisplaySettingsW(display_device.DeviceName, ENUM_CURRENT_SETTINGS, &modes[count]))
180 heap_free(modes);
181 return FALSE;
184 lstrcpyW(modes[count++].dmDeviceName, display_device.DeviceName);
187 *original_modes = modes;
188 *display_count = count;
189 return TRUE;
192 static BOOL restore_display_modes(DEVMODEW *modes, unsigned int count)
194 unsigned int index;
195 LONG ret;
197 for (index = 0; index < count; ++index)
199 ret = ChangeDisplaySettingsExW(modes[index].dmDeviceName, &modes[index], NULL,
200 CDS_UPDATEREGISTRY | CDS_NORESET, NULL);
201 if (ret != DISP_CHANGE_SUCCESSFUL)
202 return FALSE;
204 ret = ChangeDisplaySettingsExW(NULL, NULL, NULL, 0, NULL);
205 return ret == DISP_CHANGE_SUCCESSFUL;
208 /* try to make sure pending X events have been processed before continuing */
209 static void flush_events(void)
211 int diff = 200;
212 DWORD time;
213 MSG msg;
215 time = GetTickCount() + diff;
216 while (diff > 0)
218 if (MsgWaitForMultipleObjects(0, NULL, FALSE, 100, QS_ALLINPUT) == WAIT_TIMEOUT)
219 break;
220 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
221 DispatchMessageA(&msg);
222 diff = time - GetTickCount();
226 #define check_interface(a, b, c, d) check_interface_(__LINE__, a, b, c, d)
227 static HRESULT check_interface_(unsigned int line, void *iface, REFIID iid,
228 BOOL supported, BOOL is_broken)
230 HRESULT hr, expected_hr, broken_hr;
231 IUnknown *unknown = iface, *out;
233 if (supported)
235 expected_hr = S_OK;
236 broken_hr = E_NOINTERFACE;
238 else
240 expected_hr = E_NOINTERFACE;
241 broken_hr = S_OK;
244 out = (IUnknown *)0xdeadbeef;
245 hr = IUnknown_QueryInterface(unknown, iid, (void **)&out);
246 ok_(__FILE__, line)(hr == expected_hr || broken(is_broken && hr == broken_hr),
247 "Got hr %#x, expected %#x.\n", hr, expected_hr);
248 if (SUCCEEDED(hr))
249 IUnknown_Release(out);
250 else
251 ok_(__FILE__, line)(!out, "Got unexpected pointer %p.\n", out);
252 return hr;
255 static BOOL is_flip_model(DXGI_SWAP_EFFECT swap_effect)
257 return swap_effect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL
258 || swap_effect == DXGI_SWAP_EFFECT_FLIP_DISCARD;
261 static unsigned int check_multisample_quality_levels(IDXGIDevice *dxgi_device,
262 DXGI_FORMAT format, unsigned int sample_count)
264 ID3D10Device *device;
265 unsigned int levels;
266 HRESULT hr;
268 hr = IDXGIDevice_QueryInterface(dxgi_device, &IID_ID3D10Device, (void **)&device);
269 ok(hr == S_OK, "Failed to query ID3D10Device, hr %#x.\n", hr);
270 hr = ID3D10Device_CheckMultisampleQualityLevels(device, format, sample_count, &levels);
271 ok(hr == S_OK, "Failed to check multisample quality levels, hr %#x.\n", hr);
272 ID3D10Device_Release(device);
274 return levels;
277 #define MODE_DESC_IGNORE_RESOLUTION 0x00000001u
278 #define MODE_DESC_IGNORE_REFRESH_RATE 0x00000002u
279 #define MODE_DESC_IGNORE_FORMAT 0x00000004u
280 #define MODE_DESC_IGNORE_SCANLINE_ORDERING 0x00000008u
281 #define MODE_DESC_IGNORE_SCALING 0x00000010u
282 #define MODE_DESC_IGNORE_EXACT_RESOLUTION 0x00000020u
284 #define MODE_DESC_CHECK_RESOLUTION (~MODE_DESC_IGNORE_RESOLUTION & ~MODE_DESC_IGNORE_EXACT_RESOLUTION)
285 #define MODE_DESC_CHECK_FORMAT (~MODE_DESC_IGNORE_FORMAT)
287 #define check_mode_desc(a, b, c) check_mode_desc_(__LINE__, a, b, c)
288 static void check_mode_desc_(unsigned int line, const DXGI_MODE_DESC *desc,
289 const DXGI_MODE_DESC *expected_desc, unsigned int ignore_flags)
291 if (!(ignore_flags & MODE_DESC_IGNORE_RESOLUTION))
293 if (ignore_flags & MODE_DESC_IGNORE_EXACT_RESOLUTION)
294 ok_(__FILE__, line)(desc->Width * desc->Height ==
295 expected_desc->Width * expected_desc->Height,
296 "Got resolution %ux%u, expected %ux%u.\n",
297 desc->Width, desc->Height, expected_desc->Width, expected_desc->Height);
298 else
299 ok_(__FILE__, line)(desc->Width == expected_desc->Width &&
300 desc->Height == expected_desc->Height,
301 "Got resolution %ux%u, expected %ux%u.\n",
302 desc->Width, desc->Height, expected_desc->Width, expected_desc->Height);
304 if (!(ignore_flags & MODE_DESC_IGNORE_REFRESH_RATE))
306 ok_(__FILE__, line)(desc->RefreshRate.Numerator == expected_desc->RefreshRate.Numerator
307 && desc->RefreshRate.Denominator == expected_desc->RefreshRate.Denominator,
308 "Got refresh rate %u / %u, expected %u / %u.\n",
309 desc->RefreshRate.Numerator, desc->RefreshRate.Denominator,
310 expected_desc->RefreshRate.Denominator, expected_desc->RefreshRate.Denominator);
312 if (!(ignore_flags & MODE_DESC_IGNORE_FORMAT))
314 ok_(__FILE__, line)(desc->Format == expected_desc->Format,
315 "Got format %#x, expected %#x.\n", desc->Format, expected_desc->Format);
317 if (!(ignore_flags & MODE_DESC_IGNORE_SCANLINE_ORDERING))
319 ok_(__FILE__, line)(desc->ScanlineOrdering == expected_desc->ScanlineOrdering,
320 "Got scanline ordering %#x, expected %#x.\n",
321 desc->ScanlineOrdering, expected_desc->ScanlineOrdering);
323 if (!(ignore_flags & MODE_DESC_IGNORE_SCALING))
325 ok_(__FILE__, line)(desc->Scaling == expected_desc->Scaling,
326 "Got scaling %#x, expected %#x.\n",
327 desc->Scaling, expected_desc->Scaling);
331 static BOOL equal_luid(LUID a, LUID b)
333 return a.LowPart == b.LowPart && a.HighPart == b.HighPart;
336 #define check_adapter_desc(a, b) check_adapter_desc_(__LINE__, a, b)
337 static void check_adapter_desc_(unsigned int line, const DXGI_ADAPTER_DESC *desc,
338 const struct DXGI_ADAPTER_DESC *expected_desc)
340 ok_(__FILE__, line)(!lstrcmpW(desc->Description, expected_desc->Description),
341 "Got description %s, expected %s.\n",
342 wine_dbgstr_w(desc->Description), wine_dbgstr_w(expected_desc->Description));
343 ok_(__FILE__, line)(desc->VendorId == expected_desc->VendorId,
344 "Got vendor id %04x, expected %04x.\n",
345 desc->VendorId, expected_desc->VendorId);
346 ok_(__FILE__, line)(desc->DeviceId == expected_desc->DeviceId,
347 "Got device id %04x, expected %04x.\n",
348 desc->DeviceId, expected_desc->DeviceId);
349 ok_(__FILE__, line)(desc->SubSysId == expected_desc->SubSysId,
350 "Got subsys id %04x, expected %04x.\n",
351 desc->SubSysId, expected_desc->SubSysId);
352 ok_(__FILE__, line)(desc->Revision == expected_desc->Revision,
353 "Got revision %02x, expected %02x.\n",
354 desc->Revision, expected_desc->Revision);
355 ok_(__FILE__, line)(desc->DedicatedVideoMemory == expected_desc->DedicatedVideoMemory,
356 "Got dedicated video memory %lu, expected %lu.\n",
357 desc->DedicatedVideoMemory, expected_desc->DedicatedVideoMemory);
358 ok_(__FILE__, line)(desc->DedicatedSystemMemory == expected_desc->DedicatedSystemMemory,
359 "Got dedicated system memory %lu, expected %lu.\n",
360 desc->DedicatedSystemMemory, expected_desc->DedicatedSystemMemory);
361 ok_(__FILE__, line)(desc->SharedSystemMemory == expected_desc->SharedSystemMemory,
362 "Got shared system memory %lu, expected %lu.\n",
363 desc->SharedSystemMemory, expected_desc->SharedSystemMemory);
364 ok_(__FILE__, line)(equal_luid(desc->AdapterLuid, expected_desc->AdapterLuid),
365 "Got LUID %08x:%08x, expected %08x:%08x.\n",
366 desc->AdapterLuid.HighPart, desc->AdapterLuid.LowPart,
367 expected_desc->AdapterLuid.HighPart, expected_desc->AdapterLuid.LowPart);
370 #define check_output_desc(a, b) check_output_desc_(__LINE__, a, b)
371 static void check_output_desc_(unsigned int line, const DXGI_OUTPUT_DESC *desc,
372 const struct DXGI_OUTPUT_DESC *expected_desc)
374 ok_(__FILE__, line)(!lstrcmpW(desc->DeviceName, expected_desc->DeviceName),
375 "Got unexpected device name %s, expected %s.\n",
376 wine_dbgstr_w(desc->DeviceName), wine_dbgstr_w(expected_desc->DeviceName));
377 ok_(__FILE__, line)(EqualRect(&desc->DesktopCoordinates, &expected_desc->DesktopCoordinates),
378 "Got unexpected desktop coordinates %s, expected %s.\n",
379 wine_dbgstr_rect(&desc->DesktopCoordinates),
380 wine_dbgstr_rect(&expected_desc->DesktopCoordinates));
383 #define check_output_equal(a, b) check_output_equal_(__LINE__, a, b)
384 static void check_output_equal_(unsigned int line, IDXGIOutput *output1, IDXGIOutput *output2)
386 DXGI_OUTPUT_DESC desc1, desc2;
387 HRESULT hr;
389 hr = IDXGIOutput_GetDesc(output1, &desc1);
390 ok(SUCCEEDED(hr), "GetDesc failed, hr %#x.\n", hr);
391 hr = IDXGIOutput_GetDesc(output2, &desc2);
392 ok(SUCCEEDED(hr), "GetDesc failed, hr %#x.\n", hr);
393 check_output_desc_(line, &desc1, &desc2);
396 static BOOL output_belongs_to_adapter(IDXGIOutput *output, IDXGIAdapter *adapter)
398 DXGI_OUTPUT_DESC output_desc, desc;
399 unsigned int output_idx;
400 IDXGIOutput *o;
401 HRESULT hr;
403 hr = IDXGIOutput_GetDesc(output, &output_desc);
404 ok(SUCCEEDED(hr), "Failed to get output desc, hr %#x.\n", hr);
406 for (output_idx = 0; IDXGIAdapter_EnumOutputs(adapter, output_idx, &o) != DXGI_ERROR_NOT_FOUND; ++output_idx)
408 hr = IDXGIOutput_GetDesc(o, &desc);
409 ok(SUCCEEDED(hr), "Failed to get output desc, hr %#x.\n", hr);
410 IDXGIOutput_Release(o);
412 if (!lstrcmpW(desc.DeviceName, output_desc.DeviceName)
413 && EqualRect(&desc.DesktopCoordinates, &output_desc.DesktopCoordinates))
414 return TRUE;
417 return FALSE;
420 struct fullscreen_state
422 DWORD style;
423 DWORD exstyle;
424 RECT window_rect;
425 RECT client_rect;
426 HMONITOR monitor;
427 RECT monitor_rect;
430 struct swapchain_fullscreen_state
432 struct fullscreen_state fullscreen_state;
433 BOOL fullscreen;
434 IDXGIOutput *target;
437 #define capture_fullscreen_state(a, b) capture_fullscreen_state_(__LINE__, a, b)
438 static void capture_fullscreen_state_(unsigned int line, struct fullscreen_state *state, HWND window)
440 MONITORINFOEXW monitor_info;
441 BOOL ret;
443 state->style = GetWindowLongA(window, GWL_STYLE);
444 state->exstyle = GetWindowLongA(window, GWL_EXSTYLE);
446 ret = GetWindowRect(window, &state->window_rect);
447 ok_(__FILE__, line)(ret, "GetWindowRect failed.\n");
448 ret = GetClientRect(window, &state->client_rect);
449 ok_(__FILE__, line)(ret, "GetClientRect failed.\n");
451 state->monitor = MonitorFromWindow(window, MONITOR_DEFAULTTONULL);
452 ok_(__FILE__, line)(!!state->monitor, "Failed to get monitor from window.\n");
454 monitor_info.cbSize = sizeof(monitor_info);
455 ret = GetMonitorInfoW(state->monitor, (MONITORINFO *)&monitor_info);
456 ok_(__FILE__, line)(ret, "Failed to get monitor info.\n");
457 state->monitor_rect = monitor_info.rcMonitor;
460 static void check_fullscreen_state_(unsigned int line, const struct fullscreen_state *state,
461 const struct fullscreen_state *expected_state, BOOL windowed)
463 todo_wine_if(!windowed)
464 ok_(__FILE__, line)((state->style & ~WS_VISIBLE) == (expected_state->style & ~WS_VISIBLE),
465 "Got style %#x, expected %#x.\n",
466 state->style & ~(DWORD)WS_VISIBLE, expected_state->style & ~(DWORD)WS_VISIBLE);
467 ok_(__FILE__, line)((state->exstyle & ~WS_EX_TOPMOST) == (expected_state->exstyle & ~WS_EX_TOPMOST),
468 "Got exstyle %#x, expected %#x.\n",
469 state->exstyle & ~(DWORD)WS_EX_TOPMOST, expected_state->exstyle & ~(DWORD)WS_EX_TOPMOST);
470 ok_(__FILE__, line)(EqualRect(&state->window_rect, &expected_state->window_rect),
471 "Got window rect %s, expected %s.\n",
472 wine_dbgstr_rect(&state->window_rect), wine_dbgstr_rect(&expected_state->window_rect));
473 ok_(__FILE__, line)(EqualRect(&state->client_rect, &expected_state->client_rect),
474 "Got client rect %s, expected %s.\n",
475 wine_dbgstr_rect(&state->client_rect), wine_dbgstr_rect(&expected_state->client_rect));
476 ok_(__FILE__, line)(state->monitor == expected_state->monitor,
477 "Got monitor %p, expected %p.\n",
478 state->monitor, expected_state->monitor);
479 ok_(__FILE__, line)(EqualRect(&state->monitor_rect, &expected_state->monitor_rect),
480 "Got monitor rect %s, expected %s.\n",
481 wine_dbgstr_rect(&state->monitor_rect), wine_dbgstr_rect(&expected_state->monitor_rect));
484 #define check_window_fullscreen_state(a, b) check_window_fullscreen_state_(__LINE__, a, b, TRUE)
485 static void check_window_fullscreen_state_(unsigned int line, HWND window,
486 const struct fullscreen_state *expected_state, BOOL windowed)
488 struct fullscreen_state current_state;
489 capture_fullscreen_state_(line, &current_state, window);
490 check_fullscreen_state_(line, &current_state, expected_state, windowed);
493 #define check_swapchain_fullscreen_state(a, b) check_swapchain_fullscreen_state_(__LINE__, a, b)
494 static void check_swapchain_fullscreen_state_(unsigned int line, IDXGISwapChain *swapchain,
495 const struct swapchain_fullscreen_state *expected_state)
497 IDXGIOutput *containing_output, *target;
498 DXGI_SWAP_CHAIN_DESC swapchain_desc;
499 BOOL fullscreen;
500 HRESULT hr;
502 hr = IDXGISwapChain_GetDesc(swapchain, &swapchain_desc);
503 ok_(__FILE__, line)(hr == S_OK, "Failed to get swapchain desc, hr %#x.\n", hr);
504 check_window_fullscreen_state_(line, swapchain_desc.OutputWindow,
505 &expected_state->fullscreen_state, swapchain_desc.Windowed);
507 ok_(__FILE__, line)(swapchain_desc.Windowed == !expected_state->fullscreen,
508 "Got windowed %#x, expected %#x.\n",
509 swapchain_desc.Windowed, !expected_state->fullscreen);
511 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, &target);
512 ok_(__FILE__, line)(hr == S_OK, "Failed to get fullscreen state, hr %#x.\n", hr);
513 ok_(__FILE__, line)(fullscreen == expected_state->fullscreen, "Got fullscreen %#x, expected %#x.\n",
514 fullscreen, expected_state->fullscreen);
516 if (!swapchain_desc.Windowed && expected_state->fullscreen)
518 IDXGIAdapter *adapter;
520 hr = IDXGISwapChain_GetContainingOutput(swapchain, &containing_output);
521 ok_(__FILE__, line)(hr == S_OK, "Failed to get containing output, hr %#x.\n", hr);
523 hr = IDXGIOutput_GetParent(containing_output, &IID_IDXGIAdapter, (void **)&adapter);
524 ok_(__FILE__, line)(hr == S_OK, "Failed to get parent, hr %#x.\n", hr);
526 check_output_equal_(line, target, expected_state->target);
527 ok_(__FILE__, line)(target == containing_output, "Got target %p, expected %p.\n",
528 target, containing_output);
529 ok_(__FILE__, line)(output_belongs_to_adapter(target, adapter),
530 "Output %p doesn't belong to adapter %p.\n",
531 target, adapter);
533 IDXGIOutput_Release(target);
534 IDXGIOutput_Release(containing_output);
535 IDXGIAdapter_Release(adapter);
537 else
539 ok_(__FILE__, line)(!target, "Got unexpected target %p.\n", target);
543 #define compute_expected_swapchain_fullscreen_state_after_fullscreen_change(a, b, c, d, e, f) \
544 compute_expected_swapchain_fullscreen_state_after_fullscreen_change_(__LINE__, a, b, c, d, e, f)
545 static void compute_expected_swapchain_fullscreen_state_after_fullscreen_change_(unsigned int line,
546 struct swapchain_fullscreen_state *state, const DXGI_SWAP_CHAIN_DESC *swapchain_desc,
547 const RECT *old_monitor_rect, unsigned int new_width, unsigned int new_height, IDXGIOutput *target)
549 if (!new_width && !new_height)
551 RECT client_rect;
552 GetClientRect(swapchain_desc->OutputWindow, &client_rect);
553 new_width = client_rect.right - client_rect.left;
554 new_height = client_rect.bottom - client_rect.top;
557 if (target)
559 DXGI_MODE_DESC mode_desc = swapchain_desc->BufferDesc;
560 HRESULT hr;
562 mode_desc.Width = new_width;
563 mode_desc.Height = new_height;
564 hr = IDXGIOutput_FindClosestMatchingMode(target, &mode_desc, &mode_desc, NULL);
565 ok_(__FILE__, line)(SUCCEEDED(hr), "FindClosestMatchingMode failed, hr %#x.\n", hr);
566 new_width = mode_desc.Width;
567 new_height = mode_desc.Height;
570 state->fullscreen_state.style &= WS_VISIBLE | WS_CLIPSIBLINGS;
571 state->fullscreen_state.exstyle &= WS_EX_TOPMOST;
573 state->fullscreen = TRUE;
574 if (swapchain_desc->Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH)
576 unsigned int new_x = (old_monitor_rect->left >= 0)
577 ? old_monitor_rect->left : old_monitor_rect->right - new_width;
578 unsigned new_y = (old_monitor_rect->top >= 0)
579 ? old_monitor_rect->top : old_monitor_rect->bottom - new_height;
580 RECT new_monitor_rect = {0, 0, new_width, new_height};
581 OffsetRect(&new_monitor_rect, new_x, new_y);
583 SetRect(&state->fullscreen_state.client_rect, 0, 0, new_width, new_height);
584 state->fullscreen_state.monitor_rect = new_monitor_rect;
585 state->fullscreen_state.window_rect = new_monitor_rect;
587 if (target)
588 state->target = target;
590 else
592 state->fullscreen_state.window_rect = *old_monitor_rect;
593 SetRect(&state->fullscreen_state.client_rect, 0, 0,
594 old_monitor_rect->right - old_monitor_rect->left,
595 old_monitor_rect->bottom - old_monitor_rect->top);
599 #define wait_fullscreen_state(a, b, c) wait_fullscreen_state_(__LINE__, a, b, c)
600 static void wait_fullscreen_state_(unsigned int line, IDXGISwapChain *swapchain, BOOL expected, BOOL todo)
602 static const unsigned int wait_timeout = 2000;
603 static const unsigned int wait_step = 100;
604 unsigned int total_time = 0;
605 HRESULT hr;
606 BOOL state;
608 while (total_time < wait_timeout)
610 state = !expected;
611 if (FAILED(hr = IDXGISwapChain_GetFullscreenState(swapchain, &state, NULL)))
612 break;
613 if (state == expected)
614 break;
615 Sleep(wait_step);
616 total_time += wait_step;
618 ok_(__FILE__, line)(hr == S_OK, "Failed to get fullscreen state, hr %#x.\n", hr);
619 todo_wine_if(todo) ok_(__FILE__, line)(state == expected,
620 "Got unexpected state %#x, expected %#x.\n", state, expected);
623 /* VidPN exclusive ownership doesn't change immediately.
624 * This helper is used to wait for the expected status */
625 #define get_expected_vidpn_exclusive_ownership(a, b) \
626 get_expected_vidpn_exclusive_ownership_(__LINE__, a, b)
627 static NTSTATUS get_expected_vidpn_exclusive_ownership_(unsigned int line,
628 const D3DKMT_CHECKVIDPNEXCLUSIVEOWNERSHIP *desc, NTSTATUS expected)
630 static const unsigned int wait_timeout = 2000;
631 static const unsigned int wait_step = 100;
632 unsigned int total_time = 0;
633 NTSTATUS status;
635 while (total_time < wait_timeout)
637 status = pD3DKMTCheckVidPnExclusiveOwnership(desc);
638 if (status == expected)
639 break;
640 Sleep(wait_step);
641 total_time += wait_step;
643 return status;
646 static HWND create_window(void)
648 RECT r = {0, 0, 640, 480};
650 AdjustWindowRect(&r, WS_OVERLAPPEDWINDOW | WS_VISIBLE, FALSE);
652 return CreateWindowA("static", "dxgi_test", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
653 0, 0, r.right - r.left, r.bottom - r.top, NULL, NULL, NULL, NULL);
656 static IDXGIAdapter *create_adapter(void)
658 IDXGIFactory4 *factory4;
659 IDXGIFactory *factory;
660 IDXGIAdapter *adapter;
661 HRESULT hr;
663 if (!use_warp_adapter && !use_adapter_idx)
664 return NULL;
666 if (FAILED(hr = CreateDXGIFactory(&IID_IDXGIFactory, (void **)&factory)))
668 trace("Failed to create IDXGIFactory, hr %#x.\n", hr);
669 return NULL;
672 adapter = NULL;
673 if (use_warp_adapter)
675 if (SUCCEEDED(hr = IDXGIFactory_QueryInterface(factory, &IID_IDXGIFactory4, (void **)&factory4)))
677 hr = IDXGIFactory4_EnumWarpAdapter(factory4, &IID_IDXGIAdapter, (void **)&adapter);
678 IDXGIFactory4_Release(factory4);
680 else
682 trace("Failed to get IDXGIFactory4, hr %#x.\n", hr);
685 else
687 hr = IDXGIFactory_EnumAdapters(factory, use_adapter_idx, &adapter);
689 IDXGIFactory_Release(factory);
690 if (FAILED(hr))
691 trace("Failed to get adapter, hr %#x.\n", hr);
692 return adapter;
695 static IDXGIDevice *create_device(unsigned int flags)
697 IDXGIDevice *dxgi_device;
698 ID3D10Device1 *device;
699 IDXGIAdapter *adapter;
700 HRESULT hr;
702 adapter = create_adapter();
703 hr = D3D10CreateDevice1(adapter, D3D10_DRIVER_TYPE_HARDWARE, NULL,
704 flags, D3D10_FEATURE_LEVEL_10_0, D3D10_1_SDK_VERSION, &device);
705 if (adapter)
706 IDXGIAdapter_Release(adapter);
707 if (SUCCEEDED(hr))
708 goto success;
710 if (SUCCEEDED(D3D10CreateDevice1(NULL, D3D10_DRIVER_TYPE_WARP, NULL,
711 flags, D3D10_FEATURE_LEVEL_10_0, D3D10_1_SDK_VERSION, &device)))
712 goto success;
713 if (SUCCEEDED(D3D10CreateDevice1(NULL, D3D10_DRIVER_TYPE_REFERENCE, NULL,
714 flags, D3D10_FEATURE_LEVEL_10_0, D3D10_1_SDK_VERSION, &device)))
715 goto success;
717 return NULL;
719 success:
720 hr = ID3D10Device1_QueryInterface(device, &IID_IDXGIDevice, (void **)&dxgi_device);
721 ok(SUCCEEDED(hr), "Created device does not implement IDXGIDevice\n");
722 ID3D10Device1_Release(device);
724 return dxgi_device;
727 static IDXGIDevice *create_d3d11_device(void)
729 static const D3D_FEATURE_LEVEL feature_level[] =
731 D3D_FEATURE_LEVEL_11_0,
733 unsigned int feature_level_count = ARRAY_SIZE(feature_level);
734 IDXGIDevice *device = NULL;
735 ID3D11Device *d3d_device;
736 HRESULT hr;
738 if (!pD3D11CreateDevice)
739 return NULL;
741 hr = pD3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, feature_level, feature_level_count,
742 D3D11_SDK_VERSION, &d3d_device, NULL, NULL);
743 if (FAILED(hr))
744 hr = pD3D11CreateDevice(NULL, D3D_DRIVER_TYPE_WARP, NULL, 0, feature_level, feature_level_count,
745 D3D11_SDK_VERSION, &d3d_device, NULL, NULL);
746 if (FAILED(hr))
747 hr = pD3D11CreateDevice(NULL, D3D_DRIVER_TYPE_REFERENCE, NULL, 0, feature_level, feature_level_count,
748 D3D11_SDK_VERSION, &d3d_device, NULL, NULL);
750 if (SUCCEEDED(hr))
752 hr = ID3D11Device_QueryInterface(d3d_device, &IID_IDXGIDevice, (void **)&device);
753 ok(SUCCEEDED(hr), "Created device does not implement IDXGIDevice.\n");
754 ID3D11Device_Release(d3d_device);
757 return device;
760 static ID3D12Device *create_d3d12_device(void)
762 IDXGIAdapter *adapter;
763 ID3D12Device *device;
764 HRESULT hr;
766 if (!pD3D12CreateDevice)
767 return NULL;
769 adapter = create_adapter();
770 hr = pD3D12CreateDevice((IUnknown *)adapter, D3D_FEATURE_LEVEL_11_0, &IID_ID3D12Device, (void **)&device);
771 if (adapter)
772 IDXGIAdapter_Release(adapter);
773 if (FAILED(hr))
774 return NULL;
776 return device;
779 static ID3D12CommandQueue *create_d3d12_direct_queue(ID3D12Device *device)
781 D3D12_COMMAND_QUEUE_DESC command_queue_desc;
782 ID3D12CommandQueue *queue;
783 HRESULT hr;
785 command_queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
786 command_queue_desc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
787 command_queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
788 command_queue_desc.NodeMask = 0;
789 hr = ID3D12Device_CreateCommandQueue(device, &command_queue_desc,
790 &IID_ID3D12CommandQueue, (void **)&queue);
791 ok(hr == S_OK, "Failed to create command queue, hr %#x.\n", hr);
792 return queue;
795 static HRESULT wait_for_fence(ID3D12Fence *fence, UINT64 value)
797 HANDLE event;
798 HRESULT hr;
799 DWORD ret;
801 if (ID3D12Fence_GetCompletedValue(fence) >= value)
802 return S_OK;
804 if (!(event = CreateEventA(NULL, FALSE, FALSE, NULL)))
805 return E_FAIL;
807 if (FAILED(hr = ID3D12Fence_SetEventOnCompletion(fence, value, event)))
809 CloseHandle(event);
810 return hr;
813 ret = WaitForSingleObject(event, INFINITE);
814 CloseHandle(event);
815 return ret == WAIT_OBJECT_0 ? S_OK : E_FAIL;
818 #define wait_queue_idle(a, b) wait_queue_idle_(__LINE__, a, b)
819 static void wait_queue_idle_(unsigned int line, ID3D12Device *device, ID3D12CommandQueue *queue)
821 ID3D12Fence *fence;
822 HRESULT hr;
824 hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE,
825 &IID_ID3D12Fence, (void **)&fence);
826 ok_(__FILE__, line)(hr == S_OK, "Failed to create fence, hr %#x.\n", hr);
828 hr = ID3D12CommandQueue_Signal(queue, fence, 1);
829 ok_(__FILE__, line)(hr == S_OK, "Failed to signal fence, hr %#x.\n", hr);
830 hr = wait_for_fence(fence, 1);
831 ok_(__FILE__, line)(hr == S_OK, "Failed to wait for fence, hr %#x.\n", hr);
833 ID3D12Fence_Release(fence);
836 #define wait_device_idle(a) wait_device_idle_(__LINE__, a)
837 static void wait_device_idle_(unsigned int line, IUnknown *device)
839 ID3D12Device *d3d12_device;
840 ID3D12CommandQueue *queue;
841 HRESULT hr;
843 hr = IUnknown_QueryInterface(device, &IID_ID3D12CommandQueue, (void **)&queue);
844 if (hr != S_OK)
845 return;
847 hr = ID3D12CommandQueue_GetDevice(queue, &IID_ID3D12Device, (void **)&d3d12_device);
848 ok_(__FILE__, line)(hr == S_OK, "Failed to get d3d12 device, hr %#x.\n", hr);
850 wait_queue_idle_(line, d3d12_device, queue);
852 ID3D12CommandQueue_Release(queue);
853 ID3D12Device_Release(d3d12_device);
856 #define get_factory(a, b, c) get_factory_(__LINE__, a, b, c)
857 static void get_factory_(unsigned int line, IUnknown *device, BOOL is_d3d12, IDXGIFactory **factory)
859 IDXGIDevice *dxgi_device;
860 IDXGIAdapter *adapter;
861 HRESULT hr;
863 if (is_d3d12)
865 hr = CreateDXGIFactory(&IID_IDXGIFactory, (void **)factory);
866 ok_(__FILE__, line)(hr == S_OK, "Failed to create factory, hr %#x.\n", hr);
868 else
870 dxgi_device = (IDXGIDevice *)device;
871 hr = IDXGIDevice_GetAdapter(dxgi_device, &adapter);
872 ok_(__FILE__, line)(hr == S_OK, "Failed to get adapter, hr %#x.\n", hr);
873 hr = IDXGIAdapter_GetParent(adapter, &IID_IDXGIFactory, (void **)factory);
874 ok_(__FILE__, line)(hr == S_OK, "Failed to get parent, hr %#x.\n", hr);
875 IDXGIAdapter_Release(adapter);
879 #define get_adapter(a, b) get_adapter_(__LINE__, a, b)
880 static IDXGIAdapter *get_adapter_(unsigned int line, IUnknown *device, BOOL is_d3d12)
882 IDXGIAdapter *adapter = NULL;
883 ID3D12Device *d3d12_device;
884 IDXGIFactory4 *factory4;
885 IDXGIFactory *factory;
886 HRESULT hr;
887 LUID luid;
889 if (is_d3d12)
891 get_factory_(line, device, is_d3d12, &factory);
892 hr = ID3D12CommandQueue_GetDevice((ID3D12CommandQueue *)device, &IID_ID3D12Device, (void **)&d3d12_device);
893 ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#x.\n", hr);
894 luid = ID3D12Device_GetAdapterLuid(d3d12_device);
895 ID3D12Device_Release(d3d12_device);
896 hr = IDXGIFactory_QueryInterface(factory, &IID_IDXGIFactory4, (void **)&factory4);
897 ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#x.\n", hr);
898 hr = IDXGIFactory4_EnumAdapterByLuid(factory4, luid, &IID_IDXGIAdapter, (void **)&adapter);
899 IDXGIFactory4_Release(factory4);
900 IDXGIFactory_Release(factory);
902 else
904 hr = IDXGIDevice_GetAdapter((IDXGIDevice *)device, &adapter);
905 ok_(__FILE__, line)(hr == S_OK, "Got unexpected hr %#x.\n", hr);
908 return adapter;
911 #define create_swapchain(a, b, c) create_swapchain_(__LINE__, a, b, c)
912 static IDXGISwapChain *create_swapchain_(unsigned int line, IUnknown *device, BOOL is_d3d12, HWND window)
914 DXGI_SWAP_CHAIN_DESC desc;
915 IDXGISwapChain *swapchain;
916 IDXGIFactory *factory;
917 HRESULT hr;
919 desc.BufferDesc.Width = 640;
920 desc.BufferDesc.Height = 480;
921 desc.BufferDesc.RefreshRate.Numerator = 60;
922 desc.BufferDesc.RefreshRate.Denominator = 1;
923 desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
924 desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
925 desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
926 desc.SampleDesc.Count = 1;
927 desc.SampleDesc.Quality = 0;
928 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
929 desc.BufferCount = is_d3d12 ? 2 : 1;
930 desc.OutputWindow = window;
931 desc.Windowed = TRUE;
932 desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
933 desc.Flags = 0;
935 get_factory(device, is_d3d12, &factory);
936 hr = IDXGIFactory_CreateSwapChain(factory, device, &desc, &swapchain);
937 ok_(__FILE__, line)(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
938 IDXGIFactory_Release(factory);
940 return swapchain;
943 static void test_adapter_desc(void)
945 DXGI_ADAPTER_DESC1 desc1;
946 IDXGIAdapter1 *adapter1;
947 DXGI_ADAPTER_DESC desc;
948 IDXGIAdapter *adapter;
949 IDXGIDevice *device;
950 ULONG refcount;
951 HRESULT hr;
953 if (!(device = create_device(0)))
955 skip("Failed to create device.\n");
956 return;
959 hr = IDXGIDevice_GetAdapter(device, &adapter);
960 ok(SUCCEEDED(hr), "GetAdapter failed, hr %#x.\n", hr);
962 hr = IDXGIAdapter_GetDesc(adapter, NULL);
963 ok(hr == E_INVALIDARG, "GetDesc returned %#x, expected %#x.\n",
964 hr, E_INVALIDARG);
966 hr = IDXGIAdapter_GetDesc(adapter, &desc);
967 ok(SUCCEEDED(hr), "GetDesc failed, hr %#x.\n", hr);
969 trace("%s.\n", wine_dbgstr_w(desc.Description));
970 trace("%04x: %04x:%04x (rev %02x).\n",
971 desc.SubSysId, desc.VendorId, desc.DeviceId, desc.Revision);
972 trace("Dedicated video memory: %lu (%lu MB).\n",
973 desc.DedicatedVideoMemory, desc.DedicatedVideoMemory / (1024 * 1024));
974 trace("Dedicated system memory: %lu (%lu MB).\n",
975 desc.DedicatedSystemMemory, desc.DedicatedSystemMemory / (1024 * 1024));
976 trace("Shared system memory: %lu (%lu MB).\n",
977 desc.SharedSystemMemory, desc.SharedSystemMemory / (1024 * 1024));
978 trace("LUID: %08x:%08x.\n", desc.AdapterLuid.HighPart, desc.AdapterLuid.LowPart);
980 hr = IDXGIAdapter_QueryInterface(adapter, &IID_IDXGIAdapter1, (void **)&adapter1);
981 ok(SUCCEEDED(hr) || broken(hr == E_NOINTERFACE), "Got unexpected hr %#x.\n", hr);
982 if (hr == E_NOINTERFACE)
983 goto done;
985 hr = IDXGIAdapter1_GetDesc1(adapter1, &desc1);
986 ok(SUCCEEDED(hr), "GetDesc1 failed, hr %#x.\n", hr);
988 ok(!lstrcmpW(desc.Description, desc1.Description),
989 "Got unexpected description %s.\n", wine_dbgstr_w(desc1.Description));
990 ok(desc1.VendorId == desc.VendorId, "Got unexpected vendor ID %04x.\n", desc1.VendorId);
991 ok(desc1.DeviceId == desc.DeviceId, "Got unexpected device ID %04x.\n", desc1.DeviceId);
992 ok(desc1.SubSysId == desc.SubSysId, "Got unexpected sub system ID %04x.\n", desc1.SubSysId);
993 ok(desc1.Revision == desc.Revision, "Got unexpected revision %02x.\n", desc1.Revision);
994 ok(desc1.DedicatedVideoMemory == desc.DedicatedVideoMemory,
995 "Got unexpected dedicated video memory %lu.\n", desc1.DedicatedVideoMemory);
996 ok(desc1.DedicatedSystemMemory == desc.DedicatedSystemMemory,
997 "Got unexpected dedicated system memory %lu.\n", desc1.DedicatedSystemMemory);
998 ok(desc1.SharedSystemMemory == desc.SharedSystemMemory,
999 "Got unexpected shared system memory %lu.\n", desc1.SharedSystemMemory);
1000 ok(equal_luid(desc1.AdapterLuid, desc.AdapterLuid),
1001 "Got unexpected adapter LUID %08x:%08x.\n", desc1.AdapterLuid.HighPart, desc1.AdapterLuid.LowPart);
1002 trace("Flags: %08x.\n", desc1.Flags);
1004 IDXGIAdapter1_Release(adapter1);
1006 done:
1007 IDXGIAdapter_Release(adapter);
1008 refcount = IDXGIDevice_Release(device);
1009 ok(!refcount, "Device has %u references left.\n", refcount);
1012 static void test_adapter_luid(void)
1014 DXGI_ADAPTER_DESC device_adapter_desc, desc, desc2;
1015 static const LUID luid = {0xdeadbeef, 0xdeadbeef};
1016 IDXGIAdapter *adapter, *adapter2;
1017 unsigned int found_adapter_count;
1018 unsigned int adapter_index;
1019 BOOL is_null_luid_adapter;
1020 IDXGIFactory4 *factory4;
1021 IDXGIFactory *factory;
1022 BOOL have_unique_luid;
1023 IDXGIDevice *device;
1024 ULONG refcount;
1025 HRESULT hr;
1027 if (!(device = create_device(0)))
1029 skip("Failed to create device.\n");
1030 return;
1033 hr = IDXGIDevice_GetAdapter(device, &adapter);
1034 ok(hr == S_OK, "Failed to get adapter, hr %#x.\n", hr);
1035 hr = IDXGIAdapter_GetDesc(adapter, &device_adapter_desc);
1036 ok(hr == S_OK, "Failed to get adapter desc, hr %#x.\n", hr);
1037 IDXGIAdapter_Release(adapter);
1038 refcount = IDXGIDevice_Release(device);
1039 ok(!refcount, "Device has %u references left.\n", refcount);
1041 is_null_luid_adapter = !device_adapter_desc.AdapterLuid.LowPart
1042 && !device_adapter_desc.SubSysId && !device_adapter_desc.Revision
1043 && !device_adapter_desc.VendorId && !device_adapter_desc.DeviceId;
1045 hr = CreateDXGIFactory(&IID_IDXGIFactory, (void **)&factory);
1046 ok(hr == S_OK, "Failed to create DXGI factory, hr %#x.\n", hr);
1048 hr = IDXGIFactory_QueryInterface(factory, &IID_IDXGIFactory4, (void **)&factory4);
1049 ok(hr == S_OK || hr == E_NOINTERFACE, "Got unexpected hr %#x.\n", hr);
1051 have_unique_luid = TRUE;
1052 found_adapter_count = 0;
1053 adapter_index = 0;
1054 while ((hr = IDXGIFactory_EnumAdapters(factory, adapter_index, &adapter)) == S_OK)
1056 hr = IDXGIAdapter_GetDesc(adapter, &desc);
1057 ok(hr == S_OK, "Failed to get adapter desc, hr %#x.\n", hr);
1059 if (equal_luid(desc.AdapterLuid, device_adapter_desc.AdapterLuid))
1061 check_adapter_desc(&desc, &device_adapter_desc);
1062 ++found_adapter_count;
1065 if (equal_luid(desc.AdapterLuid, luid))
1066 have_unique_luid = FALSE;
1068 if (factory4)
1070 hr = IDXGIFactory4_EnumAdapterByLuid(factory4, desc.AdapterLuid,
1071 &IID_IDXGIAdapter, (void **)&adapter2);
1072 ok(hr == S_OK, "Failed to enum adapter by LUID, hr %#x.\n", hr);
1073 hr = IDXGIAdapter_GetDesc(adapter2, &desc2);
1074 ok(hr == S_OK, "Failed to get adapter desc, hr %#x.\n", hr);
1075 check_adapter_desc(&desc2, &desc);
1076 ok(adapter2 != adapter, "Expected to get new instance of IDXGIAdapter.\n");
1077 refcount = IDXGIAdapter_Release(adapter2);
1078 ok(!refcount, "Adapter has %u references left.\n", refcount);
1081 refcount = IDXGIAdapter_Release(adapter);
1082 ok(!refcount, "Adapter has %u references left.\n", refcount);
1084 ++adapter_index;
1086 ok(hr == DXGI_ERROR_NOT_FOUND, "Got unexpected hr %#x.\n", hr);
1088 /* Older versions of WARP aren't enumerated by IDXGIFactory_EnumAdapters(). */
1089 ok(found_adapter_count == 1 || broken(is_null_luid_adapter),
1090 "Found %u adapters for LUID %08x:%08x.\n",
1091 found_adapter_count, device_adapter_desc.AdapterLuid.HighPart,
1092 device_adapter_desc.AdapterLuid.LowPart);
1094 if (factory4)
1095 IDXGIFactory4_Release(factory4);
1096 refcount = IDXGIFactory_Release(factory);
1097 ok(!refcount, "Factory has %u references left.\n", refcount);
1099 if (!pCreateDXGIFactory2
1100 || FAILED(hr = pCreateDXGIFactory2(0, &IID_IDXGIFactory4, (void **)&factory4)))
1102 skip("DXGI 1.4 is not available.\n");
1103 return;
1106 hr = IDXGIFactory4_EnumAdapterByLuid(factory4, device_adapter_desc.AdapterLuid,
1107 &IID_IDXGIAdapter, NULL);
1108 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
1110 hr = IDXGIFactory4_EnumAdapterByLuid(factory4, device_adapter_desc.AdapterLuid,
1111 &IID_IDXGIAdapter, (void **)&adapter);
1112 ok(hr == S_OK, "Failed to enum adapter by LUID, hr %#x.\n", hr);
1113 if (SUCCEEDED(hr))
1115 hr = IDXGIAdapter_GetDesc(adapter, &desc);
1116 ok(hr == S_OK, "Failed to get adapter desc, hr %#x.\n", hr);
1117 check_adapter_desc(&desc, &device_adapter_desc);
1118 refcount = IDXGIAdapter_Release(adapter);
1119 ok(!refcount, "Adapter has %u references left.\n", refcount);
1122 if (have_unique_luid)
1124 hr = IDXGIFactory4_EnumAdapterByLuid(factory4, luid, &IID_IDXGIAdapter, (void **)&adapter);
1125 ok(hr == DXGI_ERROR_NOT_FOUND, "Got unexpected hr %#x.\n", hr);
1127 else
1129 skip("Our LUID is not unique.\n");
1132 refcount = IDXGIFactory4_Release(factory4);
1133 ok(!refcount, "Factory has %u references left.\n", refcount);
1136 static void test_query_video_memory_info(void)
1138 DXGI_QUERY_VIDEO_MEMORY_INFO memory_info;
1139 IDXGIAdapter3 *adapter3;
1140 IDXGIAdapter *adapter;
1141 IDXGIDevice *device;
1142 ULONG refcount;
1143 HRESULT hr;
1145 if (!(device = create_device(0)))
1147 skip("Failed to create device.\n");
1148 return;
1151 hr = IDXGIDevice_GetAdapter(device, &adapter);
1152 ok(hr == S_OK, "Failed to get adapter, hr %#x.\n", hr);
1153 hr = IDXGIAdapter_QueryInterface(adapter, &IID_IDXGIAdapter3, (void **)&adapter3);
1154 ok(hr == S_OK || hr == E_NOINTERFACE, "Got unexpected hr %#x.\n", hr);
1155 if (hr == E_NOINTERFACE)
1156 goto done;
1158 hr = IDXGIAdapter3_QueryVideoMemoryInfo(adapter3, 0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &memory_info);
1159 ok(hr == S_OK, "Failed to query video memory info, hr %#x.\n", hr);
1160 ok(memory_info.Budget >= memory_info.AvailableForReservation,
1161 "Available for reservation 0x%s is greater than budget 0x%s.\n",
1162 wine_dbgstr_longlong(memory_info.AvailableForReservation),
1163 wine_dbgstr_longlong(memory_info.Budget));
1164 ok(!memory_info.CurrentReservation, "Got unexpected current reservation 0x%s.\n",
1165 wine_dbgstr_longlong(memory_info.CurrentReservation));
1167 hr = IDXGIAdapter3_QueryVideoMemoryInfo(adapter3, 0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL, &memory_info);
1168 ok(hr == S_OK, "Failed to query video memory info, hr %#x.\n", hr);
1169 ok(memory_info.Budget >= memory_info.AvailableForReservation,
1170 "Available for reservation 0x%s is greater than budget 0x%s.\n",
1171 wine_dbgstr_longlong(memory_info.AvailableForReservation),
1172 wine_dbgstr_longlong(memory_info.Budget));
1173 ok(!memory_info.CurrentReservation, "Got unexpected current reservation 0x%s.\n",
1174 wine_dbgstr_longlong(memory_info.CurrentReservation));
1176 hr = IDXGIAdapter3_QueryVideoMemoryInfo(adapter3, 0, DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL + 1, &memory_info);
1177 ok(hr == E_INVALIDARG, "Failed to query video memory info, hr %#x.\n", hr);
1179 IDXGIAdapter3_Release(adapter3);
1181 done:
1182 IDXGIAdapter_Release(adapter);
1183 refcount = IDXGIDevice_Release(device);
1184 ok(!refcount, "Device has %u references left.\n", refcount);
1187 static void test_check_interface_support(void)
1189 LARGE_INTEGER driver_version;
1190 IDXGIAdapter *adapter;
1191 IDXGIDevice *device;
1192 IUnknown *iface;
1193 ULONG refcount;
1194 HRESULT hr;
1196 if (!(device = create_device(0)))
1198 skip("Failed to create device.\n");
1199 return;
1202 hr = IDXGIDevice_GetAdapter(device, &adapter);
1203 ok(SUCCEEDED(hr), "GetAdapter failed, hr %#x.\n", hr);
1205 hr = IDXGIAdapter_CheckInterfaceSupport(adapter, &IID_IDXGIDevice, NULL);
1206 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1207 hr = IDXGIAdapter_CheckInterfaceSupport(adapter, &IID_IDXGIDevice, &driver_version);
1208 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1209 hr = IDXGIAdapter_CheckInterfaceSupport(adapter, &IID_ID3D10Device, NULL);
1210 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1211 hr = IDXGIAdapter_CheckInterfaceSupport(adapter, &IID_ID3D10Device, &driver_version);
1212 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1214 trace("UMD version: %u.%u.%u.%u.\n",
1215 HIWORD(U(driver_version).HighPart), LOWORD(U(driver_version).HighPart),
1216 HIWORD(U(driver_version).LowPart), LOWORD(U(driver_version).LowPart));
1218 hr = IDXGIDevice_QueryInterface(device, &IID_ID3D10Device1, (void **)&iface);
1219 if (SUCCEEDED(hr))
1221 IUnknown_Release(iface);
1222 hr = IDXGIAdapter_CheckInterfaceSupport(adapter, &IID_ID3D10Device1, NULL);
1223 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1224 hr = IDXGIAdapter_CheckInterfaceSupport(adapter, &IID_ID3D10Device1, &driver_version);
1225 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1227 else
1229 win_skip("D3D10.1 is not supported.\n");
1232 hr = IDXGIAdapter_CheckInterfaceSupport(adapter, &IID_ID3D11Device, NULL);
1233 ok(hr == DXGI_ERROR_UNSUPPORTED, "Got unexpected hr %#x.\n", hr);
1234 driver_version.LowPart = driver_version.HighPart = 0xdeadbeef;
1235 hr = IDXGIAdapter_CheckInterfaceSupport(adapter, &IID_ID3D11Device, &driver_version);
1236 ok(hr == DXGI_ERROR_UNSUPPORTED, "Got unexpected hr %#x.\n", hr);
1237 ok(driver_version.HighPart == 0xdeadbeef, "Got unexpected driver version %#x.\n", driver_version.HighPart);
1238 ok(driver_version.LowPart == 0xdeadbeef, "Got unexpected driver version %#x.\n", driver_version.LowPart);
1240 IDXGIAdapter_Release(adapter);
1241 refcount = IDXGIDevice_Release(device);
1242 ok(!refcount, "Device has %u references left.\n", refcount);
1245 static void test_create_surface(void)
1247 ID3D11Texture2D *texture2d;
1248 DXGI_SURFACE_DESC desc;
1249 IDXGISurface *surface;
1250 IDXGIDevice *device;
1251 ULONG refcount;
1252 HRESULT hr;
1254 if (!(device = create_device(0)))
1256 skip("Failed to create device.\n");
1257 return;
1260 desc.Width = 512;
1261 desc.Height = 512;
1262 desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
1263 desc.SampleDesc.Count = 1;
1264 desc.SampleDesc.Quality = 0;
1266 hr = IDXGIDevice_CreateSurface(device, &desc, 1, DXGI_USAGE_RENDER_TARGET_OUTPUT, NULL, &surface);
1267 ok(SUCCEEDED(hr), "Failed to create a dxgi surface, hr %#x\n", hr);
1269 check_interface(surface, &IID_ID3D10Texture2D, TRUE, FALSE);
1270 /* Not available on all Windows versions. */
1271 check_interface(surface, &IID_ID3D11Texture2D, TRUE, TRUE);
1272 /* Not available on all Windows versions. */
1273 check_interface(surface, &IID_IDXGISurface1, TRUE, TRUE);
1275 IDXGISurface_Release(surface);
1276 refcount = IDXGIDevice_Release(device);
1277 ok(!refcount, "Device has %u references left.\n", refcount);
1279 /* DXGI_USAGE_UNORDERED_ACCESS */
1280 if (!(device = create_d3d11_device()))
1282 skip("Failed to create D3D11 device.\n");
1283 return;
1286 surface = NULL;
1287 hr = IDXGIDevice_CreateSurface(device, &desc, 1, DXGI_USAGE_UNORDERED_ACCESS, NULL, &surface);
1288 ok(SUCCEEDED(hr), "Failed to create a dxgi surface, hr %#x\n", hr);
1290 if (surface)
1292 ID3D11UnorderedAccessView *uav;
1293 ID3D11Device *d3d_device;
1295 hr = IDXGISurface_QueryInterface(surface, &IID_ID3D11Texture2D, (void **)&texture2d);
1296 ok(SUCCEEDED(hr), "Failed to get texture interface, hr %#x.\n", hr);
1298 ID3D11Texture2D_GetDevice(texture2d, &d3d_device);
1300 hr = ID3D11Device_CreateUnorderedAccessView(d3d_device, (ID3D11Resource *)texture2d, NULL, &uav);
1301 ok(SUCCEEDED(hr), "Failed to create unordered access view, hr %#x.\n", hr);
1302 ID3D11UnorderedAccessView_Release(uav);
1304 ID3D11Device_Release(d3d_device);
1305 ID3D11Texture2D_Release(texture2d);
1307 IDXGISurface_Release(surface);
1310 refcount = IDXGIDevice_Release(device);
1311 ok(!refcount, "Device has %u references left.\n", refcount);
1314 static void test_parents(void)
1316 DXGI_SURFACE_DESC surface_desc;
1317 IDXGISurface *surface;
1318 IDXGIFactory *factory;
1319 IDXGIAdapter *adapter;
1320 IDXGIDevice *device;
1321 IDXGIOutput *output;
1322 IUnknown *parent;
1323 ULONG refcount;
1324 HRESULT hr;
1326 if (!(device = create_device(0)))
1328 skip("Failed to create device.\n");
1329 return;
1332 surface_desc.Width = 512;
1333 surface_desc.Height = 512;
1334 surface_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
1335 surface_desc.SampleDesc.Count = 1;
1336 surface_desc.SampleDesc.Quality = 0;
1338 hr = IDXGIDevice_CreateSurface(device, &surface_desc, 1, DXGI_USAGE_RENDER_TARGET_OUTPUT, NULL, &surface);
1339 ok(SUCCEEDED(hr), "Failed to create a dxgi surface, hr %#x\n", hr);
1341 hr = IDXGISurface_GetParent(surface, &IID_IDXGIDevice, (void **)&parent);
1342 IDXGISurface_Release(surface);
1343 ok(SUCCEEDED(hr), "GetParent failed, hr %#x.\n", hr);
1344 ok(parent == (IUnknown *)device, "Got parent %p, expected %p.\n", parent, device);
1345 IUnknown_Release(parent);
1347 hr = IDXGIDevice_GetAdapter(device, &adapter);
1348 ok(SUCCEEDED(hr), "GetAdapter failed, hr %#x.\n", hr);
1350 hr = IDXGIAdapter_EnumOutputs(adapter, 0, &output);
1351 if (hr == DXGI_ERROR_NOT_FOUND)
1353 skip("Adapter has not outputs.\n");
1355 else
1357 ok(SUCCEEDED(hr), "EnumOutputs failed, hr %#x.\n", hr);
1359 hr = IDXGIOutput_GetParent(output, &IID_IDXGIAdapter, (void **)&parent);
1360 IDXGIOutput_Release(output);
1361 ok(SUCCEEDED(hr), "GetParent failed, hr %#x.\n", hr);
1362 ok(parent == (IUnknown *)adapter, "Got parent %p, expected %p.\n", parent, adapter);
1363 IUnknown_Release(parent);
1366 hr = IDXGIAdapter_GetParent(adapter, &IID_IDXGIFactory, (void **)&factory);
1367 ok(SUCCEEDED(hr), "GetParent failed, hr %#x.\n", hr);
1369 hr = IDXGIFactory_GetParent(factory, &IID_IUnknown, (void **)&parent);
1370 ok(hr == E_NOINTERFACE, "GetParent returned %#x, expected %#x.\n", hr, E_NOINTERFACE);
1371 ok(parent == NULL, "Got parent %p, expected %p.\n", parent, NULL);
1372 IDXGIFactory_Release(factory);
1374 hr = IDXGIDevice_GetParent(device, &IID_IDXGIAdapter, (void **)&parent);
1375 ok(SUCCEEDED(hr), "GetParent failed, hr %#x.\n", hr);
1376 ok(parent == (IUnknown *)adapter, "Got parent %p, expected %p.\n", parent, adapter);
1377 IUnknown_Release(parent);
1379 IDXGIAdapter_Release(adapter);
1380 refcount = IDXGIDevice_Release(device);
1381 ok(!refcount, "Device has %u references left.\n", refcount);
1384 static void test_output(void)
1386 unsigned int mode_count, mode_count_comp, i, last_height, last_width;
1387 double last_refresh_rate;
1388 IDXGIAdapter *adapter;
1389 IDXGIDevice *device;
1390 HRESULT hr;
1391 IDXGIOutput *output;
1392 ULONG refcount;
1393 DXGI_MODE_DESC *modes;
1395 if (!(device = create_device(0)))
1397 skip("Failed to create device.\n");
1398 return;
1401 hr = IDXGIDevice_GetAdapter(device, &adapter);
1402 ok(SUCCEEDED(hr), "GetAdapter failed, hr %#x.\n", hr);
1404 hr = IDXGIAdapter_EnumOutputs(adapter, 0, NULL);
1405 ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
1407 hr = IDXGIAdapter_EnumOutputs(adapter, 0, &output);
1408 if (hr == DXGI_ERROR_NOT_FOUND)
1410 skip("Adapter doesn't have any outputs.\n");
1411 IDXGIAdapter_Release(adapter);
1412 IDXGIDevice_Release(device);
1413 return;
1415 ok(SUCCEEDED(hr), "EnumOutputs failed, hr %#x.\n", hr);
1417 hr = IDXGIOutput_GetDisplayModeList(output, DXGI_FORMAT_R8G8B8A8_UNORM, 0, NULL, NULL);
1418 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
1420 hr = IDXGIOutput_GetDisplayModeList(output, DXGI_FORMAT_R8G8B8A8_UNORM, 0, &mode_count, NULL);
1421 ok(SUCCEEDED(hr)
1422 || broken(hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE), /* Remote Desktop Services / Win 7 testbot */
1423 "Failed to list modes, hr %#x.\n", hr);
1424 if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
1426 win_skip("GetDisplayModeList() not supported.\n");
1427 IDXGIOutput_Release(output);
1428 IDXGIAdapter_Release(adapter);
1429 IDXGIDevice_Release(device);
1430 return;
1432 mode_count_comp = mode_count;
1434 hr = IDXGIOutput_GetDisplayModeList(output, 0, 0, &mode_count, NULL);
1435 ok(SUCCEEDED(hr), "Failed to list modes, hr %#x.\n", hr);
1436 ok(!mode_count, "Got unexpected mode_count %u.\n", mode_count);
1438 hr = IDXGIOutput_GetDisplayModeList(output, DXGI_FORMAT_R8G8B8A8_UNORM,
1439 DXGI_ENUM_MODES_SCALING, &mode_count, NULL);
1440 ok(SUCCEEDED(hr), "Failed to list modes, hr %#x.\n", hr);
1441 ok(mode_count >= mode_count_comp, "Got unexpected mode_count %u, expected >= %u.\n", mode_count, mode_count_comp);
1442 mode_count_comp = mode_count;
1444 modes = heap_calloc(mode_count + 10, sizeof(*modes));
1445 ok(!!modes, "Failed to allocate memory.\n");
1447 hr = IDXGIOutput_GetDisplayModeList(output, DXGI_FORMAT_R8G8B8A8_UNORM,
1448 DXGI_ENUM_MODES_SCALING, NULL, modes);
1449 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
1450 ok(!modes[0].Height, "No output was expected.\n");
1452 mode_count = 0;
1453 hr = IDXGIOutput_GetDisplayModeList(output, DXGI_FORMAT_R8G8B8A8_UNORM,
1454 DXGI_ENUM_MODES_SCALING, &mode_count, modes);
1455 ok(hr == DXGI_ERROR_MORE_DATA, "Got unexpected hr %#x.\n", hr);
1456 ok(!modes[0].Height, "No output was expected.\n");
1458 mode_count = mode_count_comp;
1459 hr = IDXGIOutput_GetDisplayModeList(output, DXGI_FORMAT_R8G8B8A8_UNORM,
1460 DXGI_ENUM_MODES_SCALING, &mode_count, modes);
1461 ok(SUCCEEDED(hr), "Failed to list modes, hr %#x.\n", hr);
1462 ok(mode_count == mode_count_comp, "Got unexpected mode_count %u, expected %u.\n", mode_count, mode_count_comp);
1464 last_width = last_height = 0;
1465 last_refresh_rate = 0.;
1466 for (i = 0; i < mode_count; i++)
1468 double refresh_rate = modes[i].RefreshRate.Numerator / (double)modes[i].RefreshRate.Denominator;
1470 ok(modes[i].Width && modes[i].Height, "Mode %u: Invalid dimensions %ux%u.\n",
1471 i, modes[i].Width, modes[i].Height);
1473 ok(modes[i].Width >= last_width,
1474 "Mode %u: Modes should have been sorted, width %u < %u.\n", i, modes[i].Width, last_width);
1475 if (modes[i].Width != last_width)
1477 last_width = modes[i].Width;
1478 last_height = 0;
1479 last_refresh_rate = 0.;
1480 continue;
1483 ok(modes[i].Height >= last_height,
1484 "Mode %u: Modes should have been sorted, height %u < %u.\n", i, modes[i].Height, last_height);
1485 if (modes[i].Height != last_height)
1487 last_height = modes[i].Height;
1488 last_refresh_rate = 0.;
1489 continue;
1492 ok(refresh_rate >= last_refresh_rate,
1493 "Mode %u: Modes should have been sorted, refresh rate %f < %f.\n", i, refresh_rate, last_refresh_rate);
1494 if (refresh_rate != last_refresh_rate)
1495 last_refresh_rate = refresh_rate;
1498 mode_count += 5;
1499 hr = IDXGIOutput_GetDisplayModeList(output, DXGI_FORMAT_R8G8B8A8_UNORM,
1500 DXGI_ENUM_MODES_SCALING, &mode_count, modes);
1501 ok(SUCCEEDED(hr), "Failed to list modes, hr %#x.\n", hr);
1502 ok(mode_count == mode_count_comp, "Got unexpected mode_count %u, expected %u.\n", mode_count, mode_count_comp);
1504 if (mode_count_comp)
1506 mode_count = mode_count_comp - 1;
1507 hr = IDXGIOutput_GetDisplayModeList(output, DXGI_FORMAT_R8G8B8A8_UNORM,
1508 DXGI_ENUM_MODES_SCALING, &mode_count, modes);
1509 ok(hr == DXGI_ERROR_MORE_DATA, "Got unexpected hr %#x.\n", hr);
1510 ok(mode_count == mode_count_comp - 1, "Got unexpected mode_count %u, expected %u.\n",
1511 mode_count, mode_count_comp - 1);
1513 else
1515 skip("Not enough modes for test.\n");
1518 heap_free(modes);
1519 IDXGIOutput_Release(output);
1520 IDXGIAdapter_Release(adapter);
1521 refcount = IDXGIDevice_Release(device);
1522 ok(!refcount, "Device has %u references left.\n", refcount);
1525 static void test_find_closest_matching_mode(void)
1527 static const DXGI_MODE_SCALING scaling_tests[] =
1529 DXGI_MODE_SCALING_CENTERED,
1530 DXGI_MODE_SCALING_STRETCHED
1532 DXGI_MODE_DESC *modes, mode, matching_mode;
1533 unsigned int i, j, mode_count;
1534 IDXGIAdapter *adapter;
1535 IDXGIDevice *device;
1536 IDXGIOutput *output;
1537 ULONG refcount;
1538 HRESULT hr;
1540 if (!(device = create_device(0)))
1542 skip("Failed to create device.\n");
1543 return;
1546 hr = IDXGIDevice_GetAdapter(device, &adapter);
1547 ok(SUCCEEDED(hr), "GetAdapter failed, hr %#x.\n", hr);
1549 hr = IDXGIAdapter_EnumOutputs(adapter, 0, &output);
1550 if (hr == DXGI_ERROR_NOT_FOUND)
1552 win_skip("Adapter doesn't have any outputs.\n");
1553 IDXGIAdapter_Release(adapter);
1554 IDXGIDevice_Release(device);
1555 return;
1557 ok(SUCCEEDED(hr), "EnumOutputs failed, hr %#x.\n", hr);
1559 memset(&mode, 0, sizeof(mode));
1560 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, NULL);
1561 ok(hr == DXGI_ERROR_INVALID_CALL || broken(hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE), /* Win 7 testbot */
1562 "Got unexpected hr %#x.\n", hr);
1563 if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
1565 win_skip("FindClosestMatchingMode() not supported.\n");
1566 goto done;
1569 memset(&mode, 0, sizeof(mode));
1570 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, (IUnknown *)device);
1571 todo_wine ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1573 hr = IDXGIOutput_GetDisplayModeList(output, DXGI_FORMAT_R8G8B8A8_UNORM, 0, &mode_count, NULL);
1574 ok(SUCCEEDED(hr), "Failed to list modes, hr %#x.\n", hr);
1576 modes = heap_calloc(mode_count, sizeof(*modes));
1577 ok(!!modes, "Failed to allocate memory.\n");
1579 hr = IDXGIOutput_GetDisplayModeList(output, DXGI_FORMAT_R8G8B8A8_UNORM, 0, &mode_count, modes);
1580 ok(SUCCEEDED(hr), "Failed to list modes, hr %#x.\n", hr);
1582 for (i = 0; i < mode_count; ++i)
1584 mode = modes[i];
1585 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, NULL);
1586 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1587 check_mode_desc(&matching_mode, &modes[i], MODE_DESC_IGNORE_SCALING);
1589 mode.Format = DXGI_FORMAT_UNKNOWN;
1590 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, NULL);
1591 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
1593 mode = modes[i];
1594 mode.Width = 0;
1595 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, NULL);
1596 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
1598 mode = modes[i];
1599 mode.Height = 0;
1600 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, NULL);
1601 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
1603 mode = modes[i];
1604 mode.Width = mode.Height = 0;
1605 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, NULL);
1606 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1607 check_mode_desc(&matching_mode, &modes[i], MODE_DESC_IGNORE_SCALING | MODE_DESC_IGNORE_RESOLUTION);
1608 ok(matching_mode.Width > 0 && matching_mode.Height > 0, "Got unexpected resolution %ux%u.\n",
1609 matching_mode.Width, matching_mode.Height);
1611 mode = modes[i];
1612 mode.RefreshRate.Numerator = mode.RefreshRate.Denominator = 0;
1613 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, NULL);
1614 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1615 check_mode_desc(&matching_mode, &modes[i], MODE_DESC_IGNORE_SCALING | MODE_DESC_IGNORE_REFRESH_RATE);
1616 ok(matching_mode.RefreshRate.Numerator > 0 && matching_mode.RefreshRate.Denominator > 0,
1617 "Got unexpected refresh rate %u / %u.\n",
1618 matching_mode.RefreshRate.Numerator, matching_mode.RefreshRate.Denominator);
1620 mode = modes[i];
1621 mode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
1622 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, NULL);
1623 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1624 check_mode_desc(&matching_mode, &modes[i], MODE_DESC_IGNORE_SCALING | MODE_DESC_IGNORE_SCANLINE_ORDERING);
1625 ok(matching_mode.ScanlineOrdering, "Got unexpected scanline ordering %#x.\n",
1626 matching_mode.ScanlineOrdering);
1628 memset(&mode, 0, sizeof(mode));
1629 mode.Width = modes[i].Width;
1630 mode.Height = modes[i].Height;
1631 mode.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
1632 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, NULL);
1633 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1634 check_mode_desc(&matching_mode, &modes[i], MODE_DESC_CHECK_RESOLUTION & MODE_DESC_CHECK_FORMAT);
1636 memset(&mode, 0, sizeof(mode));
1637 mode.Width = modes[i].Width - 1;
1638 mode.Height = modes[i].Height - 1;
1639 mode.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
1640 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, NULL);
1641 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1642 check_mode_desc(&matching_mode, &modes[i],
1643 (MODE_DESC_CHECK_RESOLUTION & MODE_DESC_CHECK_FORMAT) | MODE_DESC_IGNORE_EXACT_RESOLUTION);
1645 memset(&mode, 0, sizeof(mode));
1646 mode.Width = modes[i].Width + 1;
1647 mode.Height = modes[i].Height + 1;
1648 mode.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
1649 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, NULL);
1650 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1651 check_mode_desc(&matching_mode, &modes[i],
1652 (MODE_DESC_CHECK_RESOLUTION & MODE_DESC_CHECK_FORMAT) | MODE_DESC_IGNORE_EXACT_RESOLUTION);
1655 memset(&mode, 0, sizeof(mode));
1656 mode.Width = mode.Height = 10;
1657 mode.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
1658 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, NULL);
1659 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1660 /* Find mode for the lowest resolution. */
1661 mode = modes[0];
1662 for (i = 0; i < mode_count; ++i)
1664 if (mode.Width >= modes[i].Width && mode.Height >= modes[i].Height)
1665 mode = modes[i];
1667 check_mode_desc(&matching_mode, &mode, MODE_DESC_CHECK_RESOLUTION & MODE_DESC_CHECK_FORMAT);
1669 memset(&mode, 0, sizeof(mode));
1670 mode.Width = modes[0].Width;
1671 mode.Height = modes[0].Height;
1672 mode.Format = modes[0].Format;
1673 mode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST;
1674 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, NULL);
1675 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1676 check_mode_desc(&matching_mode, &modes[0], MODE_DESC_CHECK_RESOLUTION & MODE_DESC_CHECK_FORMAT);
1678 memset(&mode, 0, sizeof(mode));
1679 mode.Width = modes[0].Width;
1680 mode.Height = modes[0].Height;
1681 mode.Format = modes[0].Format;
1682 mode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_LOWER_FIELD_FIRST;
1683 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, NULL);
1684 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1685 check_mode_desc(&matching_mode, &modes[0], MODE_DESC_CHECK_RESOLUTION & MODE_DESC_CHECK_FORMAT);
1687 for (i = 0; i < ARRAY_SIZE(scaling_tests); ++i)
1689 for (j = 0; j < mode_count; ++j)
1691 if (modes[j].Scaling != scaling_tests[i])
1692 continue;
1694 memset(&mode, 0, sizeof(mode));
1695 mode.Width = modes[j].Width;
1696 mode.Height = modes[j].Height;
1697 mode.Format = modes[j].Format;
1698 mode.Scaling = modes[j].Scaling;
1699 hr = IDXGIOutput_FindClosestMatchingMode(output, &mode, &matching_mode, NULL);
1700 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
1701 check_mode_desc(&matching_mode, &modes[j],
1702 MODE_DESC_IGNORE_REFRESH_RATE | MODE_DESC_IGNORE_SCANLINE_ORDERING);
1703 break;
1707 heap_free(modes);
1709 done:
1710 IDXGIOutput_Release(output);
1711 IDXGIAdapter_Release(adapter);
1712 refcount = IDXGIDevice_Release(device);
1713 ok(!refcount, "Device has %u references left.\n", refcount);
1716 struct refresh_rates
1718 UINT numerator;
1719 UINT denominator;
1720 BOOL numerator_should_pass;
1721 BOOL denominator_should_pass;
1724 static void test_create_swapchain(void)
1726 struct swapchain_fullscreen_state initial_state, expected_state;
1727 unsigned int i, expected_width, expected_height;
1728 DXGI_SWAP_CHAIN_DESC creation_desc, result_desc;
1729 DXGI_SWAP_CHAIN_FULLSCREEN_DESC fullscreen_desc;
1730 DXGI_SWAP_CHAIN_DESC1 swapchain_desc;
1731 IDXGIDevice *device, *bgra_device;
1732 ULONG refcount, expected_refcount;
1733 IUnknown *obj, *obj2, *parent;
1734 IDXGISwapChain1 *swapchain1;
1735 RECT *expected_client_rect;
1736 IDXGISwapChain *swapchain;
1737 IDXGISurface1 *surface;
1738 IDXGIAdapter *adapter;
1739 IDXGIFactory *factory;
1740 IDXGIOutput *target;
1741 BOOL fullscreen;
1742 HWND window;
1743 HRESULT hr;
1745 const struct refresh_rates refresh_list[] =
1747 {60, 60, FALSE, FALSE},
1748 {60, 0, TRUE, FALSE},
1749 {60, 1, TRUE, TRUE},
1750 { 0, 60, TRUE, FALSE},
1751 { 0, 0, TRUE, FALSE},
1754 if (!(device = create_device(0)))
1756 skip("Failed to create device.\n");
1757 return;
1760 creation_desc.BufferDesc.Width = 800;
1761 creation_desc.BufferDesc.Height = 600;
1762 creation_desc.BufferDesc.RefreshRate.Numerator = 60;
1763 creation_desc.BufferDesc.RefreshRate.Denominator = 60;
1764 creation_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
1765 creation_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
1766 creation_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
1767 creation_desc.SampleDesc.Count = 1;
1768 creation_desc.SampleDesc.Quality = 0;
1769 creation_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
1770 creation_desc.BufferCount = 1;
1771 creation_desc.OutputWindow = NULL;
1772 creation_desc.Windowed = TRUE;
1773 creation_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
1774 creation_desc.Flags = 0;
1776 hr = IDXGIDevice_QueryInterface(device, &IID_IUnknown, (void **)&obj);
1777 ok(hr == S_OK, "IDXGIDevice does not implement IUnknown.\n");
1779 hr = IDXGIDevice_GetAdapter(device, &adapter);
1780 ok(hr == S_OK, "Failed to get adapter, hr %#x.\n", hr);
1782 hr = IDXGIAdapter_GetParent(adapter, &IID_IDXGIFactory, (void **)&factory);
1783 ok(hr == S_OK, "Failed to get parent, hr %#x.\n", hr);
1785 expected_refcount = get_refcount(adapter);
1786 refcount = get_refcount(factory);
1787 ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
1788 refcount = get_refcount(device);
1789 ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
1791 creation_desc.OutputWindow = NULL;
1792 hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
1793 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
1795 creation_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 0, 0, 0, 0, 0, 0);
1796 memset(&initial_state, 0, sizeof(initial_state));
1797 capture_fullscreen_state(&initial_state.fullscreen_state, creation_desc.OutputWindow);
1799 hr = IDXGIFactory_CreateSwapChain(factory, NULL, &creation_desc, &swapchain);
1800 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
1801 hr = IDXGIFactory_CreateSwapChain(factory, obj, NULL, &swapchain);
1802 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
1803 hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, NULL);
1804 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
1805 hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
1806 ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
1808 refcount = get_refcount(adapter);
1809 ok(refcount >= expected_refcount, "Got refcount %u, expected >= %u.\n", refcount, expected_refcount);
1810 refcount = get_refcount(factory);
1811 todo_wine ok(refcount == 4, "Got unexpected refcount %u.\n", refcount);
1812 refcount = get_refcount(device);
1813 ok(refcount == 3, "Got unexpected refcount %u.\n", refcount);
1815 hr = IDXGISwapChain_GetDesc(swapchain, NULL);
1816 ok(hr == E_INVALIDARG, "GetDesc unexpectedly returned %#x.\n", hr);
1818 hr = IDXGISwapChain_GetParent(swapchain, &IID_IUnknown, (void **)&parent);
1819 ok(hr == S_OK, "Failed to get parent,%#x.\n", hr);
1820 ok(parent == (IUnknown *)factory, "Got unexpected parent interface pointer %p.\n", parent);
1821 refcount = IUnknown_Release(parent);
1822 todo_wine ok(refcount == 4, "Got unexpected refcount %u.\n", refcount);
1824 hr = IDXGISwapChain_GetParent(swapchain, &IID_IDXGIFactory, (void **)&parent);
1825 ok(hr == S_OK, "Failed to get parent,%#x.\n", hr);
1826 ok(parent == (IUnknown *)factory, "Got unexpected parent interface pointer %p.\n", parent);
1827 refcount = IUnknown_Release(parent);
1828 todo_wine ok(refcount == 4, "Got unexpected refcount %u.\n", refcount);
1830 hr = IDXGISwapChain_QueryInterface(swapchain, &IID_IDXGISwapChain1, (void **)&swapchain1);
1831 ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* Not available on all Windows versions. */,
1832 "Failed to query IDXGISwapChain1 interface, hr %#x.\n", hr);
1833 if (SUCCEEDED(hr))
1835 hr = IDXGISwapChain1_GetDesc1(swapchain1, NULL);
1836 ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
1837 hr = IDXGISwapChain1_GetDesc1(swapchain1, &swapchain_desc);
1838 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1839 ok(!swapchain_desc.Stereo, "Got unexpected stereo %#x.\n", swapchain_desc.Stereo);
1840 ok(swapchain_desc.Scaling == DXGI_SCALING_STRETCH,
1841 "Got unexpected scaling %#x.\n", swapchain_desc.Scaling);
1842 ok(swapchain_desc.AlphaMode == DXGI_ALPHA_MODE_IGNORE,
1843 "Got unexpected alpha mode %#x.\n", swapchain_desc.AlphaMode);
1844 hr = IDXGISwapChain1_GetFullscreenDesc(swapchain1, NULL);
1845 ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
1846 hr = IDXGISwapChain1_GetFullscreenDesc(swapchain1, &fullscreen_desc);
1847 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1848 ok(fullscreen_desc.Windowed == creation_desc.Windowed,
1849 "Got unexpected windowed %#x.\n", fullscreen_desc.Windowed);
1850 hr = IDXGISwapChain1_GetHwnd(swapchain1, &window);
1851 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
1852 ok(window == creation_desc.OutputWindow, "Got unexpected window %p.\n", window);
1853 IDXGISwapChain1_Release(swapchain1);
1856 refcount = IDXGISwapChain_Release(swapchain);
1857 ok(!refcount, "Swapchain has %u references left.\n", refcount);
1859 refcount = get_refcount(factory);
1860 ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
1862 for (i = 0; i < ARRAY_SIZE(refresh_list); ++i)
1864 creation_desc.BufferDesc.RefreshRate.Numerator = refresh_list[i].numerator;
1865 creation_desc.BufferDesc.RefreshRate.Denominator = refresh_list[i].denominator;
1867 hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
1868 ok(hr == S_OK, "Test %u: Failed to create swapchain, hr %#x.\n", i, hr);
1870 hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
1871 ok(hr == S_OK, "Test %u: Failed to get swapchain desc, hr %#x.\n", i, hr);
1873 ok(result_desc.Windowed == creation_desc.Windowed, "Test %u: Got unexpected windowed %#x.\n",
1874 i, result_desc.Windowed);
1876 todo_wine_if (!refresh_list[i].numerator_should_pass)
1877 ok(result_desc.BufferDesc.RefreshRate.Numerator == refresh_list[i].numerator,
1878 "Numerator %u is %u.\n", i, result_desc.BufferDesc.RefreshRate.Numerator);
1880 todo_wine_if (!refresh_list[i].denominator_should_pass)
1881 ok(result_desc.BufferDesc.RefreshRate.Denominator == refresh_list[i].denominator,
1882 "Denominator %u is %u.\n", i, result_desc.BufferDesc.RefreshRate.Denominator);
1884 fullscreen = 0xdeadbeef;
1885 target = (void *)0xdeadbeef;
1886 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, &target);
1887 ok(hr == S_OK, "Test %u: Failed to get fullscreen state, hr %#x.\n", i, hr);
1888 ok(!fullscreen, "Test %u: Got unexpected fullscreen %#x.\n", i, fullscreen);
1889 ok(!target, "Test %u: Got unexpected target %p.\n", i, target);
1891 hr = IDXGISwapChain_GetFullscreenState(swapchain, NULL, NULL);
1892 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
1893 fullscreen = 0xdeadbeef;
1894 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
1895 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
1896 ok(!fullscreen, "Test %u: Got unexpected fullscreen %#x.\n", i, fullscreen);
1897 target = (void *)0xdeadbeef;
1898 hr = IDXGISwapChain_GetFullscreenState(swapchain, NULL, &target);
1899 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
1900 ok(!target, "Test %u: Got unexpected target %p.\n", i, target);
1902 check_swapchain_fullscreen_state(swapchain, &initial_state);
1903 IDXGISwapChain_Release(swapchain);
1906 check_window_fullscreen_state(creation_desc.OutputWindow, &initial_state.fullscreen_state);
1908 /* Test GDI-compatible swapchain */
1909 bgra_device = create_device(D3D10_CREATE_DEVICE_BGRA_SUPPORT);
1910 ok(!!bgra_device, "Failed to create BGRA capable device.\n");
1912 hr = IDXGIDevice_QueryInterface(bgra_device, &IID_IUnknown, (void **)&obj2);
1913 ok(hr == S_OK, "IDXGIDevice does not implement IUnknown.\n");
1915 hr = IDXGIFactory_CreateSwapChain(factory, obj2, &creation_desc, &swapchain);
1916 ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
1918 hr = IDXGISwapChain_GetBuffer(swapchain, 0, &IID_IDXGISurface1, (void **)&surface);
1919 if (SUCCEEDED(hr))
1921 HDC hdc;
1923 hr = IDXGISurface1_GetDC(surface, FALSE, &hdc);
1924 ok(FAILED(hr), "Expected GetDC() to fail, %#x\n", hr);
1926 IDXGISurface1_Release(surface);
1927 IDXGISwapChain_Release(swapchain);
1929 creation_desc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
1930 creation_desc.Flags = DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE;
1932 hr = IDXGIFactory_CreateSwapChain(factory, obj2, &creation_desc, &swapchain);
1933 ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
1935 creation_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
1936 creation_desc.Flags = 0;
1938 hr = IDXGISwapChain_GetBuffer(swapchain, 0, &IID_IDXGISurface1, (void **)&surface);
1939 ok(hr == S_OK, "Failed to get front buffer, hr %#x.\n", hr);
1941 hr = IDXGISurface1_GetDC(surface, FALSE, &hdc);
1942 ok(hr == S_OK, "Expected GetDC() to succeed, %#x\n", hr);
1943 IDXGISurface1_ReleaseDC(surface, NULL);
1945 IDXGISurface1_Release(surface);
1946 IDXGISwapChain_Release(swapchain);
1948 else
1950 win_skip("IDXGISurface1 is not supported, skipping GetDC() tests.\n");
1951 IDXGISwapChain_Release(swapchain);
1953 IUnknown_Release(obj2);
1954 IDXGIDevice_Release(bgra_device);
1956 creation_desc.Windowed = FALSE;
1958 for (i = 0; i < ARRAY_SIZE(refresh_list); ++i)
1960 creation_desc.BufferDesc.RefreshRate.Numerator = refresh_list[i].numerator;
1961 creation_desc.BufferDesc.RefreshRate.Denominator = refresh_list[i].denominator;
1963 hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
1964 ok(SUCCEEDED(hr), "Test %u: Failed to create swapchain, hr %#x.\n", i, hr);
1966 hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
1967 ok(hr == S_OK, "Test %u: Failed to get swapchain desc, hr %#x.\n", i, hr);
1969 /* When numerator is non-zero and denominator is zero, the windowed mode is used.
1970 * Additionally, some versions of WARP seem to always fail to change fullscreen state. */
1971 if (result_desc.Windowed != creation_desc.Windowed)
1972 trace("Test %u: Failed to change fullscreen state.\n", i);
1974 todo_wine_if (!refresh_list[i].numerator_should_pass)
1975 ok(result_desc.BufferDesc.RefreshRate.Numerator == refresh_list[i].numerator,
1976 "Numerator %u is %u.\n", i, result_desc.BufferDesc.RefreshRate.Numerator);
1978 todo_wine_if (!refresh_list[i].denominator_should_pass)
1979 ok(result_desc.BufferDesc.RefreshRate.Denominator == refresh_list[i].denominator,
1980 "Denominator %u is %u.\n", i, result_desc.BufferDesc.RefreshRate.Denominator);
1982 fullscreen = FALSE;
1983 target = NULL;
1984 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, &target);
1985 ok(hr == S_OK, "Test %u: Failed to get fullscreen state, hr %#x.\n", i, hr);
1986 ok(fullscreen == !result_desc.Windowed, "Test %u: Got fullscreen %#x, expected %#x.\n",
1987 i, fullscreen, result_desc.Windowed);
1988 ok(result_desc.Windowed ? !target : !!target, "Test %u: Got unexpected target %p.\n", i, target);
1989 if (!result_desc.Windowed)
1991 IDXGIOutput *containing_output;
1992 hr = IDXGISwapChain_GetContainingOutput(swapchain, &containing_output);
1993 ok(hr == S_OK, "Test %u: Failed to get containing output, hr %#x.\n", i, hr);
1994 ok(containing_output == target, "Test %u: Got unexpected containing output pointer %p.\n",
1995 i, containing_output);
1996 IDXGIOutput_Release(containing_output);
1998 ok(output_belongs_to_adapter(target, adapter),
1999 "Test %u: Output %p doesn't belong to adapter %p.\n",
2000 i, target, adapter);
2001 IDXGIOutput_Release(target);
2003 hr = IDXGISwapChain_GetFullscreenState(swapchain, NULL, NULL);
2004 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
2005 fullscreen = FALSE;
2006 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
2007 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
2008 ok(fullscreen, "Test %u: Got unexpected fullscreen %#x.\n", i, fullscreen);
2009 target = NULL;
2010 hr = IDXGISwapChain_GetFullscreenState(swapchain, NULL, &target);
2011 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
2012 ok(!!target, "Test %u: Got unexpected target %p.\n", i, target);
2013 IDXGIOutput_Release(target);
2016 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2017 ok(hr == S_OK, "Test %u: Failed to set fullscreen state, hr %#x.\n", i, hr);
2019 fullscreen = 0xdeadbeef;
2020 target = (void *)0xdeadbeef;
2021 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, &target);
2022 ok(hr == S_OK, "Test %u: Failed to get fullscreen state, hr %#x.\n", i, hr);
2023 ok(!fullscreen, "Test %u: Got unexpected fullscreen %#x.\n", i, fullscreen);
2024 ok(!target, "Test %u: Got unexpected target %p.\n", i, target);
2026 check_swapchain_fullscreen_state(swapchain, &initial_state);
2027 IDXGISwapChain_Release(swapchain);
2030 check_window_fullscreen_state(creation_desc.OutputWindow, &initial_state.fullscreen_state);
2032 /* Test swapchain creation with DXGI_FORMAT_UNKNOWN. */
2033 creation_desc.BufferDesc.Format = DXGI_FORMAT_UNKNOWN;
2034 creation_desc.Windowed = TRUE;
2035 creation_desc.Flags = 0;
2036 hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
2037 ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
2039 creation_desc.Windowed = FALSE;
2040 hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
2041 ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
2043 creation_desc.BufferCount = 2;
2044 creation_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
2045 hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
2046 ok(hr == E_INVALIDARG || hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
2047 creation_desc.BufferCount = 1;
2048 creation_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
2050 check_window_fullscreen_state(creation_desc.OutputWindow, &initial_state.fullscreen_state);
2052 /* Test swapchain creation with backbuffer width and height equal to 0. */
2053 expected_state = initial_state;
2054 expected_client_rect = &expected_state.fullscreen_state.client_rect;
2056 /* Windowed */
2057 expected_width = expected_client_rect->right;
2058 expected_height = expected_client_rect->bottom;
2060 creation_desc.BufferDesc.Width = 0;
2061 creation_desc.BufferDesc.Height = 0;
2062 creation_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
2063 creation_desc.Windowed = TRUE;
2064 creation_desc.Flags = 0;
2065 hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
2066 ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
2067 hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
2068 ok(hr == S_OK, "Failed to get swapchain desc, hr %#x.\n", hr);
2069 ok(result_desc.BufferDesc.Width == expected_width, "Got width %u, expected %u.\n",
2070 result_desc.BufferDesc.Width, expected_width);
2071 ok(result_desc.BufferDesc.Height == expected_height, "Got height %u, expected %u.\n",
2072 result_desc.BufferDesc.Height, expected_height);
2073 check_swapchain_fullscreen_state(swapchain, &expected_state);
2074 IDXGISwapChain_Release(swapchain);
2076 DestroyWindow(creation_desc.OutputWindow);
2077 creation_desc.OutputWindow = CreateWindowA("static", "dxgi_test",
2078 WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
2079 0, 0, 222, 222, 0, 0, 0, 0);
2080 expected_state.fullscreen_state.style = WS_CLIPSIBLINGS | WS_CAPTION
2081 | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
2082 SetRect(&expected_state.fullscreen_state.window_rect, 0, 0, 222, 222);
2083 GetClientRect(creation_desc.OutputWindow, expected_client_rect);
2084 expected_width = expected_client_rect->right;
2085 expected_height = expected_client_rect->bottom;
2087 creation_desc.BufferDesc.Width = 0;
2088 creation_desc.BufferDesc.Height = 0;
2089 hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
2090 ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
2091 hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
2092 ok(hr == S_OK, "Failed to get swapchain desc, hr %#x.\n", hr);
2093 ok(result_desc.BufferDesc.Width == expected_width, "Got width %u, expected %u.\n",
2094 result_desc.BufferDesc.Width, expected_width);
2095 ok(result_desc.BufferDesc.Height == expected_height, "Got height %u, expected %u.\n",
2096 result_desc.BufferDesc.Height, expected_height);
2097 check_swapchain_fullscreen_state(swapchain, &expected_state);
2098 IDXGISwapChain_Release(swapchain);
2100 DestroyWindow(creation_desc.OutputWindow);
2101 creation_desc.OutputWindow = CreateWindowA("static", "dxgi_test",
2102 WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
2103 1, 1, 0, 0, 0, 0, 0, 0);
2104 expected_state.fullscreen_state.style = WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
2105 expected_state.fullscreen_state.exstyle = 0;
2106 SetRect(&expected_state.fullscreen_state.window_rect, 1, 1, 1, 1);
2107 SetRectEmpty(expected_client_rect);
2108 expected_width = expected_height = 8;
2110 creation_desc.BufferDesc.Width = 0;
2111 creation_desc.BufferDesc.Height = 0;
2112 hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
2113 ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
2114 hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
2115 ok(hr == S_OK, "Failed to get swapchain desc, hr %#x.\n", hr);
2116 ok(result_desc.BufferDesc.Width == expected_width, "Got width %u, expected %u.\n",
2117 result_desc.BufferDesc.Width, expected_width);
2118 ok(result_desc.BufferDesc.Height == expected_height, "Got height %u, expected %u.\n",
2119 result_desc.BufferDesc.Height, expected_height);
2120 check_swapchain_fullscreen_state(swapchain, &expected_state);
2121 IDXGISwapChain_Release(swapchain);
2123 DestroyWindow(creation_desc.OutputWindow);
2124 creation_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 0, 0, 0, 0, 0, 0);
2125 check_window_fullscreen_state(creation_desc.OutputWindow, &initial_state.fullscreen_state);
2127 /* Fullscreen */
2128 creation_desc.Windowed = FALSE;
2129 hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
2130 ok(SUCCEEDED(hr), "Failed to create swapchain, hr %#x.\n", hr);
2131 hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
2132 ok(hr == S_OK, "Failed to get swapchain desc, hr %#x.\n", hr);
2133 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2134 ok(hr == S_OK, "Failed to set fullscreen state, hr %#x.\n", hr);
2135 hr = IDXGISwapChain_GetContainingOutput(swapchain, &expected_state.target);
2136 ok(hr == S_OK || broken(hr == DXGI_ERROR_UNSUPPORTED) /* Win 7 testbot */,
2137 "Failed to get containing output, hr %#x.\n", hr);
2138 check_swapchain_fullscreen_state(swapchain, &initial_state);
2139 IDXGISwapChain_Release(swapchain);
2140 if (hr == DXGI_ERROR_UNSUPPORTED)
2142 win_skip("GetContainingOutput() not supported.\n");
2143 goto done;
2145 if (result_desc.Windowed)
2147 win_skip("Fullscreen not supported.\n");
2148 IDXGIOutput_Release(expected_state.target);
2149 goto done;
2152 creation_desc.BufferDesc.Width = 0;
2153 creation_desc.BufferDesc.Height = 0;
2154 creation_desc.Windowed = FALSE;
2155 creation_desc.Flags = 0;
2156 compute_expected_swapchain_fullscreen_state_after_fullscreen_change(&expected_state,
2157 &creation_desc, &initial_state.fullscreen_state.monitor_rect, 0, 0, expected_state.target);
2158 expected_width = expected_client_rect->right - expected_client_rect->left;
2159 expected_height = expected_client_rect->bottom - expected_client_rect->top;
2161 hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
2162 ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
2163 hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
2164 ok(hr == S_OK, "Failed to get swapchain desc, hr %#x.\n", hr);
2165 todo_wine ok(result_desc.BufferDesc.Width == expected_width, "Got width %u, expected %u.\n",
2166 result_desc.BufferDesc.Width, expected_width);
2167 todo_wine ok(result_desc.BufferDesc.Height == expected_height, "Got height %u, expected %u.\n",
2168 result_desc.BufferDesc.Height, expected_height);
2169 check_swapchain_fullscreen_state(swapchain, &expected_state);
2170 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2171 ok(hr == S_OK, "Failed to set fullscreen state, hr %#x.\n", hr);
2172 check_swapchain_fullscreen_state(swapchain, &initial_state);
2173 IDXGISwapChain_Release(swapchain);
2175 /* Fullscreen and DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH */
2176 creation_desc.BufferDesc.Width = 0;
2177 creation_desc.BufferDesc.Height = 0;
2178 creation_desc.Windowed = FALSE;
2179 creation_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
2180 compute_expected_swapchain_fullscreen_state_after_fullscreen_change(&expected_state,
2181 &creation_desc, &initial_state.fullscreen_state.monitor_rect, 0, 0, expected_state.target);
2182 expected_width = expected_client_rect->right - expected_client_rect->left;
2183 expected_height = expected_client_rect->bottom - expected_client_rect->top;
2185 hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
2186 ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
2187 hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
2188 ok(hr == S_OK, "Failed to get swapchain desc, hr %#x.\n", hr);
2189 todo_wine ok(result_desc.BufferDesc.Width == expected_width, "Got width %u, expected %u.\n",
2190 result_desc.BufferDesc.Width, expected_width);
2191 todo_wine ok(result_desc.BufferDesc.Height == expected_height, "Got height %u, expected %u.\n",
2192 result_desc.BufferDesc.Height, expected_height);
2193 check_swapchain_fullscreen_state(swapchain, &expected_state);
2194 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2195 ok(hr == S_OK, "Failed to set fullscreen state, hr %#x.\n", hr);
2196 check_swapchain_fullscreen_state(swapchain, &initial_state);
2197 IDXGISwapChain_Release(swapchain);
2199 IDXGIOutput_Release(expected_state.target);
2201 done:
2202 IUnknown_Release(obj);
2203 refcount = IDXGIDevice_Release(device);
2204 ok(!refcount, "Device has %u references left.\n", refcount);
2205 refcount = IDXGIAdapter_Release(adapter);
2206 ok(!refcount, "Adapter has %u references left.\n", refcount);
2207 refcount = IDXGIFactory_Release(factory);
2208 ok(!refcount, "Factory has %u references left.\n", refcount);
2209 check_window_fullscreen_state(creation_desc.OutputWindow, &initial_state.fullscreen_state);
2210 DestroyWindow(creation_desc.OutputWindow);
2213 static void test_get_containing_output(IUnknown *device, BOOL is_d3d12)
2215 unsigned int adapter_idx, output_idx, output_count;
2216 DXGI_OUTPUT_DESC output_desc, output_desc2;
2217 DXGI_SWAP_CHAIN_DESC swapchain_desc;
2218 IDXGIOutput *output, *output2;
2219 MONITORINFOEXW monitor_info;
2220 IDXGISwapChain *swapchain;
2221 IDXGIFactory *factory;
2222 IDXGIAdapter *adapter;
2223 POINT points[4 * 16];
2224 unsigned int i, j;
2225 HMONITOR monitor;
2226 BOOL fullscreen;
2227 ULONG refcount;
2228 HRESULT hr;
2229 BOOL ret;
2231 adapter = get_adapter(device, is_d3d12);
2232 if (!adapter)
2234 skip("Failed to get adapter on Direct3D %d.\n", is_d3d12 ? 12 : 10);
2235 return;
2238 output_count = 0;
2239 while ((hr = IDXGIAdapter_EnumOutputs(adapter, output_count, &output)) != DXGI_ERROR_NOT_FOUND)
2241 ok(SUCCEEDED(hr), "Failed to enumerate output %u, hr %#x.\n", output_count, hr);
2242 IDXGIOutput_Release(output);
2243 ++output_count;
2245 IDXGIAdapter_Release(adapter);
2247 swapchain_desc.BufferDesc.Width = 100;
2248 swapchain_desc.BufferDesc.Height = 100;
2249 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
2250 swapchain_desc.BufferDesc.RefreshRate.Denominator = 60;
2251 swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
2252 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
2253 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
2254 swapchain_desc.SampleDesc.Count = 1;
2255 swapchain_desc.SampleDesc.Quality = 0;
2256 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
2257 swapchain_desc.BufferCount = is_d3d12 ? 2 : 1;
2258 swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test",
2259 WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, 100, 100, 0, 0, 0, 0);
2260 swapchain_desc.Windowed = TRUE;
2261 swapchain_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
2262 swapchain_desc.Flags = 0;
2264 get_factory(device, is_d3d12, &factory);
2265 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
2266 ok(SUCCEEDED(hr), "CreateSwapChain failed, hr %#x.\n", hr);
2268 monitor = MonitorFromWindow(swapchain_desc.OutputWindow, 0);
2269 ok(!!monitor, "MonitorFromWindow failed.\n");
2271 monitor_info.cbSize = sizeof(monitor_info);
2272 ret = GetMonitorInfoW(monitor, (MONITORINFO *)&monitor_info);
2273 ok(ret, "Failed to get monitor info.\n");
2275 hr = IDXGISwapChain_GetContainingOutput(swapchain, &output);
2276 ok(SUCCEEDED(hr) || broken(hr == DXGI_ERROR_UNSUPPORTED) /* Win 7 testbot */,
2277 "GetContainingOutput failed, hr %#x.\n", hr);
2278 if (hr == DXGI_ERROR_UNSUPPORTED)
2280 win_skip("GetContainingOutput() not supported.\n");
2281 goto done;
2284 hr = IDXGIOutput_GetDesc(output, &output_desc);
2285 ok(SUCCEEDED(hr), "GetDesc failed, hr %#x.\n", hr);
2287 hr = IDXGISwapChain_GetContainingOutput(swapchain, &output2);
2288 ok(SUCCEEDED(hr), "GetContainingOutput failed, hr %#x.\n", hr);
2289 ok(output != output2, "Got unexpected output pointers %p, %p.\n", output, output2);
2290 check_output_equal(output, output2);
2292 refcount = IDXGIOutput_Release(output);
2293 ok(!refcount, "IDXGIOutput has %u references left.\n", refcount);
2294 refcount = IDXGIOutput_Release(output2);
2295 ok(!refcount, "IDXGIOutput has %u references left.\n", refcount);
2297 ok(!lstrcmpW(output_desc.DeviceName, monitor_info.szDevice),
2298 "Got unexpected device name %s, expected %s.\n",
2299 wine_dbgstr_w(output_desc.DeviceName), wine_dbgstr_w(monitor_info.szDevice));
2300 ok(EqualRect(&output_desc.DesktopCoordinates, &monitor_info.rcMonitor),
2301 "Got unexpected desktop coordinates %s, expected %s.\n",
2302 wine_dbgstr_rect(&output_desc.DesktopCoordinates),
2303 wine_dbgstr_rect(&monitor_info.rcMonitor));
2305 for (adapter_idx = 0; SUCCEEDED(IDXGIFactory_EnumAdapters(factory, adapter_idx, &adapter));
2306 ++adapter_idx)
2308 for (output_idx = 0; SUCCEEDED(IDXGIAdapter_EnumOutputs(adapter, output_idx, &output));
2309 ++output_idx)
2311 hr = IDXGIOutput_GetDesc(output, &output_desc);
2312 ok(SUCCEEDED(hr), "Adapter %u output %u: GetDesc failed, hr %#x.\n", adapter_idx,
2313 output_idx, hr);
2315 /* Move the OutputWindow to the current output. */
2316 ret = SetWindowPos(swapchain_desc.OutputWindow, 0, output_desc.DesktopCoordinates.left,
2317 output_desc.DesktopCoordinates.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
2318 ok(ret, "Adapter %u output %u: SetWindowPos failed.\n", adapter_idx, output_idx);
2320 hr = IDXGISwapChain_GetContainingOutput(swapchain, &output2);
2321 if (FAILED(hr))
2323 win_skip("Adapter %u output %u: GetContainingOutput failed, hr %#x.\n",
2324 adapter_idx, output_idx, hr);
2325 IDXGIOutput_Release(output);
2326 continue;
2329 check_output_equal(output, output2);
2331 refcount = IDXGIOutput_Release(output2);
2332 ok(!refcount, "Adapter %u output %u: IDXGIOutput has %u references left.\n",
2333 adapter_idx, output_idx, refcount);
2335 /* Move the OutputWindow around the corners of the current output desktop coordinates. */
2336 for (i = 0; i < 4; ++i)
2338 static const POINT offsets[] =
2340 { 0, 0},
2341 {-49, 0}, {-50, 0}, {-51, 0},
2342 { 0, -49}, { 0, -50}, { 0, -51},
2343 {-49, -49}, {-50, -49}, {-51, -49},
2344 {-49, -50}, {-50, -50}, {-51, -50},
2345 {-49, -51}, {-50, -51}, {-51, -51},
2347 unsigned int x = 0, y = 0;
2349 switch (i)
2351 case 0:
2352 x = output_desc.DesktopCoordinates.left;
2353 y = output_desc.DesktopCoordinates.top;
2354 break;
2355 case 1:
2356 x = output_desc.DesktopCoordinates.right;
2357 y = output_desc.DesktopCoordinates.top;
2358 break;
2359 case 2:
2360 x = output_desc.DesktopCoordinates.right;
2361 y = output_desc.DesktopCoordinates.bottom;
2362 break;
2363 case 3:
2364 x = output_desc.DesktopCoordinates.left;
2365 y = output_desc.DesktopCoordinates.bottom;
2366 break;
2369 for (j = 0; j < ARRAY_SIZE(offsets); ++j)
2371 unsigned int idx = ARRAY_SIZE(offsets) * i + j;
2372 assert(idx < ARRAY_SIZE(points));
2373 points[idx].x = x + offsets[j].x;
2374 points[idx].y = y + offsets[j].y;
2378 for (i = 0; i < ARRAY_SIZE(points); ++i)
2380 ret = SetWindowPos(swapchain_desc.OutputWindow, 0, points[i].x, points[i].y,
2381 0, 0, SWP_NOSIZE | SWP_NOZORDER);
2382 ok(ret, "Adapter %u output %u point %u: Failed to set window position.\n",
2383 adapter_idx, output_idx, i);
2385 monitor = MonitorFromWindow(swapchain_desc.OutputWindow, MONITOR_DEFAULTTONEAREST);
2386 ok(!!monitor, "Adapter %u output %u point %u: Failed to get monitor from window.\n",
2387 adapter_idx, output_idx, i);
2389 monitor_info.cbSize = sizeof(monitor_info);
2390 ret = GetMonitorInfoW(monitor, (MONITORINFO *)&monitor_info);
2391 ok(ret, "Adapter %u output %u point %u: Failed to get monitor info.\n", adapter_idx,
2392 output_idx, i);
2394 hr = IDXGISwapChain_GetContainingOutput(swapchain, &output2);
2395 ok(hr == S_OK || broken(hr == DXGI_ERROR_UNSUPPORTED),
2396 "Adapter %u output %u point %u: Failed to get containing output, hr %#x.\n",
2397 adapter_idx, output_idx, i, hr);
2398 if (hr != S_OK)
2399 continue;
2400 ok(!!output2, "Adapter %u output %u point %u: Got unexpected containing output %p.\n",
2401 adapter_idx, output_idx, i, output2);
2402 hr = IDXGIOutput_GetDesc(output2, &output_desc);
2403 ok(hr == S_OK, "Adapter %u output %u point %u: Failed to get output desc, hr %#x.\n",
2404 adapter_idx, output_idx, i, hr);
2405 refcount = IDXGIOutput_Release(output2);
2406 ok(!refcount, "Adapter %u output %u point %u: IDXGIOutput has %u references left.\n",
2407 adapter_idx, output_idx, i, refcount);
2409 ok(!lstrcmpW(output_desc.DeviceName, monitor_info.szDevice),
2410 "Adapter %u output %u point %u: Got unexpected device name %s, expected %s.\n",
2411 adapter_idx, output_idx, i, wine_dbgstr_w(output_desc.DeviceName),
2412 wine_dbgstr_w(monitor_info.szDevice));
2413 ok(EqualRect(&output_desc.DesktopCoordinates, &monitor_info.rcMonitor),
2414 "Adapter %u output %u point %u: Expect desktop coordinates %s, got %s.\n",
2415 adapter_idx, output_idx, i,
2416 wine_dbgstr_rect(&output_desc.DesktopCoordinates),
2417 wine_dbgstr_rect(&monitor_info.rcMonitor));
2419 IDXGIOutput_Release(output);
2421 IDXGIAdapter_Release(adapter);
2424 /* Test GetContainingOutput with a full screen swapchain. The containing output should stay
2425 * the same even if the device window is moved */
2426 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
2427 if (FAILED(hr))
2429 skip("SetFullscreenState failed, hr %#x.\n", hr);
2430 goto done;
2433 hr = IDXGISwapChain_GetContainingOutput(swapchain, &output2);
2434 if (FAILED(hr))
2436 win_skip("GetContainingOutput failed, hr %#x.\n", hr);
2437 IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2438 goto done;
2441 for (adapter_idx = 0; SUCCEEDED(IDXGIFactory_EnumAdapters(factory, adapter_idx, &adapter));
2442 ++adapter_idx)
2444 for (output_idx = 0; SUCCEEDED(IDXGIAdapter_EnumOutputs(adapter, output_idx, &output));
2445 ++output_idx)
2447 hr = IDXGIOutput_GetDesc(output, &output_desc);
2448 ok(hr == S_OK, "Adapter %u output %u: GetDesc failed, hr %#x.\n", adapter_idx,
2449 output_idx, hr);
2450 IDXGIOutput_Release(output);
2452 /* Move the OutputWindow to the current output. */
2453 ret = SetWindowPos(swapchain_desc.OutputWindow, 0, output_desc.DesktopCoordinates.left,
2454 output_desc.DesktopCoordinates.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
2455 ok(ret, "Adapter %u output %u: SetWindowPos failed.\n", adapter_idx, output_idx);
2457 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, &output);
2458 ok(hr == S_OK, "Adapter %u output %u: GetFullscreenState failed, hr %#x.\n",
2459 adapter_idx, output_idx, hr);
2460 ok(fullscreen, "Adapter %u output %u: Expect swapchain full screen.\n", adapter_idx,
2461 output_idx);
2462 ok(output == output2, "Adapter %u output %u: Expect output %p, got %p.\n",
2463 adapter_idx, output_idx, output2, output);
2464 IDXGIOutput_Release(output);
2466 hr = IDXGISwapChain_GetContainingOutput(swapchain, &output);
2467 ok(hr == S_OK, "Adapter %u output %u: GetContainingOutput failed, hr %#x.\n",
2468 adapter_idx, output_idx, hr);
2469 ok(output == output2, "Adapter %u output %u: Expect output %p, got %p.\n",
2470 adapter_idx, output_idx, output2, output);
2471 IDXGIOutput_Release(output);
2473 IDXGIAdapter_Release(adapter);
2476 IDXGIOutput_Release(output2);
2477 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2478 ok(hr == S_OK, "SetFullscreenState failed, hr %#x.\n", hr);
2480 /* Test GetContainingOutput after a full screen swapchain is made windowed by pressing
2481 * Alt+Enter, then move it to another output and use Alt+Enter to enter full screen */
2482 output = NULL;
2483 output2 = NULL;
2484 for (adapter_idx = 0; SUCCEEDED(IDXGIFactory_EnumAdapters(factory, adapter_idx, &adapter));
2485 ++adapter_idx)
2487 for (output_idx = 0; SUCCEEDED(IDXGIAdapter_EnumOutputs(adapter, output_idx,
2488 output ? &output2 : &output)); ++output_idx)
2490 if (output2)
2491 break;
2494 IDXGIAdapter_Release(adapter);
2495 if (output2)
2496 break;
2499 if (output && output2)
2501 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, output);
2502 IDXGIOutput_Release(output);
2503 if (FAILED(hr))
2505 skip("SetFullscreenState failed, hr %#x.\n", hr);
2506 IDXGIOutput_Release(output2);
2507 goto done;
2510 /* Post an Alt + VK_RETURN WM_SYSKEYDOWN to leave full screen on the first output */
2511 PostMessageA(swapchain_desc.OutputWindow, WM_SYSKEYDOWN, VK_RETURN,
2512 (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x20000001);
2513 flush_events();
2514 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
2515 ok(hr == S_OK, "GetFullscreenState failed, hr %#x.\n", hr);
2516 ok(!fullscreen, "Expect swapchain not full screen.\n");
2518 /* Move the swapchain output window to the second output */
2519 hr = IDXGIOutput_GetDesc(output2, &output_desc2);
2520 ok(hr == S_OK, "GetDesc failed, hr %#x.\n", hr);
2521 ret = SetWindowPos(swapchain_desc.OutputWindow, 0, output_desc2.DesktopCoordinates.left,
2522 output_desc2.DesktopCoordinates.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
2523 ok(ret, "SetWindowPos failed.\n");
2525 /* Post an Alt + VK_RETURN WM_SYSKEYDOWN to enter full screen on the second output */
2526 PostMessageA(swapchain_desc.OutputWindow, WM_SYSKEYDOWN, VK_RETURN,
2527 (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x20000001);
2528 flush_events();
2529 output = NULL;
2530 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, &output);
2531 ok(hr == S_OK, "GetFullscreenState failed, hr %#x.\n", hr);
2532 ok(fullscreen, "Expect swapchain full screen.\n");
2533 ok(!!output, "Expect output not NULL.\n");
2534 hr = IDXGIOutput_GetDesc(output, &output_desc);
2535 ok(hr == S_OK, "GetDesc failed, hr %#x.\n", hr);
2536 hr = IDXGIOutput_GetDesc(output2, &output_desc2);
2537 ok(hr == S_OK, "GetDesc failed, hr %#x.\n", hr);
2538 ok(!lstrcmpW(output_desc.DeviceName, output_desc2.DeviceName),
2539 "Expect device name %s, got %s.\n", wine_dbgstr_w(output_desc2.DeviceName),
2540 wine_dbgstr_w(output_desc.DeviceName));
2541 IDXGIOutput_Release(output);
2543 output = NULL;
2544 hr = IDXGISwapChain_GetContainingOutput(swapchain, &output);
2545 ok(hr == S_OK, "GetContainingOutput failed, hr %#x.\n", hr);
2546 hr = IDXGIOutput_GetDesc(output, &output_desc);
2547 ok(hr == S_OK, "GetDesc failed, hr %#x.\n", hr);
2548 hr = IDXGIOutput_GetDesc(output2, &output_desc2);
2549 ok(hr == S_OK, "GetDesc failed, hr %#x.\n", hr);
2550 ok(!lstrcmpW(output_desc.DeviceName, output_desc2.DeviceName),
2551 "Expect device name %s, got %s.\n", wine_dbgstr_w(output_desc2.DeviceName),
2552 wine_dbgstr_w(output_desc.DeviceName));
2554 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2555 ok(hr == S_OK, "SetFullscreenState failed, hr %#x.\n", hr);
2557 else
2559 skip("This test requires two outputs.\n");
2562 if (output)
2563 IDXGIOutput_Release(output);
2564 if (output2)
2565 IDXGIOutput_Release(output2);
2567 done:
2568 refcount = IDXGISwapChain_Release(swapchain);
2569 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
2570 refcount = IDXGIFactory_Release(factory);
2571 ok(refcount == !is_d3d12, "IDXGIFactory has %u references left.\n", refcount);
2572 DestroyWindow(swapchain_desc.OutputWindow);
2575 static void test_swapchain_fullscreen_state(IDXGISwapChain *swapchain,
2576 IDXGIAdapter *adapter, const struct swapchain_fullscreen_state *initial_state)
2578 MONITORINFOEXW monitor_info, *output_monitor_info;
2579 struct swapchain_fullscreen_state expected_state;
2580 DXGI_SWAP_CHAIN_DESC swapchain_desc;
2581 DXGI_OUTPUT_DESC output_desc;
2582 unsigned int i, output_count;
2583 IDXGIOutput *output;
2584 HRESULT hr;
2585 BOOL ret;
2587 hr = IDXGISwapChain_GetDesc(swapchain, &swapchain_desc);
2588 ok(SUCCEEDED(hr), "GetDesc failed, hr %#x.\n", hr);
2590 check_swapchain_fullscreen_state(swapchain, initial_state);
2592 expected_state = *initial_state;
2593 compute_expected_swapchain_fullscreen_state_after_fullscreen_change(&expected_state,
2594 &swapchain_desc, &initial_state->fullscreen_state.monitor_rect, 800, 600, NULL);
2595 hr = IDXGISwapChain_GetContainingOutput(swapchain, &expected_state.target);
2596 ok(SUCCEEDED(hr), "GetContainingOutput failed, hr %#x.\n", hr);
2598 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
2599 ok(hr == S_OK || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE, "Got unexpected hr %#x.\n", hr);
2600 if (FAILED(hr))
2602 skip("Could not change fullscreen state.\n");
2603 IDXGIOutput_Release(expected_state.target);
2604 return;
2606 check_swapchain_fullscreen_state(swapchain, &expected_state);
2608 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
2609 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2610 check_swapchain_fullscreen_state(swapchain, &expected_state);
2612 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2613 ok(SUCCEEDED(hr), "SetFullscreenState failed, hr %#x.\n", hr);
2614 check_swapchain_fullscreen_state(swapchain, initial_state);
2616 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2617 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2618 check_swapchain_fullscreen_state(swapchain, initial_state);
2620 IDXGIOutput_Release(expected_state.target);
2621 expected_state.target = NULL;
2623 output_count = 0;
2624 while (IDXGIAdapter_EnumOutputs(adapter, output_count, &output) != DXGI_ERROR_NOT_FOUND)
2626 IDXGIOutput_Release(output);
2627 ++output_count;
2630 output_monitor_info = heap_calloc(output_count, sizeof(*output_monitor_info));
2631 ok(!!output_monitor_info, "Failed to allocate memory.\n");
2632 for (i = 0; i < output_count; ++i)
2634 hr = IDXGIAdapter_EnumOutputs(adapter, i, &output);
2635 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2637 hr = IDXGIOutput_GetDesc(output, &output_desc);
2638 ok(SUCCEEDED(hr), "GetDesc failed, hr %#x.\n", hr);
2640 output_monitor_info[i].cbSize = sizeof(*output_monitor_info);
2641 ret = GetMonitorInfoW(output_desc.Monitor, (MONITORINFO *)&output_monitor_info[i]);
2642 ok(ret, "Failed to get monitor info.\n");
2644 IDXGIOutput_Release(output);
2647 for (i = 0; i < output_count; ++i)
2649 RECT orig_monitor_rect = output_monitor_info[i].rcMonitor;
2650 IDXGIOutput *target;
2651 BOOL fullscreen;
2653 hr = IDXGIAdapter_EnumOutputs(adapter, i, &output);
2654 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2655 hr = IDXGIOutput_GetDesc(output, &output_desc);
2656 ok(SUCCEEDED(hr), "GetDesc failed, hr %#x.\n", hr);
2658 expected_state = *initial_state;
2659 expected_state.target = output;
2660 expected_state.fullscreen_state.monitor = output_desc.Monitor;
2661 expected_state.fullscreen_state.monitor_rect = orig_monitor_rect;
2662 compute_expected_swapchain_fullscreen_state_after_fullscreen_change(&expected_state,
2663 &swapchain_desc, &orig_monitor_rect, 800, 600, NULL);
2665 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, output);
2666 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2667 check_swapchain_fullscreen_state(swapchain, &expected_state);
2669 target = NULL;
2670 hr = IDXGISwapChain_GetFullscreenState(swapchain, NULL, &target);
2671 ok(SUCCEEDED(hr), "GetFullscreenState failed, hr %#x.\n", hr);
2672 ok(target == output, "Got target pointer %p, expected %p.\n", target, output);
2673 IDXGIOutput_Release(target);
2674 fullscreen = FALSE;
2675 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
2676 ok(SUCCEEDED(hr), "GetFullscreenState failed, hr %#x.\n", hr);
2677 ok(fullscreen, "Got unexpected fullscreen %#x.\n", hr);
2679 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, output);
2680 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2681 check_swapchain_fullscreen_state(swapchain, &expected_state);
2682 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, output);
2683 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
2684 check_swapchain_fullscreen_state(swapchain, &expected_state);
2685 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2686 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2687 check_swapchain_fullscreen_state(swapchain, initial_state);
2689 fullscreen = TRUE;
2690 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
2691 ok(SUCCEEDED(hr), "GetFullscreenState failed, hr %#x.\n", hr);
2692 ok(!fullscreen, "Got unexpected fullscreen %#x.\n", hr);
2694 check_swapchain_fullscreen_state(swapchain, initial_state);
2695 monitor_info.cbSize = sizeof(monitor_info);
2696 ret = GetMonitorInfoW(output_desc.Monitor, (MONITORINFO *)&monitor_info);
2697 ok(ret, "Failed to get monitor info.\n");
2698 ok(EqualRect(&monitor_info.rcMonitor, &orig_monitor_rect), "Got monitor rect %s, expected %s.\n",
2699 wine_dbgstr_rect(&monitor_info.rcMonitor), wine_dbgstr_rect(&orig_monitor_rect));
2701 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, output);
2702 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2704 IDXGIOutput_Release(output);
2707 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2708 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2709 check_swapchain_fullscreen_state(swapchain, initial_state);
2711 for (i = 0; i < output_count; ++i)
2713 hr = IDXGIAdapter_EnumOutputs(adapter, i, &output);
2714 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2716 hr = IDXGIOutput_GetDesc(output, &output_desc);
2717 ok(SUCCEEDED(hr), "GetDesc failed, hr %#x.\n", hr);
2719 monitor_info.cbSize = sizeof(monitor_info);
2720 ret = GetMonitorInfoW(output_desc.Monitor, (MONITORINFO *)&monitor_info);
2721 ok(ret, "Failed to get monitor info.\n");
2723 ok(EqualRect(&monitor_info.rcMonitor, &output_monitor_info[i].rcMonitor),
2724 "Got monitor rect %s, expected %s.\n",
2725 wine_dbgstr_rect(&monitor_info.rcMonitor),
2726 wine_dbgstr_rect(&output_monitor_info[i].rcMonitor));
2728 IDXGIOutput_Release(output);
2731 heap_free(output_monitor_info);
2734 static void test_set_fullscreen(IUnknown *device, BOOL is_d3d12)
2736 struct swapchain_fullscreen_state initial_state;
2737 DXGI_SWAP_CHAIN_DESC swapchain_desc;
2738 IDXGIAdapter *adapter = NULL;
2739 IDXGISwapChain *swapchain;
2740 IDXGIFactory *factory;
2741 IDXGIOutput *output;
2742 BOOL fullscreen;
2743 ULONG refcount;
2744 HRESULT hr;
2746 get_factory(device, is_d3d12, &factory);
2748 swapchain_desc.BufferDesc.Width = 800;
2749 swapchain_desc.BufferDesc.Height = 600;
2750 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
2751 swapchain_desc.BufferDesc.RefreshRate.Denominator = 60;
2752 swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
2753 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
2754 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
2755 swapchain_desc.SampleDesc.Count = 1;
2756 swapchain_desc.SampleDesc.Quality = 0;
2757 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
2758 swapchain_desc.BufferCount = is_d3d12 ? 2 : 1;
2759 swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0);
2760 swapchain_desc.Windowed = TRUE;
2761 swapchain_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
2762 swapchain_desc.Flags = 0;
2764 memset(&initial_state, 0, sizeof(initial_state));
2765 capture_fullscreen_state(&initial_state.fullscreen_state, swapchain_desc.OutputWindow);
2766 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
2767 ok(SUCCEEDED(hr), "CreateSwapChain failed, hr %#x.\n", hr);
2768 hr = IDXGISwapChain_GetContainingOutput(swapchain, &output);
2769 ok(SUCCEEDED(hr) || broken(hr == DXGI_ERROR_UNSUPPORTED), /* Win 7 testbot */
2770 "Failed to get containing output, hr %#x.\n", hr);
2771 if (FAILED(hr))
2773 skip("Could not get output.\n");
2774 goto done;
2776 hr = IDXGIOutput_GetParent(output, &IID_IDXGIAdapter, (void **)&adapter);
2777 ok(hr == S_OK, "Failed to get parent, hr %#x.\n", hr);
2778 IDXGIOutput_Release(output);
2780 check_swapchain_fullscreen_state(swapchain, &initial_state);
2781 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
2782 ok(SUCCEEDED(hr) || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE
2783 || broken(hr == DXGI_ERROR_UNSUPPORTED), /* Win 7 testbot */
2784 "SetFullscreenState failed, hr %#x.\n", hr);
2785 if (FAILED(hr))
2787 skip("Could not change fullscreen state.\n");
2788 goto done;
2790 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2791 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2792 refcount = IDXGISwapChain_Release(swapchain);
2793 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
2795 DestroyWindow(swapchain_desc.OutputWindow);
2796 swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0);
2797 check_window_fullscreen_state(swapchain_desc.OutputWindow, &initial_state.fullscreen_state);
2798 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
2799 ok(SUCCEEDED(hr), "CreateSwapChain failed, hr %#x.\n", hr);
2800 check_swapchain_fullscreen_state(swapchain, &initial_state);
2801 test_swapchain_fullscreen_state(swapchain, adapter, &initial_state);
2802 refcount = IDXGISwapChain_Release(swapchain);
2803 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
2805 DestroyWindow(swapchain_desc.OutputWindow);
2806 swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0);
2807 check_window_fullscreen_state(swapchain_desc.OutputWindow, &initial_state.fullscreen_state);
2808 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
2809 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2810 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2811 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2812 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
2813 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2814 ok(!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen);
2815 DestroyWindow(swapchain_desc.OutputWindow);
2816 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2817 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2818 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
2819 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2820 ok(!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen);
2821 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
2822 ok(hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE, "Got unexpected hr %#x.\n", hr);
2823 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
2824 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2825 ok(!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen);
2826 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2827 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2828 refcount = IDXGISwapChain_Release(swapchain);
2829 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
2831 swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0);
2832 check_window_fullscreen_state(swapchain_desc.OutputWindow, &initial_state.fullscreen_state);
2833 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
2834 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2835 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
2836 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2837 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
2838 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2839 ok(!!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen);
2840 DestroyWindow(swapchain_desc.OutputWindow);
2841 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
2842 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2843 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
2844 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2845 ok(!!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen);
2846 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2847 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2848 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
2849 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2850 ok(!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen);
2851 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
2852 ok(hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE, "Got unexpected hr %#x.\n", hr);
2853 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
2854 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2855 ok(!fullscreen, "Got unexpected fullscreen %#x.\n", fullscreen);
2856 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
2857 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
2858 refcount = IDXGISwapChain_Release(swapchain);
2859 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
2861 swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0);
2862 check_window_fullscreen_state(swapchain_desc.OutputWindow, &initial_state.fullscreen_state);
2863 swapchain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
2864 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
2865 ok(SUCCEEDED(hr), "CreateSwapChain failed, hr %#x.\n", hr);
2866 check_swapchain_fullscreen_state(swapchain, &initial_state);
2867 test_swapchain_fullscreen_state(swapchain, adapter, &initial_state);
2869 done:
2870 if (adapter)
2871 IDXGIAdapter_Release(adapter);
2872 refcount = IDXGISwapChain_Release(swapchain);
2873 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
2874 check_window_fullscreen_state(swapchain_desc.OutputWindow, &initial_state.fullscreen_state);
2875 DestroyWindow(swapchain_desc.OutputWindow);
2877 refcount = IDXGIFactory_Release(factory);
2878 ok(refcount == !is_d3d12, "Got unexpected refcount %u.\n", refcount);
2881 static void test_default_fullscreen_target_output(IUnknown *device, BOOL is_d3d12)
2883 IDXGIOutput *output, *containing_output, *target;
2884 unsigned int adapter_idx, output_idx;
2885 DXGI_SWAP_CHAIN_DESC swapchain_desc;
2886 DXGI_OUTPUT_DESC output_desc;
2887 unsigned int width, height;
2888 IDXGISwapChain *swapchain;
2889 IDXGIFactory *factory;
2890 IDXGIAdapter *adapter;
2891 BOOL fullscreen, ret;
2892 RECT window_rect;
2893 ULONG refcount;
2894 HRESULT hr;
2896 get_factory(device, is_d3d12, &factory);
2898 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
2899 swapchain_desc.BufferDesc.RefreshRate.Denominator = 60;
2900 swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
2901 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
2902 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
2903 swapchain_desc.SampleDesc.Count = 1;
2904 swapchain_desc.SampleDesc.Quality = 0;
2905 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
2906 swapchain_desc.BufferCount = is_d3d12 ? 2 : 1;
2907 swapchain_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
2908 swapchain_desc.Flags = 0;
2910 for (adapter_idx = 0; SUCCEEDED(IDXGIFactory_EnumAdapters(factory, adapter_idx, &adapter));
2911 ++adapter_idx)
2913 for (output_idx = 0; SUCCEEDED(IDXGIAdapter_EnumOutputs(adapter, output_idx, &output));
2914 ++output_idx)
2916 /* Windowed swapchain */
2917 swapchain_desc.BufferDesc.Width = 640;
2918 swapchain_desc.BufferDesc.Height = 480;
2919 swapchain_desc.OutputWindow = create_window();
2920 swapchain_desc.Windowed = TRUE;
2921 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
2922 ok(SUCCEEDED(hr), "Adapter %u output %u: CreateSwapChain failed, hr %#x.\n",
2923 adapter_idx, output_idx, hr);
2925 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, &containing_output);
2926 ok(SUCCEEDED(hr), "Adapter %u output %u: GetFullscreenState failed, hr %#x.\n",
2927 adapter_idx, output_idx, hr);
2928 ok(!fullscreen, "Adapter %u output %u: Expected not fullscreen.\n", adapter_idx,
2929 output_idx);
2930 ok(!containing_output, "Adapter %u output %u: Expected a null output.\n", adapter_idx,
2931 output_idx);
2933 /* Move the OutputWindow to the current output. */
2934 hr = IDXGIOutput_GetDesc(output, &output_desc);
2935 ok(SUCCEEDED(hr), "Adapter %u output %u: GetDesc failed, hr %#x.\n", adapter_idx,
2936 output_idx, hr);
2937 ret = SetWindowPos(swapchain_desc.OutputWindow, 0,
2938 output_desc.DesktopCoordinates.left, output_desc.DesktopCoordinates.top,
2939 0, 0, SWP_NOSIZE | SWP_NOZORDER);
2940 ok(ret, "Adapter %u output %u: SetWindowPos failed, error %#x.\n", adapter_idx,
2941 output_idx, GetLastError());
2943 hr = IDXGISwapChain_GetContainingOutput(swapchain, &containing_output);
2944 ok(SUCCEEDED(hr) || broken(hr == DXGI_ERROR_UNSUPPORTED) /* Win 7 testbot */,
2945 "Adapter %u output %u: GetContainingOutput failed, hr %#x.\n", adapter_idx,
2946 output_idx, hr);
2947 if (hr == DXGI_ERROR_UNSUPPORTED)
2949 win_skip("Adapter %u output %u: GetContainingOutput() not supported.\n",
2950 adapter_idx, output_idx);
2951 IDXGISwapChain_Release(swapchain);
2952 IDXGIOutput_Release(output);
2953 DestroyWindow(swapchain_desc.OutputWindow);
2954 continue;
2957 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
2958 ok(SUCCEEDED(hr) || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE,
2959 "Adapter %u output %u: SetFullscreenState failed, hr %#x.\n", adapter_idx,
2960 output_idx, hr);
2961 if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
2963 skip("Adapter %u output %u: Could not change fullscreen state.\n", adapter_idx,
2964 output_idx);
2965 IDXGIOutput_Release(containing_output);
2966 IDXGISwapChain_Release(swapchain);
2967 IDXGIOutput_Release(output);
2968 DestroyWindow(swapchain_desc.OutputWindow);
2969 continue;
2971 GetWindowRect(swapchain_desc.OutputWindow, &window_rect);
2972 ok(EqualRect(&window_rect, &output_desc.DesktopCoordinates),
2973 "Adapter %u output %u: Expect window rect %s, got %s.\n", adapter_idx,
2974 output_idx, wine_dbgstr_rect(&output_desc.DesktopCoordinates),
2975 wine_dbgstr_rect(&window_rect));
2977 target = NULL;
2978 hr = IDXGISwapChain_GetFullscreenState(swapchain, NULL, &target);
2979 ok(SUCCEEDED(hr), "Adapter %u output %u: GetFullscreenState failed, hr %#x.\n",
2980 adapter_idx, output_idx, hr);
2981 ok(target != containing_output,
2982 "Adapter %u output %u: Got unexpected output %p, expected %p.\n", adapter_idx,
2983 output_idx, target, containing_output);
2984 check_output_equal(target, containing_output);
2986 refcount = IDXGIOutput_Release(containing_output);
2987 ok(!refcount, "Adapter %u output %u: IDXGIOutput has %u references left.\n",
2988 adapter_idx, output_idx, refcount);
2990 hr = IDXGISwapChain_GetContainingOutput(swapchain, &containing_output);
2991 ok(SUCCEEDED(hr), "Adapter %u output %u: GetContainingOutput failed, hr %#x.\n",
2992 adapter_idx, output_idx, hr);
2993 ok(containing_output == target,
2994 "Adapter %u output %u: Got unexpected containing output %p, expected %p.\n",
2995 adapter_idx, output_idx, containing_output, target);
2996 refcount = IDXGIOutput_Release(containing_output);
2997 ok(refcount >= 2, "Adapter %u output %u: Got unexpected refcount %u.\n", adapter_idx,
2998 output_idx, refcount);
2999 refcount = IDXGIOutput_Release(target);
3000 ok(refcount >= 1, "Adapter %u output %u: Got unexpected refcount %u.\n", adapter_idx,
3001 output_idx, refcount);
3003 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
3004 ok(SUCCEEDED(hr), "Adapter %u output %u: SetFullscreenState failed, hr %#x.\n",
3005 adapter_idx, output_idx, hr);
3006 refcount = IDXGISwapChain_Release(swapchain);
3007 ok(!refcount, "Adapter %u output %u: IDXGISwapChain has %u references left.\n",
3008 adapter_idx, output_idx, refcount);
3009 DestroyWindow(swapchain_desc.OutputWindow);
3011 /* Full screen swapchain */
3012 width = output_desc.DesktopCoordinates.right - output_desc.DesktopCoordinates.left;
3013 height = output_desc.DesktopCoordinates.bottom - output_desc.DesktopCoordinates.top;
3014 swapchain_desc.BufferDesc.Width = width;
3015 swapchain_desc.BufferDesc.Height = height;
3016 swapchain_desc.OutputWindow = create_window();
3017 swapchain_desc.Windowed = FALSE;
3018 ret = SetWindowPos(swapchain_desc.OutputWindow, 0, output_desc.DesktopCoordinates.left,
3019 output_desc.DesktopCoordinates.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
3020 ok(ret, "Adapter %u output %u: SetWindowPos failed, error %#x.\n", adapter_idx,
3021 output_idx, GetLastError());
3022 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
3023 if (FAILED(hr))
3025 skip("Adapter %u output %u: CreateSwapChain failed, hr %#x.\n", adapter_idx,
3026 output_idx, hr);
3027 IDXGIOutput_Release(output);
3028 DestroyWindow(swapchain_desc.OutputWindow);
3029 continue;
3032 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, &containing_output);
3033 ok(SUCCEEDED(hr), "Adapter %u output %u: GetFullscreenState failed, hr %#x.\n",
3034 adapter_idx, output_idx, hr);
3035 ok(fullscreen, "Adapter %u output %u: Expected fullscreen.\n", adapter_idx, output_idx);
3036 ok(!!containing_output, "Adapter %u output %u: Expected a valid output.\n", adapter_idx,
3037 output_idx);
3038 if (containing_output)
3039 IDXGIOutput_Release(containing_output);
3041 ret = GetWindowRect(swapchain_desc.OutputWindow, &window_rect);
3042 ok(ret, "Adapter %u output %u: GetWindowRect failed, error %#x.\n", adapter_idx,
3043 output_idx, GetLastError());
3044 ok(EqualRect(&window_rect, &output_desc.DesktopCoordinates),
3045 "Adapter %u output %u: Expect window rect %s, got %s.\n", adapter_idx,
3046 output_idx, wine_dbgstr_rect(&output_desc.DesktopCoordinates),
3047 wine_dbgstr_rect(&window_rect));
3049 hr = IDXGISwapChain_GetContainingOutput(swapchain, &containing_output);
3050 ok(hr == S_OK, "Adapter %u output %u: GetContainingOutput failed, hr %#x.\n",
3051 adapter_idx, output_idx, hr);
3052 ok(containing_output != output,
3053 "Adapter %u output %u: Got unexpected output %p, expected %p.\n", adapter_idx,
3054 output_idx, output, containing_output);
3055 check_output_equal(output, containing_output);
3056 IDXGIOutput_Release(containing_output);
3058 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
3059 ok(hr == S_OK, "Adapter %u output %u: SetFullscreenState failed, hr %#x.\n",
3060 adapter_idx, output_idx, hr);
3061 refcount = IDXGISwapChain_Release(swapchain);
3062 ok(!refcount, "Adapter %u output %u: IDXGISwapChain has %u references left.\n",
3063 adapter_idx, output_idx, refcount);
3064 refcount = IDXGIOutput_Release(output);
3065 ok(!refcount, "Adapter %u output %u: IDXGIOutput has %u references left.\n",
3066 adapter_idx, output_idx, refcount);
3067 DestroyWindow(swapchain_desc.OutputWindow);
3069 IDXGIAdapter_Release(adapter);
3072 refcount = IDXGIFactory_Release(factory);
3073 ok(refcount == !is_d3d12, "IDXGIFactory has %u references left.\n", refcount);
3076 static void test_windowed_resize_target(IDXGISwapChain *swapchain, HWND window,
3077 struct swapchain_fullscreen_state *state)
3079 struct swapchain_fullscreen_state expected_state;
3080 struct fullscreen_state *e;
3081 DXGI_MODE_DESC mode;
3082 RECT window_rect;
3083 unsigned int i;
3084 HRESULT hr;
3085 BOOL ret;
3087 static const struct
3089 unsigned int width, height;
3091 sizes[] =
3093 {200, 200},
3094 {400, 200},
3095 {400, 400},
3096 {600, 800},
3097 {1000, 600},
3098 {1600, 100},
3099 {2000, 1000},
3102 check_swapchain_fullscreen_state(swapchain, state);
3103 expected_state = *state;
3104 e = &expected_state.fullscreen_state;
3106 for (i = 0; i < ARRAY_SIZE(sizes); ++i)
3108 SetRect(&e->client_rect, 0, 0, sizes[i].width, sizes[i].height);
3109 e->window_rect = e->client_rect;
3110 ret = AdjustWindowRectEx(&e->window_rect, GetWindowLongW(window, GWL_STYLE),
3111 FALSE, GetWindowLongW(window, GWL_EXSTYLE));
3112 ok(ret, "AdjustWindowRectEx failed.\n");
3113 if (GetMenu(window))
3114 e->client_rect.bottom -= GetSystemMetrics(SM_CYMENU);
3115 SetRect(&e->window_rect, 0, 0,
3116 e->window_rect.right - e->window_rect.left,
3117 e->window_rect.bottom - e->window_rect.top);
3118 GetWindowRect(window, &window_rect);
3119 OffsetRect(&e->window_rect, window_rect.left, window_rect.top);
3120 if (e->window_rect.right >= e->monitor_rect.right
3121 || e->window_rect.bottom >= e->monitor_rect.bottom)
3123 skip("Test %u: Window %s does not fit on screen %s.\n",
3124 i, wine_dbgstr_rect(&e->window_rect), wine_dbgstr_rect(&e->monitor_rect));
3125 continue;
3128 memset(&mode, 0, sizeof(mode));
3129 mode.Width = sizes[i].width;
3130 mode.Height = sizes[i].height;
3131 hr = IDXGISwapChain_ResizeTarget(swapchain, &mode);
3132 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3133 check_swapchain_fullscreen_state(swapchain, &expected_state);
3136 ret = SetWindowPos(window, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOZORDER);
3137 ok(ret, "SetWindowPos failed, error %#x.\n", GetLastError());
3138 GetWindowRect(window, &e->window_rect);
3139 GetClientRect(window, &e->client_rect);
3140 ret = SetWindowPos(window, 0, 0, 0, 200, 200, SWP_NOMOVE | SWP_NOZORDER);
3141 ok(ret, "SetWindowPos failed, error %#x.\n", GetLastError());
3143 memset(&mode, 0, sizeof(mode));
3144 hr = IDXGISwapChain_ResizeTarget(swapchain, &mode);
3145 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3146 check_swapchain_fullscreen_state(swapchain, &expected_state);
3148 GetWindowRect(window, &e->window_rect);
3149 GetClientRect(window, &e->client_rect);
3150 *state = expected_state;
3153 static void test_fullscreen_resize_target(IDXGISwapChain *swapchain,
3154 const struct swapchain_fullscreen_state *initial_state)
3156 struct swapchain_fullscreen_state expected_state;
3157 DXGI_SWAP_CHAIN_DESC swapchain_desc;
3158 DXGI_OUTPUT_DESC output_desc;
3159 unsigned int i, mode_count;
3160 DXGI_MODE_DESC *modes;
3161 IDXGIOutput *target;
3162 HRESULT hr;
3164 hr = IDXGISwapChain_GetDesc(swapchain, &swapchain_desc);
3165 ok(SUCCEEDED(hr), "GetDesc failed, hr %#x.\n", hr);
3167 hr = IDXGISwapChain_GetFullscreenState(swapchain, NULL, &target);
3168 ok(SUCCEEDED(hr), "GetFullscreenState failed, hr %#x.\n", hr);
3170 hr = IDXGIOutput_GetDisplayModeList(target, DXGI_FORMAT_R8G8B8A8_UNORM, 0, &mode_count, NULL);
3171 ok(SUCCEEDED(hr) || broken(hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE), /* Win 7 testbot */
3172 "Failed to list modes, hr %#x.\n", hr);
3173 if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
3175 win_skip("GetDisplayModeList() not supported.\n");
3176 IDXGIOutput_Release(target);
3177 return;
3180 modes = heap_calloc(mode_count, sizeof(*modes));
3181 ok(!!modes, "Failed to allocate memory.\n");
3183 hr = IDXGIOutput_GetDisplayModeList(target, DXGI_FORMAT_R8G8B8A8_UNORM, 0, &mode_count, modes);
3184 ok(SUCCEEDED(hr), "Failed to list modes, hr %#x.\n", hr);
3186 expected_state = *initial_state;
3187 for (i = 0; i < min(mode_count, 20); ++i)
3189 /* FIXME: Modes with scaling aren't fully tested. */
3190 if (!(swapchain_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH)
3191 && modes[i].Scaling != DXGI_MODE_SCALING_UNSPECIFIED)
3192 continue;
3194 hr = IDXGIOutput_GetDesc(target, &output_desc);
3195 ok(hr == S_OK, "Failed to get desc, hr %#x.\n", hr);
3197 compute_expected_swapchain_fullscreen_state_after_fullscreen_change(&expected_state,
3198 &swapchain_desc, &output_desc.DesktopCoordinates, modes[i].Width, modes[i].Height, NULL);
3200 hr = IDXGISwapChain_ResizeTarget(swapchain, &modes[i]);
3201 ok(hr == S_OK || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE, "Got unexpected hr %#x.\n", hr);
3202 if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
3204 skip("Failed to change to video mode %u.\n", i);
3205 break;
3207 check_swapchain_fullscreen_state(swapchain, &expected_state);
3209 hr = IDXGIOutput_GetDesc(target, &output_desc);
3210 ok(hr == S_OK, "Failed to get desc, hr %#x.\n", hr);
3211 ok(EqualRect(&output_desc.DesktopCoordinates, &expected_state.fullscreen_state.monitor_rect),
3212 "Got desktop coordinates %s, expected %s.\n",
3213 wine_dbgstr_rect(&output_desc.DesktopCoordinates),
3214 wine_dbgstr_rect(&expected_state.fullscreen_state.monitor_rect));
3217 heap_free(modes);
3218 IDXGIOutput_Release(target);
3221 static void test_resize_target(IUnknown *device, BOOL is_d3d12)
3223 struct swapchain_fullscreen_state initial_state, expected_state;
3224 unsigned int adapter_idx, output_idx, test_idx;
3225 DXGI_SWAP_CHAIN_DESC swapchain_desc;
3226 DXGI_OUTPUT_DESC output_desc;
3227 IDXGISwapChain *swapchain;
3228 IDXGIFactory *factory;
3229 IDXGIAdapter *adapter;
3230 IDXGIOutput *output;
3231 ULONG refcount;
3232 HRESULT hr;
3234 static const struct
3236 POINT origin;
3237 BOOL fullscreen;
3238 BOOL menu;
3239 unsigned int flags;
3241 tests[] =
3243 {{ 0, 0}, TRUE, FALSE, 0},
3244 {{10, 10}, TRUE, FALSE, 0},
3245 {{ 0, 0}, TRUE, FALSE, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH},
3246 {{10, 10}, TRUE, FALSE, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH},
3247 {{ 0, 0}, FALSE, FALSE, 0},
3248 {{ 0, 0}, FALSE, FALSE, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH},
3249 {{10, 10}, FALSE, FALSE, 0},
3250 {{10, 10}, FALSE, FALSE, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH},
3251 {{ 0, 0}, FALSE, TRUE, 0},
3252 {{ 0, 0}, FALSE, TRUE, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH},
3253 {{10, 10}, FALSE, TRUE, 0},
3254 {{10, 10}, FALSE, TRUE, DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH},
3257 get_factory(device, is_d3d12, &factory);
3259 swapchain_desc.BufferDesc.Width = 800;
3260 swapchain_desc.BufferDesc.Height = 600;
3261 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
3262 swapchain_desc.BufferDesc.RefreshRate.Denominator = 1;
3263 swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
3264 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
3265 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
3266 swapchain_desc.SampleDesc.Count = 1;
3267 swapchain_desc.SampleDesc.Quality = 0;
3268 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
3269 swapchain_desc.BufferCount = is_d3d12 ? 2 : 1;
3270 swapchain_desc.Windowed = TRUE;
3271 swapchain_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
3272 swapchain_desc.Flags = 0;
3274 for (adapter_idx = 0; SUCCEEDED(IDXGIFactory_EnumAdapters(factory, adapter_idx, &adapter));
3275 ++adapter_idx)
3277 for (output_idx = 0; SUCCEEDED(IDXGIAdapter_EnumOutputs(adapter, output_idx, &output));
3278 ++output_idx)
3280 hr = IDXGIOutput_GetDesc(output, &output_desc);
3281 ok(hr == S_OK, "Adapter %u output %u: GetDesc failed, hr %#x.\n", adapter_idx,
3282 output_idx, hr);
3284 for (test_idx = 0; test_idx < ARRAY_SIZE(tests); ++test_idx)
3286 swapchain_desc.Flags = tests[test_idx].flags;
3287 swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0,
3288 output_desc.DesktopCoordinates.left + tests[test_idx].origin.x,
3289 output_desc.DesktopCoordinates.top + tests[test_idx].origin.y,
3290 400, 200, 0, 0, 0, 0);
3291 if (tests[test_idx].menu)
3293 HMENU menu_bar = CreateMenu();
3294 HMENU menu = CreateMenu();
3295 AppendMenuA(menu_bar, MF_POPUP, (UINT_PTR)menu, "Menu");
3296 SetMenu(swapchain_desc.OutputWindow, menu_bar);
3299 memset(&initial_state, 0, sizeof(initial_state));
3300 capture_fullscreen_state(&initial_state.fullscreen_state, swapchain_desc.OutputWindow);
3302 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
3303 ok(SUCCEEDED(hr), "Adapter %u output %u test %u: CreateSwapChain failed, hr %#x.\n",
3304 adapter_idx, output_idx, test_idx, hr);
3305 check_swapchain_fullscreen_state(swapchain, &initial_state);
3307 expected_state = initial_state;
3308 if (tests[test_idx].fullscreen)
3310 expected_state.fullscreen = TRUE;
3311 compute_expected_swapchain_fullscreen_state_after_fullscreen_change(&expected_state,
3312 &swapchain_desc, &initial_state.fullscreen_state.monitor_rect, 800, 600, NULL);
3313 hr = IDXGISwapChain_GetContainingOutput(swapchain, &expected_state.target);
3314 ok(SUCCEEDED(hr) || broken(hr == DXGI_ERROR_UNSUPPORTED) /* Win 7 testbot */,
3315 "Adapter %u output %u test %u: GetContainingOutput failed, hr %#x.\n",
3316 adapter_idx, output_idx, test_idx, hr);
3317 if (hr == DXGI_ERROR_UNSUPPORTED)
3319 win_skip("Adapter %u output %u test %u: GetContainingOutput() not supported.\n",
3320 adapter_idx, output_idx, test_idx);
3321 IDXGISwapChain_Release(swapchain);
3322 DestroyWindow(swapchain_desc.OutputWindow);
3323 continue;
3326 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
3327 ok(SUCCEEDED(hr) || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE,
3328 "Adapter %u output %u test %u: SetFullscreenState failed, hr %#x.\n",
3329 adapter_idx, output_idx, test_idx, hr);
3330 if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
3332 skip("Adapter %u output %u test %u: Could not change fullscreen state.\n",
3333 adapter_idx, output_idx, test_idx);
3334 IDXGIOutput_Release(expected_state.target);
3335 IDXGISwapChain_Release(swapchain);
3336 DestroyWindow(swapchain_desc.OutputWindow);
3337 continue;
3340 check_swapchain_fullscreen_state(swapchain, &expected_state);
3342 hr = IDXGISwapChain_ResizeTarget(swapchain, NULL);
3343 ok(hr == DXGI_ERROR_INVALID_CALL, "Adapter %u output %u test %u: Got unexpected hr %#x.\n",
3344 adapter_idx, output_idx, test_idx, hr);
3345 check_swapchain_fullscreen_state(swapchain, &expected_state);
3347 if (tests[test_idx].fullscreen)
3349 test_fullscreen_resize_target(swapchain, &expected_state);
3351 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
3352 ok(SUCCEEDED(hr), "Adapter %u output %u test %u: SetFullscreenState failed, hr %#x.\n",
3353 adapter_idx, output_idx, test_idx, hr);
3354 check_swapchain_fullscreen_state(swapchain, &initial_state);
3355 IDXGIOutput_Release(expected_state.target);
3356 check_swapchain_fullscreen_state(swapchain, &initial_state);
3357 expected_state = initial_state;
3359 else
3361 test_windowed_resize_target(swapchain, swapchain_desc.OutputWindow, &expected_state);
3363 check_swapchain_fullscreen_state(swapchain, &expected_state);
3366 refcount = IDXGISwapChain_Release(swapchain);
3367 ok(!refcount, "Adapter %u output %u test %u: IDXGISwapChain has %u references left.\n",
3368 adapter_idx, output_idx, test_idx, refcount);
3369 check_window_fullscreen_state(swapchain_desc.OutputWindow, &expected_state.fullscreen_state);
3370 DestroyWindow(swapchain_desc.OutputWindow);
3372 IDXGIOutput_Release(output);
3374 IDXGIAdapter_Release(adapter);
3376 refcount = IDXGIFactory_Release(factory);
3377 ok(refcount == !is_d3d12, "Got unexpected refcount %u.\n", refcount);
3380 static LRESULT CALLBACK resize_target_wndproc(HWND hwnd, unsigned int message, WPARAM wparam, LPARAM lparam)
3382 IDXGISwapChain *swapchain = (IDXGISwapChain *)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
3383 DXGI_SWAP_CHAIN_DESC desc;
3384 HRESULT hr;
3386 switch (message)
3388 case WM_SIZE:
3389 ok(!!swapchain, "GWLP_USERDATA is NULL.\n");
3390 hr = IDXGISwapChain_GetDesc(swapchain, &desc);
3391 ok(hr == S_OK, "Failed to get desc, hr %#x.\n", hr);
3392 ok(desc.BufferDesc.Width == 800, "Got unexpected buffer width %u.\n", desc.BufferDesc.Width);
3393 ok(desc.BufferDesc.Height == 600, "Got unexpected buffer height %u.\n", desc.BufferDesc.Height);
3394 return 0;
3396 default:
3397 return DefWindowProcA(hwnd, message, wparam, lparam);
3401 struct window_thread_data
3403 HWND window;
3404 HANDLE window_created;
3405 HANDLE finished;
3408 static DWORD WINAPI window_thread(void *data)
3410 struct window_thread_data *thread_data = data;
3411 unsigned int ret;
3412 WNDCLASSA wc;
3413 MSG msg;
3415 memset(&wc, 0, sizeof(wc));
3416 wc.lpfnWndProc = resize_target_wndproc;
3417 wc.lpszClassName = "dxgi_resize_target_wndproc_wc";
3418 ok(RegisterClassA(&wc), "Failed to register window class.\n");
3420 thread_data->window = CreateWindowA("dxgi_resize_target_wndproc_wc", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0);
3421 ok(!!thread_data->window, "Failed to create window.\n");
3423 ret = SetEvent(thread_data->window_created);
3424 ok(ret, "Failed to set event, last error %#x.\n", GetLastError());
3426 for (;;)
3428 while (PeekMessageA(&msg, 0, 0, 0, PM_REMOVE))
3429 DispatchMessageA(&msg);
3431 ret = WaitForSingleObject(thread_data->finished, 0);
3432 if (ret != WAIT_TIMEOUT)
3433 break;
3435 ok(ret == WAIT_OBJECT_0, "Failed to wait for event, ret %#x, last error %#x.\n", ret, GetLastError());
3437 DestroyWindow(thread_data->window);
3438 thread_data->window = NULL;
3440 UnregisterClassA("dxgi_test_wndproc_wc", GetModuleHandleA(NULL));
3442 return 0;
3445 static void test_resize_target_wndproc(void)
3447 struct window_thread_data thread_data;
3448 DXGI_SWAP_CHAIN_DESC swapchain_desc;
3449 IDXGISwapChain *swapchain;
3450 IDXGIFactory *factory;
3451 IDXGIAdapter *adapter;
3452 DXGI_MODE_DESC mode;
3453 IDXGIDevice *device;
3454 unsigned int ret;
3455 ULONG refcount;
3456 LONG_PTR data;
3457 HANDLE thread;
3458 HRESULT hr;
3459 RECT rect;
3461 if (!(device = create_device(0)))
3463 skip("Failed to create device.\n");
3464 return;
3467 memset(&thread_data, 0, sizeof(thread_data));
3468 thread_data.window_created = CreateEventA(NULL, FALSE, FALSE, NULL);
3469 ok(!!thread_data.window_created, "Failed to create event, last error %#x.\n", GetLastError());
3470 thread_data.finished = CreateEventA(NULL, FALSE, FALSE, NULL);
3471 ok(!!thread_data.finished, "Failed to create event, last error %#x.\n", GetLastError());
3473 thread = CreateThread(NULL, 0, window_thread, &thread_data, 0, NULL);
3474 ok(!!thread, "Failed to create thread, last error %#x.\n", GetLastError());
3475 ret = WaitForSingleObject(thread_data.window_created, INFINITE);
3476 ok(ret == WAIT_OBJECT_0, "Failed to wait for thread, ret %#x, last error %#x.\n", ret, GetLastError());
3478 hr = IDXGIDevice_GetAdapter(device, &adapter);
3479 ok(hr == S_OK, "Failed to get adapter, hr %#x.\n", hr);
3480 hr = IDXGIAdapter_GetParent(adapter, &IID_IDXGIFactory, (void **)&factory);
3481 ok(hr == S_OK, "Failed to get parent, hr %#x.\n", hr);
3483 swapchain_desc.BufferDesc.Width = 800;
3484 swapchain_desc.BufferDesc.Height = 600;
3485 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
3486 swapchain_desc.BufferDesc.RefreshRate.Denominator = 1;
3487 swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
3488 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
3489 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
3490 swapchain_desc.SampleDesc.Count = 1;
3491 swapchain_desc.SampleDesc.Quality = 0;
3492 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
3493 swapchain_desc.BufferCount = 1;
3494 swapchain_desc.OutputWindow = thread_data.window;
3495 swapchain_desc.Windowed = TRUE;
3496 swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
3497 swapchain_desc.Flags = 0;
3498 hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown *)device, &swapchain_desc, &swapchain);
3499 ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
3501 data = SetWindowLongPtrA(thread_data.window, GWLP_USERDATA, (LONG_PTR)swapchain);
3502 ok(!data, "Got unexpected GWLP_USERDATA %p.\n", (void *)data);
3504 memset(&mode, 0, sizeof(mode));
3505 mode.Width = 600;
3506 mode.Height = 400;
3507 hr = IDXGISwapChain_ResizeTarget(swapchain, &mode);
3508 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3510 hr = IDXGISwapChain_GetDesc(swapchain, &swapchain_desc);
3511 ok(hr == S_OK, "Getswapchain_desc failed, hr %#x.\n", hr);
3512 ok(swapchain_desc.BufferDesc.Width == 800,
3513 "Got unexpected buffer width %u.\n", swapchain_desc.BufferDesc.Width);
3514 ok(swapchain_desc.BufferDesc.Height == 600,
3515 "Got unexpected buffer height %u.\n", swapchain_desc.BufferDesc.Height);
3517 ret = GetClientRect(swapchain_desc.OutputWindow, &rect);
3518 ok(ret, "Failed to get client rect.\n");
3519 ok(rect.right == mode.Width && rect.bottom == mode.Height,
3520 "Got unexpected client rect %s.\n", wine_dbgstr_rect(&rect));
3522 refcount = IDXGISwapChain_Release(swapchain);
3523 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
3525 IDXGIAdapter_Release(adapter);
3526 refcount = IDXGIDevice_Release(device);
3527 ok(!refcount, "Device has %u references left.\n", refcount);
3528 refcount = IDXGIFactory_Release(factory);
3529 ok(!refcount, "Factory has %u references left.\n", refcount);
3531 ret = SetEvent(thread_data.finished);
3532 ok(ret, "Failed to set event, last error %#x.\n", GetLastError());
3533 ret = WaitForSingleObject(thread, INFINITE);
3534 ok(ret == WAIT_OBJECT_0, "Failed to wait for thread, ret %#x, last error %#x.\n", ret, GetLastError());
3535 CloseHandle(thread);
3536 CloseHandle(thread_data.window_created);
3537 CloseHandle(thread_data.finished);
3540 static void test_inexact_modes(void)
3542 struct swapchain_fullscreen_state initial_state, expected_state;
3543 DXGI_SWAP_CHAIN_DESC swapchain_desc, result_desc;
3544 IDXGIOutput *output = NULL;
3545 IDXGISwapChain *swapchain;
3546 IDXGIFactory *factory;
3547 IDXGIAdapter *adapter;
3548 IDXGIDevice *device;
3549 unsigned int i;
3550 ULONG refcount;
3551 HRESULT hr;
3553 static const struct
3555 unsigned int width, height;
3557 sizes[] =
3559 {101, 101},
3560 {203, 204},
3561 {799, 601},
3564 if (!(device = create_device(0)))
3566 skip("Failed to create device.\n");
3567 return;
3570 hr = IDXGIDevice_GetAdapter(device, &adapter);
3571 ok(SUCCEEDED(hr), "GetAdapter failed, hr %#x.\n", hr);
3573 hr = IDXGIAdapter_GetParent(adapter, &IID_IDXGIFactory, (void **)&factory);
3574 ok(SUCCEEDED(hr), "GetParent failed, hr %#x.\n", hr);
3576 swapchain_desc.BufferDesc.Width = 800;
3577 swapchain_desc.BufferDesc.Height = 600;
3578 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
3579 swapchain_desc.BufferDesc.RefreshRate.Denominator = 1;
3580 swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
3581 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
3582 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
3583 swapchain_desc.SampleDesc.Count = 1;
3584 swapchain_desc.SampleDesc.Quality = 0;
3585 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
3586 swapchain_desc.BufferCount = 1;
3587 swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0);
3588 swapchain_desc.Windowed = FALSE;
3589 swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
3590 swapchain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
3592 memset(&initial_state, 0, sizeof(initial_state));
3593 capture_fullscreen_state(&initial_state.fullscreen_state, swapchain_desc.OutputWindow);
3595 hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown *)device, &swapchain_desc, &swapchain);
3596 ok(SUCCEEDED(hr), "CreateSwapChain failed, hr %#x.\n", hr);
3597 hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
3598 ok(SUCCEEDED(hr), "GetDesc failed, hr %#x.\n", hr);
3599 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
3600 ok(SUCCEEDED(hr), "SetFullscreenState failed, hr %#x.\n", hr);
3601 hr = IDXGISwapChain_GetContainingOutput(swapchain, &output);
3602 ok(SUCCEEDED(hr) || broken(hr == DXGI_ERROR_UNSUPPORTED) /* Win 7 testbot */,
3603 "GetContainingOutput failed, hr %#x.\n", hr);
3604 refcount = IDXGISwapChain_Release(swapchain);
3605 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
3606 if (hr == DXGI_ERROR_UNSUPPORTED)
3608 win_skip("GetContainingOutput() not supported.\n");
3609 goto done;
3611 if (result_desc.Windowed)
3613 win_skip("Fullscreen not supported.\n");
3614 goto done;
3617 check_window_fullscreen_state(swapchain_desc.OutputWindow, &initial_state.fullscreen_state);
3619 for (i = 0; i < ARRAY_SIZE(sizes); ++i)
3621 /* Test CreateSwapChain(). */
3622 swapchain_desc.BufferDesc.Width = sizes[i].width;
3623 swapchain_desc.BufferDesc.Height = sizes[i].height;
3624 swapchain_desc.Windowed = FALSE;
3626 expected_state = initial_state;
3627 compute_expected_swapchain_fullscreen_state_after_fullscreen_change(&expected_state,
3628 &swapchain_desc, &initial_state.fullscreen_state.monitor_rect,
3629 sizes[i].width, sizes[i].height, output);
3631 hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown *)device, &swapchain_desc, &swapchain);
3632 ok(SUCCEEDED(hr), "CreateSwapChain failed, hr %#x.\n", hr);
3634 check_swapchain_fullscreen_state(swapchain, &expected_state);
3635 hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
3636 ok(SUCCEEDED(hr), "GetDesc failed, hr %#x.\n", hr);
3637 ok(result_desc.BufferDesc.Width == sizes[i].width, "Got width %u, expected %u.\n",
3638 result_desc.BufferDesc.Width, sizes[i].width);
3639 ok(result_desc.BufferDesc.Height == sizes[i].height, "Got height %u, expected %u.\n",
3640 result_desc.BufferDesc.Height, sizes[i].height);
3642 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
3643 ok(SUCCEEDED(hr), "SetFullscreenState failed, hr %#x.\n", hr);
3644 check_swapchain_fullscreen_state(swapchain, &initial_state);
3646 refcount = IDXGISwapChain_Release(swapchain);
3647 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
3649 /* Test SetFullscreenState(). */
3650 swapchain_desc.BufferDesc.Width = sizes[i].width;
3651 swapchain_desc.BufferDesc.Height = sizes[i].height;
3652 swapchain_desc.Windowed = TRUE;
3654 hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown *)device, &swapchain_desc, &swapchain);
3655 ok(SUCCEEDED(hr), "CreateSwapChain failed, hr %#x.\n", hr);
3657 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, output);
3658 ok(SUCCEEDED(hr), "SetFullscreenState failed, hr %#x.\n", hr);
3660 check_swapchain_fullscreen_state(swapchain, &expected_state);
3661 hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
3662 ok(SUCCEEDED(hr), "GetDesc failed, hr %#x.\n", hr);
3663 ok(result_desc.BufferDesc.Width == sizes[i].width, "Got width %u, expected %u.\n",
3664 result_desc.BufferDesc.Width, sizes[i].width);
3665 ok(result_desc.BufferDesc.Height == sizes[i].height, "Got height %u, expected %u.\n",
3666 result_desc.BufferDesc.Height, sizes[i].height);
3668 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
3669 ok(SUCCEEDED(hr), "SetFullscreenState failed, hr %#x.\n", hr);
3670 check_swapchain_fullscreen_state(swapchain, &initial_state);
3672 refcount = IDXGISwapChain_Release(swapchain);
3673 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
3675 /* Test ResizeTarget(). */
3676 swapchain_desc.BufferDesc.Width = 800;
3677 swapchain_desc.BufferDesc.Height = 600;
3678 swapchain_desc.Windowed = TRUE;
3680 hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown *)device, &swapchain_desc, &swapchain);
3681 ok(SUCCEEDED(hr), "CreateSwapChain failed, hr %#x.\n", hr);
3683 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, output);
3684 ok(SUCCEEDED(hr), "SetFullscreenState failed, hr %#x.\n", hr);
3686 swapchain_desc.BufferDesc.Width = sizes[i].width;
3687 swapchain_desc.BufferDesc.Height = sizes[i].height;
3688 hr = IDXGISwapChain_ResizeTarget(swapchain, &swapchain_desc.BufferDesc);
3689 ok(SUCCEEDED(hr), "ResizeTarget failed, hr %#x.\n", hr);
3691 check_swapchain_fullscreen_state(swapchain, &expected_state);
3692 hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
3693 ok(SUCCEEDED(hr), "GetDesc failed, hr %#x.\n", hr);
3694 ok(result_desc.BufferDesc.Width == 800, "Got width %u.\n", result_desc.BufferDesc.Width);
3695 ok(result_desc.BufferDesc.Height == 600, "Got height %u.\n", result_desc.BufferDesc.Height);
3697 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
3698 ok(SUCCEEDED(hr), "SetFullscreenState failed, hr %#x.\n", hr);
3699 check_swapchain_fullscreen_state(swapchain, &initial_state);
3701 refcount = IDXGISwapChain_Release(swapchain);
3702 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
3705 done:
3706 if (output)
3707 IDXGIOutput_Release(output);
3708 IDXGIAdapter_Release(adapter);
3709 refcount = IDXGIDevice_Release(device);
3710 ok(!refcount, "Device has %u references left.\n", refcount);
3711 refcount = IDXGIFactory_Release(factory);
3712 ok(!refcount, "Factory has %u references left.\n", refcount);
3713 DestroyWindow(swapchain_desc.OutputWindow);
3716 static void test_create_factory(void)
3718 IUnknown *iface;
3719 ULONG refcount;
3720 HRESULT hr;
3722 iface = (void *)0xdeadbeef;
3723 hr = CreateDXGIFactory(&IID_IDXGIDevice, (void **)&iface);
3724 ok(hr == E_NOINTERFACE, "Got unexpected hr %#x.\n", hr);
3725 ok(!iface, "Got unexpected iface %p.\n", iface);
3727 hr = CreateDXGIFactory(&IID_IUnknown, (void **)&iface);
3728 ok(SUCCEEDED(hr), "Failed to create factory with IID_IUnknown, hr %#x.\n", hr);
3729 IUnknown_Release(iface);
3731 hr = CreateDXGIFactory(&IID_IDXGIObject, (void **)&iface);
3732 ok(SUCCEEDED(hr), "Failed to create factory with IID_IDXGIObject, hr %#x.\n", hr);
3733 IUnknown_Release(iface);
3735 hr = CreateDXGIFactory(&IID_IDXGIFactory, (void **)&iface);
3736 ok(SUCCEEDED(hr), "Failed to create factory with IID_IDXGIFactory, hr %#x.\n", hr);
3737 check_interface(iface, &IID_IDXGIFactory1, FALSE, FALSE);
3738 IUnknown_Release(iface);
3740 iface = (void *)0xdeadbeef;
3741 hr = CreateDXGIFactory(&IID_IDXGIFactory1, (void **)&iface);
3742 ok(hr == E_NOINTERFACE, "Got unexpected hr %#x.\n", hr);
3743 ok(!iface, "Got unexpected iface %p.\n", iface);
3745 iface = NULL;
3746 hr = CreateDXGIFactory(&IID_IDXGIFactory2, (void **)&iface);
3747 ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* Not available on all Windows versions. */,
3748 "Got unexpected hr %#x.\n", hr);
3749 if (SUCCEEDED(hr))
3751 refcount = IUnknown_Release(iface);
3752 ok(!refcount, "Factory has %u references left.\n", refcount);
3755 if (!pCreateDXGIFactory1)
3757 win_skip("CreateDXGIFactory1 not available.\n");
3758 return;
3761 iface = (void *)0xdeadbeef;
3762 hr = pCreateDXGIFactory1(&IID_IDXGIDevice, (void **)&iface);
3763 ok(hr == E_NOINTERFACE, "Got unexpected hr %#x.\n", hr);
3764 ok(!iface, "Got unexpected iface %p.\n", iface);
3766 hr = pCreateDXGIFactory1(&IID_IUnknown, (void **)&iface);
3767 ok(SUCCEEDED(hr), "Failed to create factory with IID_IUnknown, hr %#x.\n", hr);
3768 IUnknown_Release(iface);
3770 hr = pCreateDXGIFactory1(&IID_IDXGIObject, (void **)&iface);
3771 ok(SUCCEEDED(hr), "Failed to create factory with IID_IDXGIObject, hr %#x.\n", hr);
3772 IUnknown_Release(iface);
3774 hr = pCreateDXGIFactory1(&IID_IDXGIFactory, (void **)&iface);
3775 ok(SUCCEEDED(hr), "Failed to create factory with IID_IDXGIFactory, hr %#x.\n", hr);
3776 check_interface(iface, &IID_IDXGIFactory1, TRUE, FALSE);
3777 refcount = IUnknown_Release(iface);
3778 ok(!refcount, "Factory has %u references left.\n", refcount);
3780 hr = pCreateDXGIFactory1(&IID_IDXGIFactory1, (void **)&iface);
3781 ok(SUCCEEDED(hr), "Failed to create factory with IID_IDXGIFactory1, hr %#x.\n", hr);
3782 IUnknown_Release(iface);
3784 iface = NULL;
3785 hr = pCreateDXGIFactory1(&IID_IDXGIFactory2, (void **)&iface);
3786 ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* Not available on all Windows versions. */,
3787 "Got unexpected hr %#x.\n", hr);
3788 if (SUCCEEDED(hr))
3790 refcount = IUnknown_Release(iface);
3791 ok(!refcount, "Factory has %u references left.\n", refcount);
3794 if (!pCreateDXGIFactory2)
3796 win_skip("CreateDXGIFactory2 not available.\n");
3797 return;
3800 hr = pCreateDXGIFactory2(0, &IID_IDXGIFactory3, (void **)&iface);
3801 ok(hr == S_OK, "Failed to create factory, hr %#x.\n", hr);
3802 check_interface(iface, &IID_IDXGIFactory, TRUE, FALSE);
3803 check_interface(iface, &IID_IDXGIFactory1, TRUE, FALSE);
3804 check_interface(iface, &IID_IDXGIFactory2, TRUE, FALSE);
3805 check_interface(iface, &IID_IDXGIFactory3, TRUE, FALSE);
3806 /* Not available on all Windows versions. */
3807 check_interface(iface, &IID_IDXGIFactory4, TRUE, TRUE);
3808 check_interface(iface, &IID_IDXGIFactory5, TRUE, TRUE);
3809 refcount = IUnknown_Release(iface);
3810 ok(!refcount, "Factory has %u references left.\n", refcount);
3812 hr = pCreateDXGIFactory2(0, &IID_IDXGIFactory, (void **)&iface);
3813 ok(hr == S_OK, "Failed to create factory, hr %#x.\n", hr);
3814 check_interface(iface, &IID_IDXGIFactory, TRUE, FALSE);
3815 check_interface(iface, &IID_IDXGIFactory1, TRUE, FALSE);
3816 check_interface(iface, &IID_IDXGIFactory2, TRUE, FALSE);
3817 check_interface(iface, &IID_IDXGIFactory3, TRUE, FALSE);
3818 refcount = IUnknown_Release(iface);
3819 ok(!refcount, "Factory has %u references left.\n", refcount);
3822 static void test_private_data(void)
3824 ULONG refcount, expected_refcount;
3825 IDXGIDevice *device;
3826 HRESULT hr;
3827 IDXGIDevice *test_object;
3828 IUnknown *ptr;
3829 static const DWORD data[] = {1, 2, 3, 4};
3830 UINT size;
3831 static const GUID dxgi_private_data_test_guid =
3833 0xfdb37466,
3834 0x428f,
3835 0x4edf,
3836 {0xa3, 0x7f, 0x9b, 0x1d, 0xf4, 0x88, 0xc5, 0xfc}
3838 static const GUID dxgi_private_data_test_guid2 =
3840 0x2e5afac2,
3841 0x87b5,
3842 0x4c10,
3843 {0x9b, 0x4b, 0x89, 0xd7, 0xd1, 0x12, 0xe7, 0x2b}
3846 if (!(device = create_device(0)))
3848 skip("Failed to create device.\n");
3849 return;
3852 test_object = create_device(0);
3854 /* SetPrivateData with a pointer of NULL has the purpose of FreePrivateData in previous
3855 * d3d versions. A successful clear returns S_OK. A redundant clear S_FALSE. Setting a
3856 * NULL interface is not considered a clear but as setting an interface pointer that
3857 * happens to be NULL. */
3858 hr = IDXGIDevice_SetPrivateData(device, &dxgi_private_data_test_guid, 0, NULL);
3859 ok(hr == S_FALSE, "Got unexpected hr %#x.\n", hr);
3860 hr = IDXGIDevice_SetPrivateDataInterface(device, &dxgi_private_data_test_guid, NULL);
3861 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3862 hr = IDXGIDevice_SetPrivateData(device, &dxgi_private_data_test_guid, ~0U, NULL);
3863 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3864 hr = IDXGIDevice_SetPrivateData(device, &dxgi_private_data_test_guid, ~0U, NULL);
3865 ok(hr == S_FALSE, "Got unexpected hr %#x.\n", hr);
3867 hr = IDXGIDevice_SetPrivateDataInterface(device, &dxgi_private_data_test_guid, NULL);
3868 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3869 size = sizeof(ptr) * 2;
3870 ptr = (IUnknown *)0xdeadbeef;
3871 hr = IDXGIDevice_GetPrivateData(device, &dxgi_private_data_test_guid, &size, &ptr);
3872 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3873 ok(!ptr, "Got unexpected pointer %p.\n", ptr);
3874 ok(size == sizeof(IUnknown *), "Got unexpected size %u.\n", size);
3876 refcount = get_refcount(test_object);
3877 hr = IDXGIDevice_SetPrivateDataInterface(device, &dxgi_private_data_test_guid,
3878 (IUnknown *)test_object);
3879 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3880 expected_refcount = refcount + 1;
3881 refcount = get_refcount(test_object);
3882 ok(refcount == expected_refcount, "Got unexpected refcount %u, expected %u.\n", refcount, expected_refcount);
3883 hr = IDXGIDevice_SetPrivateDataInterface(device, &dxgi_private_data_test_guid,
3884 (IUnknown *)test_object);
3885 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3886 refcount = get_refcount(test_object);
3887 ok(refcount == expected_refcount, "Got unexpected refcount %u, expected %u.\n", refcount, expected_refcount);
3889 hr = IDXGIDevice_SetPrivateDataInterface(device, &dxgi_private_data_test_guid, NULL);
3890 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3891 expected_refcount--;
3892 refcount = get_refcount(test_object);
3893 ok(refcount == expected_refcount, "Got unexpected refcount %u, expected %u.\n", refcount, expected_refcount);
3895 hr = IDXGIDevice_SetPrivateDataInterface(device, &dxgi_private_data_test_guid,
3896 (IUnknown *)test_object);
3897 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3898 size = sizeof(data);
3899 hr = IDXGIDevice_SetPrivateData(device, &dxgi_private_data_test_guid, size, data);
3900 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3901 refcount = get_refcount(test_object);
3902 ok(refcount == expected_refcount, "Got unexpected refcount %u, expected %u.\n", refcount, expected_refcount);
3903 hr = IDXGIDevice_SetPrivateData(device, &dxgi_private_data_test_guid, 42, NULL);
3904 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3905 hr = IDXGIDevice_SetPrivateData(device, &dxgi_private_data_test_guid, 42, NULL);
3906 ok(hr == S_FALSE, "Got unexpected hr %#x.\n", hr);
3908 hr = IDXGIDevice_SetPrivateDataInterface(device, &dxgi_private_data_test_guid,
3909 (IUnknown *)test_object);
3910 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3911 expected_refcount++;
3912 size = 2 * sizeof(ptr);
3913 ptr = NULL;
3914 hr = IDXGIDevice_GetPrivateData(device, &dxgi_private_data_test_guid, &size, &ptr);
3915 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3916 ok(size == sizeof(test_object), "Got unexpected size %u.\n", size);
3917 expected_refcount++;
3918 refcount = get_refcount(test_object);
3919 ok(refcount == expected_refcount, "Got unexpected refcount %u, expected %u.\n", refcount, expected_refcount);
3920 if (ptr)
3921 IUnknown_Release(ptr);
3922 expected_refcount--;
3924 ptr = (IUnknown *)0xdeadbeef;
3925 size = 1;
3926 hr = IDXGIDevice_GetPrivateData(device, &dxgi_private_data_test_guid, &size, NULL);
3927 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3928 ok(size == sizeof(device), "Got unexpected size %u.\n", size);
3929 size = 2 * sizeof(ptr);
3930 hr = IDXGIDevice_GetPrivateData(device, &dxgi_private_data_test_guid, &size, NULL);
3931 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
3932 ok(size == sizeof(device), "Got unexpected size %u.\n", size);
3933 refcount = get_refcount(test_object);
3934 ok(refcount == expected_refcount, "Got unexpected refcount %u, expected %u.\n", refcount, expected_refcount);
3936 size = 1;
3937 hr = IDXGIDevice_GetPrivateData(device, &dxgi_private_data_test_guid, &size, &ptr);
3938 ok(hr == DXGI_ERROR_MORE_DATA, "Got unexpected hr %#x.\n", hr);
3939 ok(size == sizeof(device), "Got unexpected size %u.\n", size);
3940 ok(ptr == (IUnknown *)0xdeadbeef, "Got unexpected pointer %p.\n", ptr);
3941 hr = IDXGIDevice_GetPrivateData(device, &dxgi_private_data_test_guid2, NULL, NULL);
3942 ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
3943 size = 0xdeadbabe;
3944 hr = IDXGIDevice_GetPrivateData(device, &dxgi_private_data_test_guid2, &size, &ptr);
3945 ok(hr == DXGI_ERROR_NOT_FOUND, "Got unexpected hr %#x.\n", hr);
3946 ok(size == 0, "Got unexpected size %u.\n", size);
3947 ok(ptr == (IUnknown *)0xdeadbeef, "Got unexpected pointer %p.\n", ptr);
3948 hr = IDXGIDevice_GetPrivateData(device, &dxgi_private_data_test_guid, NULL, &ptr);
3949 ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr);
3950 ok(ptr == (IUnknown *)0xdeadbeef, "Got unexpected pointer %p.\n", ptr);
3952 refcount = IDXGIDevice_Release(device);
3953 ok(!refcount, "Device has %u references left.\n", refcount);
3954 refcount = IDXGIDevice_Release(test_object);
3955 ok(!refcount, "Test object has %u references left.\n", refcount);
3958 #define check_surface_desc(a, b) check_surface_desc_(__LINE__, a, b)
3959 static void check_surface_desc_(unsigned int line, IDXGISurface *surface,
3960 const DXGI_SWAP_CHAIN_DESC *swapchain_desc)
3962 DXGI_SURFACE_DESC surface_desc;
3963 HRESULT hr;
3965 hr = IDXGISurface_GetDesc(surface, &surface_desc);
3966 ok_(__FILE__, line)(hr == S_OK, "Failed to get surface desc, hr %#x.\n", hr);
3967 ok_(__FILE__, line)(surface_desc.Width == swapchain_desc->BufferDesc.Width,
3968 "Got Width %u, expected %u.\n", surface_desc.Width, swapchain_desc->BufferDesc.Width);
3969 ok_(__FILE__, line)(surface_desc.Height == swapchain_desc->BufferDesc.Height,
3970 "Got Height %u, expected %u.\n", surface_desc.Height, swapchain_desc->BufferDesc.Height);
3971 ok_(__FILE__, line)(surface_desc.Format == swapchain_desc->BufferDesc.Format,
3972 "Got Format %#x, expected %#x.\n", surface_desc.Format, swapchain_desc->BufferDesc.Format);
3973 ok_(__FILE__, line)(surface_desc.SampleDesc.Count == 1,
3974 "Got unexpected SampleDesc.Count %u.\n", surface_desc.SampleDesc.Count);
3975 ok_(__FILE__, line)(!surface_desc.SampleDesc.Quality,
3976 "Got unexpected SampleDesc.Quality %u.\n", surface_desc.SampleDesc.Quality);
3979 #define check_texture_desc(a, b) check_texture_desc_(__LINE__, a, b)
3980 static void check_texture_desc_(unsigned int line, ID3D10Texture2D *texture,
3981 const DXGI_SWAP_CHAIN_DESC *swapchain_desc)
3983 D3D10_TEXTURE2D_DESC texture_desc;
3985 ID3D10Texture2D_GetDesc(texture, &texture_desc);
3986 ok_(__FILE__, line)(texture_desc.Width == swapchain_desc->BufferDesc.Width,
3987 "Got Width %u, expected %u.\n", texture_desc.Width, swapchain_desc->BufferDesc.Width);
3988 ok_(__FILE__, line)(texture_desc.Height == swapchain_desc->BufferDesc.Height,
3989 "Got Height %u, expected %u.\n", texture_desc.Height, swapchain_desc->BufferDesc.Height);
3990 ok_(__FILE__, line)(texture_desc.MipLevels == 1, "Got unexpected MipLevels %u.\n", texture_desc.MipLevels);
3991 ok_(__FILE__, line)(texture_desc.ArraySize == 1, "Got unexpected ArraySize %u.\n", texture_desc.ArraySize);
3992 ok_(__FILE__, line)(texture_desc.Format == swapchain_desc->BufferDesc.Format,
3993 "Got Format %#x, expected %#x.\n", texture_desc.Format, swapchain_desc->BufferDesc.Format);
3994 ok_(__FILE__, line)(texture_desc.SampleDesc.Count == 1,
3995 "Got unexpected SampleDesc.Count %u.\n", texture_desc.SampleDesc.Count);
3996 ok_(__FILE__, line)(!texture_desc.SampleDesc.Quality,
3997 "Got unexpected SampleDesc.Quality %u.\n", texture_desc.SampleDesc.Quality);
3998 ok_(__FILE__, line)(texture_desc.Usage == D3D10_USAGE_DEFAULT,
3999 "Got unexpected Usage %#x.\n", texture_desc.Usage);
4000 ok_(__FILE__, line)(texture_desc.BindFlags == D3D10_BIND_RENDER_TARGET,
4001 "Got unexpected BindFlags %#x.\n", texture_desc.BindFlags);
4002 ok_(__FILE__, line)(!texture_desc.CPUAccessFlags,
4003 "Got unexpected CPUAccessFlags %#x.\n", texture_desc.CPUAccessFlags);
4004 ok_(__FILE__, line)(!texture_desc.MiscFlags, "Got unexpected MiscFlags %#x.\n", texture_desc.MiscFlags);
4007 #define check_resource_desc(a, b) check_resource_desc_(__LINE__, a, b)
4008 static void check_resource_desc_(unsigned int line, ID3D12Resource *resource,
4009 const DXGI_SWAP_CHAIN_DESC *swapchain_desc)
4011 D3D12_RESOURCE_DESC resource_desc;
4013 resource_desc = ID3D12Resource_GetDesc(resource);
4014 ok_(__FILE__, line)(resource_desc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE2D,
4015 "Got unexpected Dimension %#x.\n", resource_desc.Dimension);
4016 ok_(__FILE__, line)(resource_desc.Width == swapchain_desc->BufferDesc.Width, "Got Width %s, expected %u.\n",
4017 wine_dbgstr_longlong(resource_desc.Width), swapchain_desc->BufferDesc.Width);
4018 ok_(__FILE__, line)(resource_desc.Height == swapchain_desc->BufferDesc.Height,
4019 "Got Height %u, expected %u.\n", resource_desc.Height, swapchain_desc->BufferDesc.Height);
4020 ok_(__FILE__, line)(resource_desc.DepthOrArraySize == 1,
4021 "Got unexpected DepthOrArraySize %u.\n", resource_desc.DepthOrArraySize);
4022 ok_(__FILE__, line)(resource_desc.MipLevels == 1,
4023 "Got unexpected MipLevels %u.\n", resource_desc.MipLevels);
4024 ok_(__FILE__, line)(resource_desc.Format == swapchain_desc->BufferDesc.Format,
4025 "Got Format %#x, expected %#x.\n", resource_desc.Format, swapchain_desc->BufferDesc.Format);
4026 ok_(__FILE__, line)(resource_desc.SampleDesc.Count == 1,
4027 "Got unexpected SampleDesc.Count %u.\n", resource_desc.SampleDesc.Count);
4028 ok_(__FILE__, line)(!resource_desc.SampleDesc.Quality,
4029 "Got unexpected SampleDesc.Quality %u.\n", resource_desc.SampleDesc.Quality);
4030 ok_(__FILE__, line)(resource_desc.Layout == D3D12_TEXTURE_LAYOUT_UNKNOWN,
4031 "Got unexpected Layout %#x.\n", resource_desc.Layout);
4034 static void test_swapchain_resize(IUnknown *device, BOOL is_d3d12)
4036 DXGI_SWAP_CHAIN_DESC swapchain_desc;
4037 DXGI_SWAP_EFFECT swap_effect;
4038 IDXGISwapChain3 *swapchain3;
4039 IUnknown *present_queue[2];
4040 IDXGISwapChain *swapchain;
4041 ID3D12Resource *resource;
4042 ID3D10Texture2D *texture;
4043 HRESULT hr, expected_hr;
4044 IDXGISurface *surface;
4045 IDXGIFactory *factory;
4046 RECT client_rect, r;
4047 UINT node_mask[2];
4048 ULONG refcount;
4049 HWND window;
4050 BOOL ret;
4052 get_factory(device, is_d3d12, &factory);
4054 window = create_window();
4055 ret = GetClientRect(window, &client_rect);
4056 ok(ret, "Failed to get client rect.\n");
4058 swap_effect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
4060 swapchain_desc.BufferDesc.Width = 640;
4061 swapchain_desc.BufferDesc.Height = 480;
4062 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
4063 swapchain_desc.BufferDesc.RefreshRate.Denominator = 1;
4064 swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
4065 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
4066 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
4067 swapchain_desc.SampleDesc.Count = 1;
4068 swapchain_desc.SampleDesc.Quality = 0;
4069 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
4070 swapchain_desc.BufferCount = 2;
4071 swapchain_desc.OutputWindow = window;
4072 swapchain_desc.Windowed = TRUE;
4073 swapchain_desc.SwapEffect = swap_effect;
4074 swapchain_desc.Flags = 0;
4076 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
4077 ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
4078 hr = IDXGISwapChain_GetBuffer(swapchain, 0, &IID_IDXGISurface, (void **)&surface);
4079 expected_hr = is_d3d12 ? E_NOINTERFACE : S_OK;
4080 ok(hr == expected_hr, "Got hr %#x, expected %#x.\n", hr, expected_hr);
4081 ok(!surface || hr == S_OK, "Got unexpected pointer %p.\n", surface);
4082 hr = IDXGISwapChain_GetBuffer(swapchain, 0, &IID_ID3D10Texture2D, (void **)&texture);
4083 ok(hr == expected_hr, "Got hr %#x, expected %#x.\n", hr, expected_hr);
4084 ok(!texture || hr == S_OK, "Got unexpected pointer %p.\n", texture);
4085 expected_hr = is_d3d12 ? S_OK : E_NOINTERFACE;
4086 hr = IDXGISwapChain_GetBuffer(swapchain, 0, &IID_ID3D12Resource, (void **)&resource);
4087 ok(hr == expected_hr, "Got hr %#x, expected %#x.\n", hr, expected_hr);
4088 ok(!resource || hr == S_OK, "Got unexpected pointer %p.\n", resource);
4090 ret = GetClientRect(window, &r);
4091 ok(ret, "Failed to get client rect.\n");
4092 ok(EqualRect(&r, &client_rect), "Got unexpected rect %s, expected %s.\n",
4093 wine_dbgstr_rect(&r), wine_dbgstr_rect(&client_rect));
4095 memset(&swapchain_desc, 0, sizeof(swapchain_desc));
4096 hr = IDXGISwapChain_GetDesc(swapchain, &swapchain_desc);
4097 ok(SUCCEEDED(hr), "Failed to get swapchain desc, hr %#x.\n", hr);
4098 ok(swapchain_desc.BufferDesc.Width == 640,
4099 "Got unexpected BufferDesc.Width %u.\n", swapchain_desc.BufferDesc.Width);
4100 ok(swapchain_desc.BufferDesc.Height == 480,
4101 "Got unexpected bufferDesc.Height %u.\n", swapchain_desc.BufferDesc.Height);
4102 ok(swapchain_desc.BufferDesc.RefreshRate.Numerator == 60,
4103 "Got unexpected BufferDesc.RefreshRate.Numerator %u.\n",
4104 swapchain_desc.BufferDesc.RefreshRate.Numerator);
4105 ok(swapchain_desc.BufferDesc.RefreshRate.Denominator == 1,
4106 "Got unexpected BufferDesc.RefreshRate.Denominator %u.\n",
4107 swapchain_desc.BufferDesc.RefreshRate.Denominator);
4108 ok(swapchain_desc.BufferDesc.Format == DXGI_FORMAT_R8G8B8A8_UNORM,
4109 "Got unexpected BufferDesc.Format %#x.\n", swapchain_desc.BufferDesc.Format);
4110 ok(swapchain_desc.BufferDesc.ScanlineOrdering == DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,
4111 "Got unexpected BufferDesc.ScanlineOrdering %#x.\n", swapchain_desc.BufferDesc.ScanlineOrdering);
4112 ok(swapchain_desc.BufferDesc.Scaling == DXGI_MODE_SCALING_UNSPECIFIED,
4113 "Got unexpected BufferDesc.Scaling %#x.\n", swapchain_desc.BufferDesc.Scaling);
4114 ok(swapchain_desc.SampleDesc.Count == 1,
4115 "Got unexpected SampleDesc.Count %u.\n", swapchain_desc.SampleDesc.Count);
4116 ok(!swapchain_desc.SampleDesc.Quality,
4117 "Got unexpected SampleDesc.Quality %u.\n", swapchain_desc.SampleDesc.Quality);
4118 ok(swapchain_desc.BufferUsage == DXGI_USAGE_RENDER_TARGET_OUTPUT,
4119 "Got unexpected BufferUsage %#x.\n", swapchain_desc.BufferUsage);
4120 ok(swapchain_desc.BufferCount == 2,
4121 "Got unexpected BufferCount %u.\n", swapchain_desc.BufferCount);
4122 ok(swapchain_desc.OutputWindow == window,
4123 "Got unexpected OutputWindow %p, expected %p.\n", swapchain_desc.OutputWindow, window);
4124 ok(swapchain_desc.Windowed,
4125 "Got unexpected Windowed %#x.\n", swapchain_desc.Windowed);
4126 ok(swapchain_desc.SwapEffect == swap_effect,
4127 "Got unexpected SwapEffect %#x.\n", swapchain_desc.SwapEffect);
4128 ok(!swapchain_desc.Flags,
4129 "Got unexpected Flags %#x.\n", swapchain_desc.Flags);
4131 if (surface)
4132 check_surface_desc(surface, &swapchain_desc);
4133 if (texture)
4134 check_texture_desc(texture, &swapchain_desc);
4135 if (resource)
4136 check_resource_desc(resource, &swapchain_desc);
4138 hr = IDXGISwapChain_ResizeBuffers(swapchain, 2, 320, 240, DXGI_FORMAT_B8G8R8A8_UNORM, 0);
4139 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
4141 ret = GetClientRect(window, &r);
4142 ok(ret, "Failed to get client rect.\n");
4143 ok(EqualRect(&r, &client_rect), "Got unexpected rect %s, expected %s.\n",
4144 wine_dbgstr_rect(&r), wine_dbgstr_rect(&client_rect));
4146 memset(&swapchain_desc, 0, sizeof(swapchain_desc));
4147 hr = IDXGISwapChain_GetDesc(swapchain, &swapchain_desc);
4148 ok(SUCCEEDED(hr), "Failed to get swapchain desc, hr %#x.\n", hr);
4149 ok(swapchain_desc.BufferDesc.Width == 640,
4150 "Got unexpected BufferDesc.Width %u.\n", swapchain_desc.BufferDesc.Width);
4151 ok(swapchain_desc.BufferDesc.Height == 480,
4152 "Got unexpected bufferDesc.Height %u.\n", swapchain_desc.BufferDesc.Height);
4153 ok(swapchain_desc.BufferDesc.RefreshRate.Numerator == 60,
4154 "Got unexpected BufferDesc.RefreshRate.Numerator %u.\n",
4155 swapchain_desc.BufferDesc.RefreshRate.Numerator);
4156 ok(swapchain_desc.BufferDesc.RefreshRate.Denominator == 1,
4157 "Got unexpected BufferDesc.RefreshRate.Denominator %u.\n",
4158 swapchain_desc.BufferDesc.RefreshRate.Denominator);
4159 ok(swapchain_desc.BufferDesc.Format == DXGI_FORMAT_R8G8B8A8_UNORM,
4160 "Got unexpected BufferDesc.Format %#x.\n", swapchain_desc.BufferDesc.Format);
4161 ok(swapchain_desc.BufferDesc.ScanlineOrdering == DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,
4162 "Got unexpected BufferDesc.ScanlineOrdering %#x.\n", swapchain_desc.BufferDesc.ScanlineOrdering);
4163 ok(swapchain_desc.BufferDesc.Scaling == DXGI_MODE_SCALING_UNSPECIFIED,
4164 "Got unexpected BufferDesc.Scaling %#x.\n", swapchain_desc.BufferDesc.Scaling);
4165 ok(swapchain_desc.SampleDesc.Count == 1,
4166 "Got unexpected SampleDesc.Count %u.\n", swapchain_desc.SampleDesc.Count);
4167 ok(!swapchain_desc.SampleDesc.Quality,
4168 "Got unexpected SampleDesc.Quality %u.\n", swapchain_desc.SampleDesc.Quality);
4169 ok(swapchain_desc.BufferUsage == DXGI_USAGE_RENDER_TARGET_OUTPUT,
4170 "Got unexpected BufferUsage %#x.\n", swapchain_desc.BufferUsage);
4171 ok(swapchain_desc.BufferCount == 2,
4172 "Got unexpected BufferCount %u.\n", swapchain_desc.BufferCount);
4173 ok(swapchain_desc.OutputWindow == window,
4174 "Got unexpected OutputWindow %p, expected %p.\n", swapchain_desc.OutputWindow, window);
4175 ok(swapchain_desc.Windowed,
4176 "Got unexpected Windowed %#x.\n", swapchain_desc.Windowed);
4177 ok(swapchain_desc.SwapEffect == swap_effect,
4178 "Got unexpected SwapEffect %#x.\n", swapchain_desc.SwapEffect);
4179 ok(!swapchain_desc.Flags,
4180 "Got unexpected Flags %#x.\n", swapchain_desc.Flags);
4182 if (surface)
4184 check_surface_desc(surface, &swapchain_desc);
4185 IDXGISurface_Release(surface);
4187 if (texture)
4189 check_texture_desc(texture, &swapchain_desc);
4190 ID3D10Texture2D_Release(texture);
4192 if (resource)
4194 check_resource_desc(resource, &swapchain_desc);
4195 ID3D12Resource_Release(resource);
4198 hr = IDXGISwapChain_ResizeBuffers(swapchain, 2, 320, 240, DXGI_FORMAT_B8G8R8A8_UNORM, 0);
4199 ok(hr == S_OK, "Failed to resize buffers, hr %#x.\n", hr);
4200 hr = IDXGISwapChain_GetBuffer(swapchain, 0, &IID_IDXGISurface, (void **)&surface);
4201 expected_hr = is_d3d12 ? E_NOINTERFACE : S_OK;
4202 ok(hr == expected_hr, "Got hr %#x, expected %#x.\n", hr, expected_hr);
4203 ok(!surface || hr == S_OK, "Got unexpected pointer %p.\n", surface);
4204 hr = IDXGISwapChain_GetBuffer(swapchain, 0, &IID_ID3D10Texture2D, (void **)&texture);
4205 ok(hr == expected_hr, "Got hr %#x, expected %#x.\n", hr, expected_hr);
4206 ok(!texture || hr == S_OK, "Got unexpected pointer %p.\n", texture);
4207 expected_hr = is_d3d12 ? S_OK : E_NOINTERFACE;
4208 hr = IDXGISwapChain_GetBuffer(swapchain, 0, &IID_ID3D12Resource, (void **)&resource);
4209 ok(hr == expected_hr, "Got hr %#x, expected %#x.\n", hr, expected_hr);
4210 ok(!resource || hr == S_OK, "Got unexpected pointer %p.\n", resource);
4212 ret = GetClientRect(window, &r);
4213 ok(ret, "Failed to get client rect.\n");
4214 ok(EqualRect(&r, &client_rect), "Got unexpected rect %s, expected %s.\n",
4215 wine_dbgstr_rect(&r), wine_dbgstr_rect(&client_rect));
4217 memset(&swapchain_desc, 0, sizeof(swapchain_desc));
4218 hr = IDXGISwapChain_GetDesc(swapchain, &swapchain_desc);
4219 ok(SUCCEEDED(hr), "Failed to get swapchain desc, hr %#x.\n", hr);
4220 ok(swapchain_desc.BufferDesc.Width == 320,
4221 "Got unexpected BufferDesc.Width %u.\n", swapchain_desc.BufferDesc.Width);
4222 ok(swapchain_desc.BufferDesc.Height == 240,
4223 "Got unexpected bufferDesc.Height %u.\n", swapchain_desc.BufferDesc.Height);
4224 ok(swapchain_desc.BufferDesc.RefreshRate.Numerator == 60,
4225 "Got unexpected BufferDesc.RefreshRate.Numerator %u.\n",
4226 swapchain_desc.BufferDesc.RefreshRate.Numerator);
4227 ok(swapchain_desc.BufferDesc.RefreshRate.Denominator == 1,
4228 "Got unexpected BufferDesc.RefreshRate.Denominator %u.\n",
4229 swapchain_desc.BufferDesc.RefreshRate.Denominator);
4230 ok(swapchain_desc.BufferDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM,
4231 "Got unexpected BufferDesc.Format %#x.\n", swapchain_desc.BufferDesc.Format);
4232 ok(swapchain_desc.BufferDesc.ScanlineOrdering == DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,
4233 "Got unexpected BufferDesc.ScanlineOrdering %#x.\n", swapchain_desc.BufferDesc.ScanlineOrdering);
4234 ok(swapchain_desc.BufferDesc.Scaling == DXGI_MODE_SCALING_UNSPECIFIED,
4235 "Got unexpected BufferDesc.Scaling %#x.\n", swapchain_desc.BufferDesc.Scaling);
4236 ok(swapchain_desc.SampleDesc.Count == 1,
4237 "Got unexpected SampleDesc.Count %u.\n", swapchain_desc.SampleDesc.Count);
4238 ok(!swapchain_desc.SampleDesc.Quality,
4239 "Got unexpected SampleDesc.Quality %u.\n", swapchain_desc.SampleDesc.Quality);
4240 ok(swapchain_desc.BufferUsage == DXGI_USAGE_RENDER_TARGET_OUTPUT,
4241 "Got unexpected BufferUsage %#x.\n", swapchain_desc.BufferUsage);
4242 ok(swapchain_desc.BufferCount == 2,
4243 "Got unexpected BufferCount %u.\n", swapchain_desc.BufferCount);
4244 ok(swapchain_desc.OutputWindow == window,
4245 "Got unexpected OutputWindow %p, expected %p.\n", swapchain_desc.OutputWindow, window);
4246 ok(swapchain_desc.Windowed,
4247 "Got unexpected Windowed %#x.\n", swapchain_desc.Windowed);
4248 ok(swapchain_desc.SwapEffect == swap_effect,
4249 "Got unexpected SwapEffect %#x.\n", swapchain_desc.SwapEffect);
4250 ok(!swapchain_desc.Flags,
4251 "Got unexpected Flags %#x.\n", swapchain_desc.Flags);
4253 if (surface)
4255 check_surface_desc(surface, &swapchain_desc);
4256 IDXGISurface_Release(surface);
4258 if (texture)
4260 check_texture_desc(texture, &swapchain_desc);
4261 ID3D10Texture2D_Release(texture);
4263 if (resource)
4265 check_resource_desc(resource, &swapchain_desc);
4266 ID3D12Resource_Release(resource);
4269 hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
4270 ok(hr == S_OK, "Failed to resize buffers, hr %#x.\n", hr);
4272 memset(&swapchain_desc, 0, sizeof(swapchain_desc));
4273 hr = IDXGISwapChain_GetDesc(swapchain, &swapchain_desc);
4274 ok(SUCCEEDED(hr), "Failed to get swapchain desc, hr %#x.\n", hr);
4275 ok(swapchain_desc.BufferDesc.Width == client_rect.right - client_rect.left,
4276 "Got unexpected BufferDesc.Width %u, expected %u.\n",
4277 swapchain_desc.BufferDesc.Width, client_rect.right - client_rect.left);
4278 ok(swapchain_desc.BufferDesc.Height == client_rect.bottom - client_rect.top,
4279 "Got unexpected bufferDesc.Height %u, expected %u.\n",
4280 swapchain_desc.BufferDesc.Height, client_rect.bottom - client_rect.top);
4281 ok(swapchain_desc.BufferDesc.RefreshRate.Numerator == 60,
4282 "Got unexpected BufferDesc.RefreshRate.Numerator %u.\n",
4283 swapchain_desc.BufferDesc.RefreshRate.Numerator);
4284 ok(swapchain_desc.BufferDesc.RefreshRate.Denominator == 1,
4285 "Got unexpected BufferDesc.RefreshRate.Denominator %u.\n",
4286 swapchain_desc.BufferDesc.RefreshRate.Denominator);
4287 ok(swapchain_desc.BufferDesc.Format == DXGI_FORMAT_B8G8R8A8_UNORM,
4288 "Got unexpected BufferDesc.Format %#x.\n", swapchain_desc.BufferDesc.Format);
4289 ok(swapchain_desc.BufferDesc.ScanlineOrdering == DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,
4290 "Got unexpected BufferDesc.ScanlineOrdering %#x.\n", swapchain_desc.BufferDesc.ScanlineOrdering);
4291 ok(swapchain_desc.BufferDesc.Scaling == DXGI_MODE_SCALING_UNSPECIFIED,
4292 "Got unexpected BufferDesc.Scaling %#x.\n", swapchain_desc.BufferDesc.Scaling);
4293 ok(swapchain_desc.SampleDesc.Count == 1,
4294 "Got unexpected SampleDesc.Count %u.\n", swapchain_desc.SampleDesc.Count);
4295 ok(!swapchain_desc.SampleDesc.Quality,
4296 "Got unexpected SampleDesc.Quality %u.\n", swapchain_desc.SampleDesc.Quality);
4297 ok(swapchain_desc.BufferUsage == DXGI_USAGE_RENDER_TARGET_OUTPUT,
4298 "Got unexpected BufferUsage %#x.\n", swapchain_desc.BufferUsage);
4299 ok(swapchain_desc.BufferCount == 2,
4300 "Got unexpected BufferCount %u.\n", swapchain_desc.BufferCount);
4301 ok(swapchain_desc.OutputWindow == window,
4302 "Got unexpected OutputWindow %p, expected %p.\n", swapchain_desc.OutputWindow, window);
4303 ok(swapchain_desc.Windowed,
4304 "Got unexpected Windowed %#x.\n", swapchain_desc.Windowed);
4305 ok(swapchain_desc.SwapEffect == swap_effect,
4306 "Got unexpected SwapEffect %#x.\n", swapchain_desc.SwapEffect);
4307 ok(!swapchain_desc.Flags,
4308 "Got unexpected Flags %#x.\n", swapchain_desc.Flags);
4310 node_mask[0] = 1;
4311 node_mask[1] = 1;
4312 present_queue[0] = device;
4313 present_queue[1] = device;
4314 if (IDXGISwapChain_QueryInterface(swapchain, &IID_IDXGISwapChain3, (void **)&swapchain3) == E_NOINTERFACE)
4316 skip("IDXGISwapChain3 is not supported.\n");
4318 else if (!is_d3d12)
4320 hr = IDXGISwapChain3_ResizeBuffers1(swapchain3, 2, 320, 240, DXGI_FORMAT_B8G8R8A8_UNORM, 0, node_mask, present_queue);
4321 ok(hr == DXGI_ERROR_INVALID_CALL, "Expected DXGI_ERROR_INVALID_CALL, got hr %#x.\n", hr);
4322 IDXGISwapChain3_Release(swapchain3);
4324 else
4326 hr = IDXGISwapChain3_ResizeBuffers1(swapchain3, 2, 320, 240, DXGI_FORMAT_B8G8R8A8_UNORM, 0, node_mask, present_queue);
4327 ok(hr == S_OK, "Failed to resize buffers, hr %#x.\n", hr);
4328 hr = IDXGISwapChain3_ResizeBuffers1(swapchain3, 2, 320, 240, DXGI_FORMAT_B8G8R8A8_UNORM, 0, NULL, present_queue);
4329 ok(hr == DXGI_ERROR_INVALID_CALL, "Expected DXGI_ERROR_INVALID_CALL, got hr %#x.\n", hr);
4330 hr = IDXGISwapChain3_ResizeBuffers1(swapchain3, 2, 320, 240, DXGI_FORMAT_B8G8R8A8_UNORM, 0, NULL, NULL);
4331 ok(hr == DXGI_ERROR_INVALID_CALL, "Expected DXGI_ERROR_INVALID_CALL, got hr %#x.\n", hr);
4332 hr = IDXGISwapChain3_ResizeBuffers1(swapchain3, 0, 320, 240, DXGI_FORMAT_B8G8R8A8_UNORM, 0, NULL, NULL);
4333 ok(hr == DXGI_ERROR_INVALID_CALL, "Expected DXGI_ERROR_INVALID_CALL, got hr %#x.\n", hr);
4334 node_mask[0] = 2;
4335 node_mask[1] = 2;
4336 hr = IDXGISwapChain3_ResizeBuffers1(swapchain3, 2, 320, 240, DXGI_FORMAT_B8G8R8A8_UNORM, 0, node_mask, present_queue);
4337 ok(hr == DXGI_ERROR_INVALID_CALL, "Expected DXGI_ERROR_INVALID_CALL, got hr %#x.\n", hr);
4338 /* Windows validates node masks even when the buffer count is zero. It defaults to the current buffer count.
4339 * NULL queues cause some Windows versions to crash. */
4340 hr = IDXGISwapChain3_ResizeBuffers1(swapchain3, 0, 320, 240, DXGI_FORMAT_B8G8R8A8_UNORM, 0, node_mask, present_queue);
4341 ok(hr == DXGI_ERROR_INVALID_CALL, "Expected DXGI_ERROR_INVALID_CALL, got hr %#x.\n", hr);
4342 IDXGISwapChain3_Release(swapchain3);
4345 IDXGISwapChain_Release(swapchain);
4346 DestroyWindow(window);
4347 refcount = IDXGIFactory_Release(factory);
4348 ok(refcount == !is_d3d12, "Got unexpected refcount %u.\n", refcount);
4351 static void test_swapchain_parameters(void)
4353 DXGI_USAGE usage, expected_usage, broken_usage;
4354 D3D10_TEXTURE2D_DESC d3d10_texture_desc;
4355 D3D11_TEXTURE2D_DESC d3d11_texture_desc;
4356 unsigned int expected_bind_flags;
4357 ID3D10Texture2D *d3d10_texture;
4358 ID3D11Texture2D *d3d11_texture;
4359 DXGI_SWAP_CHAIN_DESC desc;
4360 IDXGISwapChain *swapchain;
4361 IDXGIResource *resource;
4362 IDXGIAdapter *adapter;
4363 IDXGIFactory *factory;
4364 IDXGIDevice *device;
4365 unsigned int i, j;
4366 ULONG refcount;
4367 IUnknown *obj;
4368 HWND window;
4369 HRESULT hr;
4371 static const struct
4373 BOOL windowed;
4374 UINT buffer_count;
4375 DXGI_SWAP_EFFECT swap_effect;
4376 HRESULT hr, vista_hr;
4377 UINT highest_accessible_buffer;
4379 tests[] =
4381 /* 0 */
4382 {TRUE, 1, DXGI_SWAP_EFFECT_DISCARD, S_OK, S_OK, 0},
4383 {TRUE, 2, DXGI_SWAP_EFFECT_DISCARD, S_OK, S_OK, 0},
4384 {TRUE, 1, DXGI_SWAP_EFFECT_SEQUENTIAL, S_OK, S_OK, 0},
4385 {TRUE, 2, DXGI_SWAP_EFFECT_SEQUENTIAL, S_OK, S_OK, 1},
4386 {TRUE, 3, DXGI_SWAP_EFFECT_SEQUENTIAL, S_OK, S_OK, 2},
4387 /* 5 */
4388 {TRUE, 0, 2 /* undefined */, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4389 {TRUE, 1, 2 /* undefined */, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4390 {TRUE, 2, 2 /* undefined */, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4391 {TRUE, 0, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4392 {TRUE, 1, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4393 /* 10 */
4394 {TRUE, 2, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, S_OK, DXGI_ERROR_INVALID_CALL, 1},
4395 {TRUE, 3, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, S_OK, DXGI_ERROR_INVALID_CALL, 2},
4396 {TRUE, 0, DXGI_SWAP_EFFECT_FLIP_DISCARD, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4397 {TRUE, 1, DXGI_SWAP_EFFECT_FLIP_DISCARD, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4398 {TRUE, 2, DXGI_SWAP_EFFECT_FLIP_DISCARD, S_OK, DXGI_ERROR_INVALID_CALL, 0},
4399 /* 15 */
4400 {TRUE, 0, 5 /* undefined */, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4401 {TRUE, 1, 5 /* undefined */, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4402 {TRUE, 2, 5 /* undefined */, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4403 {TRUE, 16, DXGI_SWAP_EFFECT_DISCARD, S_OK, S_OK, 0},
4404 {TRUE, 16, DXGI_SWAP_EFFECT_SEQUENTIAL, S_OK, S_OK, 15},
4405 /* 20 */
4406 {TRUE, 16, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, S_OK, DXGI_ERROR_INVALID_CALL, 15},
4407 {TRUE, 16, DXGI_SWAP_EFFECT_FLIP_DISCARD, S_OK, DXGI_ERROR_INVALID_CALL, 0},
4408 {TRUE, 17, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4409 {FALSE, 1, DXGI_SWAP_EFFECT_DISCARD, S_OK, S_OK, 0},
4410 {FALSE, 2, DXGI_SWAP_EFFECT_DISCARD, S_OK, S_OK, 0},
4411 /* 25 */
4412 {FALSE, 1, DXGI_SWAP_EFFECT_SEQUENTIAL, S_OK, S_OK, 0},
4413 {FALSE, 2, DXGI_SWAP_EFFECT_SEQUENTIAL, S_OK, S_OK, 1},
4414 {FALSE, 3, DXGI_SWAP_EFFECT_SEQUENTIAL, S_OK, S_OK, 2},
4415 {FALSE, 0, 2 /* undefined */, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4416 {FALSE, 1, 2 /* undefined */, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4417 /* 30 */
4418 {FALSE, 2, 2 /* undefined */, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4419 {FALSE, 0, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4420 {FALSE, 1, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4421 {FALSE, 2, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, S_OK, DXGI_ERROR_INVALID_CALL, 1},
4422 {FALSE, 3, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, S_OK, DXGI_ERROR_INVALID_CALL, 2},
4423 /* 35 */
4424 {FALSE, 0, DXGI_SWAP_EFFECT_FLIP_DISCARD, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4425 {FALSE, 1, DXGI_SWAP_EFFECT_FLIP_DISCARD, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4426 {FALSE, 2, DXGI_SWAP_EFFECT_FLIP_DISCARD, S_OK, DXGI_ERROR_INVALID_CALL, 0},
4427 {FALSE, 0, 5 /* undefined */, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4428 {FALSE, 1, 5 /* undefined */, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4429 /* 40 */
4430 {FALSE, 2, 5 /* undefined */, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4431 {FALSE, 16, DXGI_SWAP_EFFECT_DISCARD, S_OK, S_OK, 0},
4432 {FALSE, 16, DXGI_SWAP_EFFECT_SEQUENTIAL, S_OK, S_OK, 15},
4433 {FALSE, 16, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, S_OK, DXGI_ERROR_INVALID_CALL, 15},
4434 {FALSE, 17, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4435 /* 45 */
4436 {FALSE, 17, DXGI_SWAP_EFFECT_FLIP_DISCARD, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4438 /* The following test fails on Nvidia with E_OUTOFMEMORY and leaks device references in the
4439 * process. Disable it for now.
4440 {FALSE, 16, DXGI_SWAP_EFFECT_FLIP_DISCARD, S_OK, DXGI_ERROR_INVALID_CALL, 0},
4443 /* The following tests crash on Win10 1909
4444 {TRUE, 0, DXGI_SWAP_EFFECT_DISCARD, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4445 {TRUE, 0, DXGI_SWAP_EFFECT_SEQUENTIAL, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4446 {TRUE, 17, DXGI_SWAP_EFFECT_DISCARD, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4447 {TRUE, 17, DXGI_SWAP_EFFECT_SEQUENTIAL, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4448 {TRUE, 17, DXGI_SWAP_EFFECT_DISCARD, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4449 {FALSE, 0, DXGI_SWAP_EFFECT_DISCARD, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4450 {FALSE, 0, DXGI_SWAP_EFFECT_SEQUENTIAL, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4451 {FALSE, 17, DXGI_SWAP_EFFECT_DISCARD, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4452 {FALSE, 17, DXGI_SWAP_EFFECT_SEQUENTIAL, DXGI_ERROR_INVALID_CALL, DXGI_ERROR_INVALID_CALL, 0},
4455 static const DXGI_USAGE usage_tests[] =
4458 DXGI_USAGE_BACK_BUFFER,
4459 DXGI_USAGE_SHADER_INPUT,
4460 DXGI_USAGE_RENDER_TARGET_OUTPUT,
4461 DXGI_USAGE_DISCARD_ON_PRESENT,
4462 DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER,
4463 DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_DISCARD_ON_PRESENT,
4464 DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_DISCARD_ON_PRESENT,
4465 DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT,
4466 DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_DISCARD_ON_PRESENT,
4469 if (!(device = create_device(0)))
4471 skip("Failed to create device.\n");
4472 return;
4474 window = create_window();
4476 hr = IDXGIDevice_QueryInterface(device, &IID_IUnknown, (void **)&obj);
4477 ok(hr == S_OK, "IDXGIDevice does not implement IUnknown.\n");
4479 hr = IDXGIDevice_GetAdapter(device, &adapter);
4480 ok(hr == S_OK, "Failed to get adapter, hr %#x.\n", hr);
4481 hr = IDXGIAdapter_GetParent(adapter, &IID_IDXGIFactory, (void **)&factory);
4482 ok(hr == S_OK, "Failed to get parent, hr %#x.\n", hr);
4483 IDXGIAdapter_Release(adapter);
4485 for (i = 0; i < ARRAY_SIZE(tests); ++i)
4487 memset(&desc, 0, sizeof(desc));
4488 desc.BufferDesc.Width = registry_mode.dmPelsWidth;
4489 desc.BufferDesc.Height = registry_mode.dmPelsHeight;
4490 desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
4491 desc.SampleDesc.Count = 1;
4492 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
4493 desc.OutputWindow = window;
4495 desc.Windowed = tests[i].windowed;
4496 desc.BufferCount = tests[i].buffer_count;
4497 desc.SwapEffect = tests[i].swap_effect;
4499 hr = IDXGIFactory_CreateSwapChain(factory, obj, &desc, &swapchain);
4500 ok(hr == tests[i].hr || broken(hr == tests[i].vista_hr)
4501 || (SUCCEEDED(tests[i].hr) && hr == DXGI_STATUS_OCCLUDED),
4502 "Got unexpected hr %#x, test %u.\n", hr, i);
4503 if (FAILED(hr))
4504 continue;
4506 hr = IDXGISwapChain_GetBuffer(swapchain, 0, &IID_IDXGIResource, (void **)&resource);
4507 todo_wine ok(SUCCEEDED(hr), "GetBuffer(0) failed, hr %#x, test %u.\n", hr, i);
4508 if (FAILED(hr))
4510 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
4511 ok(SUCCEEDED(hr), "SetFullscreenState failed, hr %#x.\n", hr);
4513 IDXGISwapChain_Release(swapchain);
4514 continue;
4517 expected_usage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER;
4518 hr = IDXGIResource_GetUsage(resource, &usage);
4519 ok(SUCCEEDED(hr), "Failed to get resource usage, hr %#x, test %u.\n", hr, i);
4520 ok((usage & expected_usage) == expected_usage, "Got usage %x, expected %x, test %u.\n",
4521 usage, expected_usage, i);
4523 IDXGIResource_Release(resource);
4525 hr = IDXGISwapChain_GetDesc(swapchain, &desc);
4526 ok(SUCCEEDED(hr), "Failed to get swapchain desc, hr %#x.\n", hr);
4528 for (j = 1; j <= tests[i].highest_accessible_buffer; j++)
4530 hr = IDXGISwapChain_GetBuffer(swapchain, j, &IID_IDXGIResource, (void **)&resource);
4531 ok(SUCCEEDED(hr), "GetBuffer(%u) failed, hr %#x, test %u.\n", hr, i, j);
4533 /* Buffers > 0 are supposed to be read only. This is the case except that in
4534 * fullscreen mode on Windows <= 8 the last backbuffer (BufferCount - 1) is
4535 * writable. This is not the case if an unsupported refresh rate is passed
4536 * for some reason, probably because the invalid refresh rate triggers a
4537 * kinda-sorta windowed mode.
4539 * On Windows 10 all buffers > 0 are read-only. Mark the earlier behavior
4540 * broken.
4542 * This last buffer acts as a shadow frontbuffer. Writing to it doesn't show
4543 * the draw on the screen right away (Aero on or off doesn't matter), but
4544 * Present with DXGI_PRESENT_DO_NOT_SEQUENCE will show the modifications.
4546 * Note that if the application doesn't have focus creating a fullscreen
4547 * swapchain returns DXGI_STATUS_OCCLUDED and we get a windowed swapchain,
4548 * so use the Windowed property of the swapchain that was actually created. */
4549 expected_usage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_READ_ONLY;
4550 broken_usage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER;
4552 if (desc.Windowed || j < tests[i].highest_accessible_buffer)
4553 broken_usage |= DXGI_USAGE_READ_ONLY;
4555 hr = IDXGIResource_GetUsage(resource, &usage);
4556 ok(SUCCEEDED(hr), "Failed to get resource usage, hr %#x, test %u, buffer %u.\n", hr, i, j);
4557 ok(usage == expected_usage || broken(usage == broken_usage),
4558 "Got usage %x, expected %x, test %u, buffer %u.\n",
4559 usage, expected_usage, i, j);
4561 IDXGIResource_Release(resource);
4563 hr = IDXGISwapChain_GetBuffer(swapchain, j, &IID_IDXGIResource, (void **)&resource);
4564 ok(hr == DXGI_ERROR_INVALID_CALL, "GetBuffer(%u) returned unexpected hr %#x, test %u.\n", j, hr, i);
4566 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
4567 ok(SUCCEEDED(hr), "SetFullscreenState failed, hr %#x.\n", hr);
4569 IDXGISwapChain_Release(swapchain);
4572 for (i = 0; i < ARRAY_SIZE(usage_tests); ++i)
4574 usage = usage_tests[i];
4576 memset(&desc, 0, sizeof(desc));
4577 desc.BufferDesc.Width = registry_mode.dmPelsWidth;
4578 desc.BufferDesc.Height = registry_mode.dmPelsHeight;
4579 desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
4580 desc.SampleDesc.Count = 1;
4581 desc.BufferUsage = usage;
4582 desc.BufferCount = 1;
4583 desc.OutputWindow = window;
4584 desc.Windowed = TRUE;
4585 desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
4586 hr = IDXGIFactory_CreateSwapChain(factory, obj, &desc, &swapchain);
4587 ok(hr == S_OK, "Got unexpected hr %#x, test %u.\n", hr, i);
4589 hr = IDXGISwapChain_GetDesc(swapchain, &desc);
4590 ok(hr == S_OK, "Failed to get swapchain desc, hr %#x, test %u.\n", hr, i);
4591 todo_wine_if(usage & ~(DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT))
4592 ok(desc.BufferUsage == usage, "Got usage %#x, expected %#x, test %u.\n", desc.BufferUsage, usage, i);
4594 expected_bind_flags = 0;
4595 if (usage & DXGI_USAGE_RENDER_TARGET_OUTPUT)
4596 expected_bind_flags |= D3D11_BIND_RENDER_TARGET;
4597 if (usage & DXGI_USAGE_SHADER_INPUT)
4598 expected_bind_flags |= D3D11_BIND_SHADER_RESOURCE;
4600 hr = IDXGISwapChain_GetBuffer(swapchain, 0, &IID_ID3D10Texture2D, (void **)&d3d10_texture);
4601 ok(hr == S_OK, "Failed to get d3d10 texture, hr %#x, test %u.\n", hr, i);
4602 ID3D10Texture2D_GetDesc(d3d10_texture, &d3d10_texture_desc);
4603 ok(d3d10_texture_desc.BindFlags == expected_bind_flags,
4604 "Got d3d10 bind flags %#x, expected %#x, test %u.\n",
4605 d3d10_texture_desc.BindFlags, expected_bind_flags, i);
4606 ID3D10Texture2D_Release(d3d10_texture);
4608 hr = IDXGISwapChain_GetBuffer(swapchain, 0, &IID_ID3D11Texture2D, (void **)&d3d11_texture);
4609 ok(hr == S_OK || broken(hr == E_NOINTERFACE), "Failed to get d3d11 texture, hr %#x, test %u.\n", hr, i);
4610 if (SUCCEEDED(hr))
4612 ID3D11Texture2D_GetDesc(d3d11_texture, &d3d11_texture_desc);
4613 ok(d3d11_texture_desc.BindFlags == expected_bind_flags,
4614 "Got d3d11 bind flags %#x, expected %#x, test %u.\n",
4615 d3d11_texture_desc.BindFlags, expected_bind_flags, i);
4616 ID3D11Texture2D_Release(d3d11_texture);
4619 hr = IDXGISwapChain_GetBuffer(swapchain, 0, &IID_IDXGIResource, (void **)&resource);
4620 todo_wine ok(hr == S_OK, "Failed to get buffer, hr %#x, test %u.\n", hr, i);
4621 if (FAILED(hr))
4623 IDXGISwapChain_Release(swapchain);
4624 continue;
4626 expected_usage = usage | DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_DISCARD_ON_PRESENT;
4627 hr = IDXGIResource_GetUsage(resource, &usage);
4628 ok(hr == S_OK, "Failed to get resource usage, hr %#x, test %u.\n", hr, i);
4629 ok(usage == expected_usage, "Got usage %x, expected %x, test %u.\n", usage, expected_usage, i);
4630 IDXGIResource_Release(resource);
4632 IDXGISwapChain_Release(swapchain);
4635 /* multisampling */
4636 memset(&desc, 0, sizeof(desc));
4637 desc.BufferDesc.Width = registry_mode.dmPelsWidth;
4638 desc.BufferDesc.Height = registry_mode.dmPelsHeight;
4639 desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
4640 desc.SampleDesc.Count = 4;
4641 desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
4642 desc.BufferCount = 4;
4643 desc.OutputWindow = window;
4644 desc.Windowed = TRUE;
4645 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
4646 hr = IDXGIFactory_CreateSwapChain(factory, obj, &desc, &swapchain);
4647 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
4648 desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
4649 hr = IDXGIFactory_CreateSwapChain(factory, obj, &desc, &swapchain);
4650 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
4651 if (check_multisample_quality_levels(device, desc.BufferDesc.Format, desc.SampleDesc.Count))
4653 desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
4654 hr = IDXGIFactory_CreateSwapChain(factory, obj, &desc, &swapchain);
4655 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
4656 IDXGISwapChain_Release(swapchain);
4657 desc.SwapEffect = DXGI_SWAP_EFFECT_SEQUENTIAL;
4658 hr = IDXGIFactory_CreateSwapChain(factory, obj, &desc, &swapchain);
4659 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
4660 IDXGISwapChain_Release(swapchain);
4662 else
4664 skip("Multisampling not supported for DXGI_FORMAT_R8G8B8A8_UNORM.\n");
4667 IDXGIFactory_Release(factory);
4668 IUnknown_Release(obj);
4669 refcount = IDXGIDevice_Release(device);
4670 ok(!refcount, "Device has %u references left.\n", refcount);
4671 DestroyWindow(window);
4674 static void test_swapchain_present(IUnknown *device, BOOL is_d3d12)
4676 static const DWORD flags[] = {0, DXGI_PRESENT_TEST};
4677 DXGI_SWAP_CHAIN_DESC swapchain_desc;
4678 IDXGISwapChain *swapchain;
4679 IDXGIFactory *factory;
4680 IDXGIOutput *output;
4681 BOOL fullscreen;
4682 unsigned int i;
4683 ULONG refcount;
4684 HRESULT hr;
4686 get_factory(device, is_d3d12, &factory);
4688 swapchain_desc.BufferDesc.Width = 800;
4689 swapchain_desc.BufferDesc.Height = 600;
4690 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
4691 swapchain_desc.BufferDesc.RefreshRate.Denominator = 60;
4692 swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
4693 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
4694 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
4695 swapchain_desc.SampleDesc.Count = 1;
4696 swapchain_desc.SampleDesc.Quality = 0;
4697 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
4698 swapchain_desc.BufferCount = is_d3d12 ? 2 : 1;
4699 swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0);
4700 swapchain_desc.Windowed = TRUE;
4701 swapchain_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
4702 swapchain_desc.Flags = 0;
4704 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
4705 ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
4707 for (i = 0; i < 10; ++i)
4709 hr = IDXGISwapChain_Present(swapchain, i, 0);
4710 ok(hr == (i <= 4 ? S_OK : DXGI_ERROR_INVALID_CALL),
4711 "Got unexpected hr %#x for sync interval %u.\n", hr, i);
4713 hr = IDXGISwapChain_Present(swapchain, 0, 0);
4714 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
4716 for (i = 0; i < ARRAY_SIZE(flags); ++i)
4718 HWND occluding_window = CreateWindowA("static", "occluding_window",
4719 WS_POPUP | WS_VISIBLE, 0, 0, 400, 200, NULL, NULL, NULL, NULL);
4721 /* Another window covers the swapchain window. Not reported as occluded. */
4722 hr = IDXGISwapChain_Present(swapchain, 0, flags[i]);
4723 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4725 /* Minimised window. */
4726 ShowWindow(swapchain_desc.OutputWindow, SW_MINIMIZE);
4727 hr = IDXGISwapChain_Present(swapchain, 0, flags[i]);
4728 ok(hr == (is_d3d12 ? S_OK : DXGI_STATUS_OCCLUDED), "Test %u: Got unexpected hr %#x.\n", i, hr);
4729 ShowWindow(swapchain_desc.OutputWindow, SW_NORMAL);
4731 /* Hidden window. */
4732 ShowWindow(swapchain_desc.OutputWindow, SW_HIDE);
4733 hr = IDXGISwapChain_Present(swapchain, 0, flags[i]);
4734 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4735 ShowWindow(swapchain_desc.OutputWindow, SW_SHOW);
4736 DestroyWindow(occluding_window);
4738 /* Test that IDXGIOutput_ReleaseOwnership() makes the swapchain exit
4739 * fullscreen. */
4740 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
4741 /* DXGI_ERROR_NOT_CURRENTLY_AVAILABLE on some machines.
4742 * DXGI_ERROR_UNSUPPORTED on the Windows 7 testbot. */
4743 if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE || broken(hr == DXGI_ERROR_UNSUPPORTED))
4745 skip("Test %u: Could not change fullscreen state.\n", i);
4746 continue;
4748 flush_events();
4749 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4750 hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
4751 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4752 hr = IDXGISwapChain_Present(swapchain, 0, flags[i]);
4753 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4754 output = NULL;
4755 fullscreen = FALSE;
4756 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, &output);
4757 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4758 ok(fullscreen, "Test %u: Got unexpected fullscreen status.\n", i);
4759 ok(!!output, "Test %u: Got unexpected output.\n", i);
4761 if (output)
4762 IDXGIOutput_ReleaseOwnership(output);
4763 /* Still fullscreen. */
4764 fullscreen = FALSE;
4765 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
4766 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4767 ok(fullscreen, "Test %u: Got unexpected fullscreen status.\n", i);
4768 /* Calling IDXGISwapChain_Present() will exit fullscreen. */
4769 hr = IDXGISwapChain_Present(swapchain, 0, flags[i]);
4770 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4771 fullscreen = TRUE;
4772 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
4773 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4774 /* Now fullscreen mode is exited. */
4775 if (!flags[i] && !is_d3d12)
4776 /* Still fullscreen on vista and 2008. */
4777 todo_wine ok(!fullscreen || broken(fullscreen), "Test %u: Got unexpected fullscreen status.\n", i);
4778 else
4779 ok(fullscreen, "Test %u: Got unexpected fullscreen status.\n", i);
4780 if (output)
4781 IDXGIOutput_Release(output);
4783 /* Test creating a window when swapchain is in fullscreen.
4785 * The window should break the swapchain out of fullscreen mode on
4786 * d3d10/11. D3d12 is different, a new occluding window doesn't break
4787 * the swapchain out of fullscreen because d3d12 fullscreen swapchains
4788 * don't take exclusive ownership over the output, nor do they disable
4789 * compositing. D3d12 fullscreen mode acts just like borderless
4790 * fullscreen window mode. */
4791 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
4792 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4793 fullscreen = FALSE;
4794 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
4795 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4796 ok(fullscreen, "Test %u: Got unexpected fullscreen status.\n", i);
4797 hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
4798 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4799 hr = IDXGISwapChain_Present(swapchain, 0, flags[i]);
4800 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4802 occluding_window = CreateWindowA("static", "occluding_window", WS_POPUP, 0, 0, 400, 200, 0, 0, 0, 0);
4803 /* An invisible window doesn't cause the swapchain to exit fullscreen
4804 * mode. */
4805 hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
4806 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4807 hr = IDXGISwapChain_Present(swapchain, 0, flags[i]);
4808 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4809 fullscreen = FALSE;
4810 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
4811 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4812 ok(fullscreen, "Test %u: Got unexpected fullscreen status.\n", i);
4813 /* A visible, but with bottom z-order window still causes the
4814 * swapchain to exit fullscreen mode. */
4815 SetWindowPos(occluding_window, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
4816 ShowWindow(occluding_window, SW_SHOW);
4817 /* Fullscreen mode takes a while to exit. */
4818 if (!is_d3d12)
4819 wait_fullscreen_state(swapchain, FALSE, TRUE);
4821 /* No longer fullscreen before calling IDXGISwapChain_Present() except
4822 * for d3d12. */
4823 fullscreen = TRUE;
4824 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
4825 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4826 todo_wine_if(!is_d3d12) ok(is_d3d12 ? fullscreen : !fullscreen,
4827 "Test %u: Got unexpected fullscreen status.\n", i);
4829 hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
4830 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4831 hr = IDXGISwapChain_Present(swapchain, 0, flags[i]);
4832 todo_wine_if(is_d3d12) ok(hr == (is_d3d12 ? DXGI_STATUS_OCCLUDED : S_OK),
4833 "Test %u: Got unexpected hr %#x.\n", i, hr);
4835 fullscreen = TRUE;
4836 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
4837 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4838 if (flags[i] == DXGI_PRESENT_TEST)
4839 todo_wine_if(!is_d3d12) ok(is_d3d12 ? fullscreen : !fullscreen,
4840 "Test %u: Got unexpected fullscreen status.\n", i);
4841 else
4842 todo_wine ok(!fullscreen, "Test %u: Got unexpected fullscreen status.\n", i);
4844 /* Even though d3d12 doesn't exit fullscreen, a
4845 * IDXGISwapChain_ResizeBuffers() is still needed for subsequent
4846 * IDXGISwapChain_Present() calls to work, otherwise they will return
4847 * DXGI_ERROR_INVALID_CALL */
4848 hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
4849 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4850 hr = IDXGISwapChain_Present(swapchain, 0, flags[i]);
4851 if (flags[i] == DXGI_PRESENT_TEST)
4852 todo_wine_if(is_d3d12) ok(hr == (is_d3d12 ? DXGI_STATUS_OCCLUDED : S_OK),
4853 "Test %u: Got unexpected hr %#x.\n", i, hr);
4854 else
4855 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4857 /* Trying to break out of fullscreen mode again. This time, don't call
4858 * IDXGISwapChain_GetFullscreenState() before IDXGISwapChain_Present(). */
4859 ShowWindow(occluding_window, SW_HIDE);
4860 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
4861 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4862 ShowWindow(occluding_window, SW_SHOW);
4864 hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
4865 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4866 hr = IDXGISwapChain_Present(swapchain, 0, flags[i]);
4867 /* hr == S_OK on vista and 2008 */
4868 todo_wine ok(hr == DXGI_STATUS_OCCLUDED || broken(hr == S_OK),
4869 "Test %u: Got unexpected hr %#x.\n", i, hr);
4871 hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
4872 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4873 hr = IDXGISwapChain_Present(swapchain, 0, flags[i]);
4874 if (flags[i] == DXGI_PRESENT_TEST)
4876 todo_wine ok(hr == DXGI_STATUS_OCCLUDED || broken(hr == S_OK),
4877 "Test %u: Got unexpected hr %#x.\n", i, hr);
4878 /* IDXGISwapChain_Present() without flags refreshes the occlusion
4879 * state. */
4880 hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
4881 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4882 hr = IDXGISwapChain_Present(swapchain, 0, 0);
4883 todo_wine ok(hr == DXGI_STATUS_OCCLUDED || broken(hr == S_OK),
4884 "Test %u: Got unexpected hr %#x.\n", i, hr);
4885 hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
4886 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4887 hr = IDXGISwapChain_Present(swapchain, 0, DXGI_PRESENT_TEST);
4888 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4890 else
4892 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4894 fullscreen = TRUE;
4895 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
4896 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4897 todo_wine ok(!fullscreen, "Test %u: Got unexpected fullscreen status.\n", i);
4899 DestroyWindow(occluding_window);
4900 flush_events();
4901 hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
4902 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4903 hr = IDXGISwapChain_Present(swapchain, 0, flags[i]);
4904 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4906 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
4907 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4908 hr = IDXGISwapChain_ResizeBuffers(swapchain, 0, 0, 0, DXGI_FORMAT_UNKNOWN, 0);
4909 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
4912 wait_device_idle(device);
4914 IDXGISwapChain_Release(swapchain);
4915 DestroyWindow(swapchain_desc.OutputWindow);
4916 refcount = IDXGIFactory_Release(factory);
4917 ok(refcount == !is_d3d12, "Got unexpected refcount %u.\n", refcount);
4920 static void test_swapchain_backbuffer_index(IUnknown *device, BOOL is_d3d12)
4922 DXGI_SWAP_CHAIN_DESC swapchain_desc;
4923 unsigned int index, expected_index;
4924 IDXGISwapChain3 *swapchain3;
4925 IDXGISwapChain *swapchain;
4926 HRESULT hr, expected_hr;
4927 IDXGIFactory *factory;
4928 unsigned int i, j;
4929 ULONG refcount;
4930 RECT rect;
4931 BOOL ret;
4933 static const DXGI_SWAP_EFFECT swap_effects[] =
4935 DXGI_SWAP_EFFECT_DISCARD,
4936 DXGI_SWAP_EFFECT_SEQUENTIAL,
4937 DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL,
4938 DXGI_SWAP_EFFECT_FLIP_DISCARD,
4941 get_factory(device, is_d3d12, &factory);
4943 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
4944 swapchain_desc.BufferDesc.RefreshRate.Denominator = 60;
4945 swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
4946 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
4947 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
4948 swapchain_desc.SampleDesc.Count = 1;
4949 swapchain_desc.SampleDesc.Quality = 0;
4950 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
4951 swapchain_desc.BufferCount = 4;
4952 swapchain_desc.OutputWindow = create_window();
4953 swapchain_desc.Windowed = TRUE;
4954 swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
4955 swapchain_desc.Flags = 0;
4957 ret = GetClientRect(swapchain_desc.OutputWindow, &rect);
4958 ok(ret, "Failed to get client rect.\n");
4959 swapchain_desc.BufferDesc.Width = rect.right;
4960 swapchain_desc.BufferDesc.Height = rect.bottom;
4962 for (i = 0; i < ARRAY_SIZE(swap_effects); ++i)
4964 swapchain_desc.SwapEffect = swap_effects[i];
4965 expected_hr = is_d3d12 && !is_flip_model(swap_effects[i]) ? DXGI_ERROR_INVALID_CALL : S_OK;
4966 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
4967 ok(hr == expected_hr, "Got hr %#x, expected %#x.\n", hr, expected_hr);
4968 if (FAILED(hr))
4969 continue;
4971 hr = IDXGISwapChain_QueryInterface(swapchain, &IID_IDXGISwapChain3, (void **)&swapchain3);
4972 if (hr == E_NOINTERFACE)
4974 skip("IDXGISwapChain3 is not supported.\n");
4975 IDXGISwapChain_Release(swapchain);
4976 goto done;
4979 for (j = 0; j < 2 * swapchain_desc.BufferCount; ++j)
4981 index = IDXGISwapChain3_GetCurrentBackBufferIndex(swapchain3);
4982 expected_index = is_d3d12 ? j % swapchain_desc.BufferCount : 0;
4983 ok(index == expected_index, "Got back buffer index %u, expected %u.\n", index, expected_index);
4984 hr = IDXGISwapChain3_Present(swapchain3, 0, 0);
4985 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
4988 wait_device_idle(device);
4990 IDXGISwapChain3_Release(swapchain3);
4991 refcount = IDXGISwapChain_Release(swapchain);
4992 ok(!refcount, "Swapchain has %u references left.\n", refcount);
4995 done:
4996 DestroyWindow(swapchain_desc.OutputWindow);
4997 refcount = IDXGIFactory_Release(factory);
4998 ok(refcount == !is_d3d12, "Got unexpected refcount %u.\n", refcount);
5001 static void test_swapchain_formats(IUnknown *device, BOOL is_d3d12)
5003 DXGI_SWAP_CHAIN_DESC swapchain_desc;
5004 IDXGISwapChain *swapchain;
5005 HRESULT hr, expected_hr;
5006 IDXGIFactory *factory;
5007 unsigned int i;
5008 ULONG refcount;
5009 RECT rect;
5010 BOOL ret;
5012 static const struct
5014 DXGI_FORMAT format;
5015 DXGI_SWAP_EFFECT swap_effect;
5016 BOOL supported;
5018 tests[] =
5020 {DXGI_FORMAT_UNKNOWN, DXGI_SWAP_EFFECT_DISCARD, FALSE},
5021 {DXGI_FORMAT_UNKNOWN, DXGI_SWAP_EFFECT_SEQUENTIAL, FALSE},
5022 {DXGI_FORMAT_UNKNOWN, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, FALSE},
5023 {DXGI_FORMAT_UNKNOWN, DXGI_SWAP_EFFECT_FLIP_DISCARD, FALSE},
5024 {DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_SWAP_EFFECT_DISCARD, TRUE},
5025 {DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_SWAP_EFFECT_SEQUENTIAL, TRUE},
5026 {DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, TRUE},
5027 {DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_SWAP_EFFECT_FLIP_DISCARD, TRUE},
5028 {DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_SWAP_EFFECT_DISCARD, TRUE},
5029 {DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_SWAP_EFFECT_SEQUENTIAL, TRUE},
5030 {DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, FALSE},
5031 {DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, DXGI_SWAP_EFFECT_FLIP_DISCARD, FALSE},
5032 {DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SWAP_EFFECT_DISCARD, TRUE},
5033 {DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SWAP_EFFECT_SEQUENTIAL, TRUE},
5034 {DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, TRUE},
5035 {DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SWAP_EFFECT_FLIP_DISCARD, TRUE},
5036 {DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, DXGI_SWAP_EFFECT_DISCARD, TRUE},
5037 {DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, DXGI_SWAP_EFFECT_SEQUENTIAL, TRUE},
5038 {DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, FALSE},
5039 {DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, DXGI_SWAP_EFFECT_FLIP_DISCARD, FALSE},
5040 {DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_SWAP_EFFECT_DISCARD, TRUE},
5041 {DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_SWAP_EFFECT_SEQUENTIAL, TRUE},
5042 {DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, TRUE},
5043 {DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_SWAP_EFFECT_FLIP_DISCARD, TRUE},
5044 {DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_SWAP_EFFECT_DISCARD, TRUE},
5045 {DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_SWAP_EFFECT_SEQUENTIAL, TRUE},
5046 {DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_SWAP_EFFECT_FLIP_DISCARD, TRUE},
5047 {DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, TRUE},
5048 {DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM, DXGI_SWAP_EFFECT_FLIP_DISCARD, FALSE},
5049 {DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM, DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL, FALSE},
5052 get_factory(device, is_d3d12, &factory);
5054 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
5055 swapchain_desc.BufferDesc.RefreshRate.Denominator = 60;
5056 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
5057 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
5058 swapchain_desc.SampleDesc.Count = 1;
5059 swapchain_desc.SampleDesc.Quality = 0;
5060 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
5061 swapchain_desc.BufferCount = 4;
5062 swapchain_desc.OutputWindow = create_window();
5063 swapchain_desc.Windowed = TRUE;
5064 swapchain_desc.Flags = 0;
5066 ret = GetClientRect(swapchain_desc.OutputWindow, &rect);
5067 ok(ret, "Failed to get client rect.\n");
5068 swapchain_desc.BufferDesc.Width = rect.right;
5069 swapchain_desc.BufferDesc.Height = rect.bottom;
5071 for (i = 0; i < ARRAY_SIZE(tests); ++i)
5073 if (is_d3d12 && !is_flip_model(tests[i].swap_effect))
5074 continue;
5076 swapchain_desc.BufferDesc.Format = tests[i].format;
5077 swapchain_desc.SwapEffect = tests[i].swap_effect;
5078 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
5079 expected_hr = tests[i].supported ? S_OK : DXGI_ERROR_INVALID_CALL;
5080 if (tests[i].format == DXGI_FORMAT_UNKNOWN && !is_d3d12)
5081 expected_hr = E_INVALIDARG;
5082 ok(hr == expected_hr
5083 /* Flip presentation model not supported. */
5084 || broken(hr == DXGI_ERROR_INVALID_CALL && is_flip_model(tests[i].swap_effect) && !is_d3d12),
5085 "Test %u, d3d12 %#x: Got hr %#x, expected %#x.\n", i, is_d3d12, hr, expected_hr);
5087 if (SUCCEEDED(hr))
5089 refcount = IDXGISwapChain_Release(swapchain);
5090 ok(!refcount, "Swapchain has %u references left.\n", refcount);
5094 DestroyWindow(swapchain_desc.OutputWindow);
5095 refcount = IDXGIFactory_Release(factory);
5096 ok(refcount == !is_d3d12, "Got unexpected refcount %u.\n", refcount);
5099 static void test_maximum_frame_latency(void)
5101 IDXGIDevice1 *device1;
5102 IDXGIDevice *device;
5103 UINT max_latency;
5104 ULONG refcount;
5105 HRESULT hr;
5107 if (!(device = create_device(0)))
5109 skip("Failed to create device.\n");
5110 return;
5113 if (SUCCEEDED(IDXGIDevice_QueryInterface(device, &IID_IDXGIDevice1, (void **)&device1)))
5115 hr = IDXGIDevice1_GetMaximumFrameLatency(device1, NULL);
5116 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
5118 hr = IDXGIDevice1_GetMaximumFrameLatency(device1, &max_latency);
5119 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
5120 ok(max_latency == DEFAULT_FRAME_LATENCY, "Got unexpected maximum frame latency %u.\n", max_latency);
5122 hr = IDXGIDevice1_SetMaximumFrameLatency(device1, MAX_FRAME_LATENCY);
5123 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
5124 hr = IDXGIDevice1_GetMaximumFrameLatency(device1, &max_latency);
5125 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
5126 ok(max_latency == MAX_FRAME_LATENCY, "Got unexpected maximum frame latency %u.\n", max_latency);
5128 hr = IDXGIDevice1_SetMaximumFrameLatency(device1, MAX_FRAME_LATENCY + 1);
5129 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
5130 hr = IDXGIDevice1_GetMaximumFrameLatency(device1, &max_latency);
5131 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
5132 ok(max_latency == MAX_FRAME_LATENCY, "Got unexpected maximum frame latency %u.\n", max_latency);
5134 hr = IDXGIDevice1_SetMaximumFrameLatency(device1, 0);
5135 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
5136 hr = IDXGIDevice1_GetMaximumFrameLatency(device1, &max_latency);
5137 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
5138 /* 0 does not reset to the default frame latency on all Windows versions. */
5139 ok(max_latency == DEFAULT_FRAME_LATENCY || broken(!max_latency),
5140 "Got unexpected maximum frame latency %u.\n", max_latency);
5142 IDXGIDevice1_Release(device1);
5144 else
5146 win_skip("IDXGIDevice1 is not implemented.\n");
5149 refcount = IDXGIDevice_Release(device);
5150 ok(!refcount, "Device has %u references left.\n", refcount);
5153 static void test_output_desc(void)
5155 IDXGIAdapter *adapter, *adapter2;
5156 IDXGIOutput *output, *output2;
5157 DXGI_OUTPUT_DESC desc;
5158 IDXGIFactory *factory;
5159 unsigned int i, j;
5160 ULONG refcount;
5161 HRESULT hr;
5163 hr = CreateDXGIFactory(&IID_IDXGIFactory, (void **)&factory);
5164 ok(SUCCEEDED(hr), "Failed to create DXGI factory, hr %#x.\n", hr);
5166 for (i = 0; ; ++i)
5168 hr = IDXGIFactory_EnumAdapters(factory, i, &adapter);
5169 if (hr == DXGI_ERROR_NOT_FOUND)
5170 break;
5171 ok(SUCCEEDED(hr), "Failed to enumerate adapter %u, hr %#x.\n", i, hr);
5173 hr = IDXGIFactory_EnumAdapters(factory, i, &adapter2);
5174 ok(SUCCEEDED(hr), "Failed to enumerate adapter %u, hr %#x.\n", i, hr);
5175 ok(adapter != adapter2, "Expected to get new instance of IDXGIAdapter, %p == %p.\n", adapter, adapter2);
5176 refcount = get_refcount(adapter);
5177 ok(refcount == 1, "Get unexpected refcount %u for adapter %u.\n", refcount, i);
5178 IDXGIAdapter_Release(adapter2);
5180 refcount = get_refcount(factory);
5181 ok(refcount == 2, "Get unexpected refcount %u.\n", refcount);
5182 refcount = get_refcount(adapter);
5183 ok(refcount == 1, "Get unexpected refcount %u for adapter %u.\n", refcount, i);
5185 for (j = 0; ; ++j)
5187 MONITORINFOEXW monitor_info;
5188 BOOL ret;
5190 hr = IDXGIAdapter_EnumOutputs(adapter, j, &output);
5191 if (hr == DXGI_ERROR_NOT_FOUND)
5192 break;
5193 ok(SUCCEEDED(hr), "Failed to enumerate output %u on adapter %u, hr %#x.\n", j, i, hr);
5195 hr = IDXGIAdapter_EnumOutputs(adapter, j, &output2);
5196 ok(SUCCEEDED(hr), "Failed to enumerate output %u on adapter %u, hr %#x.\n", j, i, hr);
5197 ok(output != output2, "Expected to get new instance of IDXGIOutput, %p == %p.\n", output, output2);
5198 refcount = get_refcount(output);
5199 ok(refcount == 1, "Get unexpected refcount %u for output %u, adapter %u.\n", refcount, j, i);
5200 IDXGIOutput_Release(output2);
5202 refcount = get_refcount(factory);
5203 ok(refcount == 2, "Get unexpected refcount %u.\n", refcount);
5204 refcount = get_refcount(adapter);
5205 ok(refcount == 2, "Get unexpected refcount %u for adapter %u.\n", refcount, i);
5206 refcount = get_refcount(output);
5207 ok(refcount == 1, "Get unexpected refcount %u for output %u, adapter %u.\n", refcount, j, i);
5209 hr = IDXGIOutput_GetDesc(output, &desc);
5210 ok(SUCCEEDED(hr), "Failed to get desc for output %u on adapter %u, hr %#x.\n", j, i, hr);
5212 monitor_info.cbSize = sizeof(monitor_info);
5213 ret = GetMonitorInfoW(desc.Monitor, (MONITORINFO *)&monitor_info);
5214 ok(ret, "Failed to get monitor info.\n");
5215 ok(!lstrcmpW(desc.DeviceName, monitor_info.szDevice), "Got unexpected device name %s, expected %s.\n",
5216 wine_dbgstr_w(desc.DeviceName), wine_dbgstr_w(monitor_info.szDevice));
5217 ok(EqualRect(&desc.DesktopCoordinates, &monitor_info.rcMonitor),
5218 "Got unexpected desktop coordinates %s, expected %s.\n",
5219 wine_dbgstr_rect(&desc.DesktopCoordinates),
5220 wine_dbgstr_rect(&monitor_info.rcMonitor));
5222 IDXGIOutput_Release(output);
5223 refcount = get_refcount(adapter);
5224 ok(refcount == 1, "Get unexpected refcount %u for adapter %u.\n", refcount, i);
5227 IDXGIAdapter_Release(adapter);
5228 refcount = get_refcount(factory);
5229 ok(refcount == 1, "Get unexpected refcount %u.\n", refcount);
5232 refcount = IDXGIFactory_Release(factory);
5233 ok(!refcount, "IDXGIFactory has %u references left.\n", refcount);
5236 struct dxgi_factory
5238 IDXGIFactory IDXGIFactory_iface;
5239 IDXGIFactory *wrapped_iface;
5240 unsigned int wrapped_adapter_count;
5243 static inline struct dxgi_factory *impl_from_IDXGIFactory(IDXGIFactory *iface)
5245 return CONTAINING_RECORD(iface, struct dxgi_factory, IDXGIFactory_iface);
5248 static HRESULT STDMETHODCALLTYPE dxgi_factory_QueryInterface(IDXGIFactory *iface, REFIID iid, void **out)
5250 struct dxgi_factory *factory = impl_from_IDXGIFactory(iface);
5252 if (IsEqualGUID(iid, &IID_IDXGIFactory)
5253 || IsEqualGUID(iid, &IID_IDXGIObject)
5254 || IsEqualGUID(iid, &IID_IUnknown))
5256 IDXGIFactory_AddRef(iface);
5257 *out = iface;
5258 return S_OK;
5260 return IDXGIFactory_QueryInterface(factory->wrapped_iface, iid, out);
5263 static ULONG STDMETHODCALLTYPE dxgi_factory_AddRef(IDXGIFactory *iface)
5265 struct dxgi_factory *factory = impl_from_IDXGIFactory(iface);
5266 return IDXGIFactory_AddRef(factory->wrapped_iface);
5269 static ULONG STDMETHODCALLTYPE dxgi_factory_Release(IDXGIFactory *iface)
5271 struct dxgi_factory *factory = impl_from_IDXGIFactory(iface);
5272 return IDXGIFactory_Release(factory->wrapped_iface);
5275 static HRESULT STDMETHODCALLTYPE dxgi_factory_SetPrivateData(IDXGIFactory *iface,
5276 REFGUID guid, UINT data_size, const void *data)
5278 struct dxgi_factory *factory = impl_from_IDXGIFactory(iface);
5279 return IDXGIFactory_SetPrivateData(factory->wrapped_iface, guid, data_size, data);
5282 static HRESULT STDMETHODCALLTYPE dxgi_factory_SetPrivateDataInterface(IDXGIFactory *iface,
5283 REFGUID guid, const IUnknown *object)
5285 struct dxgi_factory *factory = impl_from_IDXGIFactory(iface);
5286 return IDXGIFactory_SetPrivateDataInterface(factory->wrapped_iface, guid, object);
5289 static HRESULT STDMETHODCALLTYPE dxgi_factory_GetPrivateData(IDXGIFactory *iface,
5290 REFGUID guid, UINT *data_size, void *data)
5292 struct dxgi_factory *factory = impl_from_IDXGIFactory(iface);
5293 return IDXGIFactory_GetPrivateData(factory->wrapped_iface, guid, data_size, data);
5296 static HRESULT STDMETHODCALLTYPE dxgi_factory_GetParent(IDXGIFactory *iface, REFIID iid, void **parent)
5298 struct dxgi_factory *factory = impl_from_IDXGIFactory(iface);
5299 return IDXGIFactory_GetParent(factory->wrapped_iface, iid, parent);
5302 static HRESULT STDMETHODCALLTYPE dxgi_factory_EnumAdapters(IDXGIFactory *iface,
5303 UINT adapter_idx, IDXGIAdapter **adapter)
5305 struct dxgi_factory *factory = impl_from_IDXGIFactory(iface);
5306 HRESULT hr;
5308 if (SUCCEEDED(hr = IDXGIFactory_EnumAdapters(factory->wrapped_iface, adapter_idx, adapter)))
5309 ++factory->wrapped_adapter_count;
5310 return hr;
5313 static HRESULT STDMETHODCALLTYPE dxgi_factory_MakeWindowAssociation(IDXGIFactory *iface,
5314 HWND window, UINT flags)
5316 struct dxgi_factory *factory = impl_from_IDXGIFactory(iface);
5317 return IDXGIFactory_MakeWindowAssociation(factory->wrapped_iface, window, flags);
5320 static HRESULT STDMETHODCALLTYPE dxgi_factory_GetWindowAssociation(IDXGIFactory *iface, HWND *window)
5322 struct dxgi_factory *factory = impl_from_IDXGIFactory(iface);
5323 return IDXGIFactory_GetWindowAssociation(factory->wrapped_iface, window);
5326 static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChain(IDXGIFactory *iface,
5327 IUnknown *device, DXGI_SWAP_CHAIN_DESC *desc, IDXGISwapChain **swapchain)
5329 struct dxgi_factory *factory = impl_from_IDXGIFactory(iface);
5330 return IDXGIFactory_CreateSwapChain(factory->wrapped_iface, device, desc, swapchain);
5333 static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSoftwareAdapter(IDXGIFactory *iface,
5334 HMODULE swrast, IDXGIAdapter **adapter)
5336 struct dxgi_factory *factory = impl_from_IDXGIFactory(iface);
5337 return IDXGIFactory_CreateSoftwareAdapter(factory->wrapped_iface, swrast, adapter);
5340 static const struct IDXGIFactoryVtbl dxgi_factory_vtbl =
5342 dxgi_factory_QueryInterface,
5343 dxgi_factory_AddRef,
5344 dxgi_factory_Release,
5345 dxgi_factory_SetPrivateData,
5346 dxgi_factory_SetPrivateDataInterface,
5347 dxgi_factory_GetPrivateData,
5348 dxgi_factory_GetParent,
5349 dxgi_factory_EnumAdapters,
5350 dxgi_factory_MakeWindowAssociation,
5351 dxgi_factory_GetWindowAssociation,
5352 dxgi_factory_CreateSwapChain,
5353 dxgi_factory_CreateSoftwareAdapter,
5356 struct dxgi_adapter
5358 IDXGIAdapter IDXGIAdapter_iface;
5359 IDXGIAdapter *wrapped_iface;
5360 struct dxgi_factory factory;
5361 unsigned int wrapped_output_count;
5364 static inline struct dxgi_adapter *impl_from_IDXGIAdapter(IDXGIAdapter *iface)
5366 return CONTAINING_RECORD(iface, struct dxgi_adapter, IDXGIAdapter_iface);
5369 static HRESULT STDMETHODCALLTYPE dxgi_adapter_QueryInterface(IDXGIAdapter *iface, REFIID iid, void **out)
5371 struct dxgi_adapter *adapter = impl_from_IDXGIAdapter(iface);
5373 if (IsEqualGUID(iid, &IID_IDXGIAdapter)
5374 || IsEqualGUID(iid, &IID_IDXGIObject)
5375 || IsEqualGUID(iid, &IID_IUnknown))
5377 IDXGIAdapter_AddRef(adapter->wrapped_iface);
5378 *out = iface;
5379 return S_OK;
5381 return IDXGIAdapter_QueryInterface(adapter->wrapped_iface, iid, out);
5384 static ULONG STDMETHODCALLTYPE dxgi_adapter_AddRef(IDXGIAdapter *iface)
5386 struct dxgi_adapter *adapter = impl_from_IDXGIAdapter(iface);
5387 return IDXGIAdapter_AddRef(adapter->wrapped_iface);
5390 static ULONG STDMETHODCALLTYPE dxgi_adapter_Release(IDXGIAdapter *iface)
5392 struct dxgi_adapter *adapter = impl_from_IDXGIAdapter(iface);
5393 return IDXGIAdapter_Release(adapter->wrapped_iface);
5396 static HRESULT STDMETHODCALLTYPE dxgi_adapter_SetPrivateData(IDXGIAdapter *iface,
5397 REFGUID guid, UINT data_size, const void *data)
5399 struct dxgi_adapter *adapter = impl_from_IDXGIAdapter(iface);
5400 return IDXGIAdapter_SetPrivateData(adapter->wrapped_iface, guid, data_size, data);
5403 static HRESULT STDMETHODCALLTYPE dxgi_adapter_SetPrivateDataInterface(IDXGIAdapter *iface,
5404 REFGUID guid, const IUnknown *object)
5406 struct dxgi_adapter *adapter = impl_from_IDXGIAdapter(iface);
5407 return IDXGIAdapter_SetPrivateDataInterface(adapter->wrapped_iface, guid, object);
5410 static HRESULT STDMETHODCALLTYPE dxgi_adapter_GetPrivateData(IDXGIAdapter *iface,
5411 REFGUID guid, UINT *data_size, void *data)
5413 struct dxgi_adapter *adapter = impl_from_IDXGIAdapter(iface);
5414 return IDXGIAdapter_GetPrivateData(adapter->wrapped_iface, guid, data_size, data);
5417 static HRESULT STDMETHODCALLTYPE dxgi_adapter_GetParent(IDXGIAdapter *iface, REFIID iid, void **parent)
5419 struct dxgi_adapter *adapter = impl_from_IDXGIAdapter(iface);
5420 return IDXGIFactory_QueryInterface(&adapter->factory.IDXGIFactory_iface, iid, parent);
5423 static HRESULT STDMETHODCALLTYPE dxgi_adapter_EnumOutputs(IDXGIAdapter *iface,
5424 UINT output_idx, IDXGIOutput **output)
5426 struct dxgi_adapter *adapter = impl_from_IDXGIAdapter(iface);
5427 HRESULT hr;
5429 if (SUCCEEDED(hr = IDXGIAdapter_EnumOutputs(adapter->wrapped_iface, output_idx, output)))
5430 ++adapter->wrapped_output_count;
5431 return hr;
5434 static HRESULT STDMETHODCALLTYPE dxgi_adapter_GetDesc(IDXGIAdapter *iface, DXGI_ADAPTER_DESC *desc)
5436 struct dxgi_adapter *adapter = impl_from_IDXGIAdapter(iface);
5437 return IDXGIAdapter_GetDesc(adapter->wrapped_iface, desc);
5440 static HRESULT STDMETHODCALLTYPE dxgi_adapter_CheckInterfaceSupport(IDXGIAdapter *iface,
5441 REFGUID guid, LARGE_INTEGER *umd_version)
5443 struct dxgi_adapter *adapter = impl_from_IDXGIAdapter(iface);
5444 return IDXGIAdapter_CheckInterfaceSupport(adapter->wrapped_iface, guid, umd_version);
5447 static const struct IDXGIAdapterVtbl dxgi_adapter_vtbl =
5449 dxgi_adapter_QueryInterface,
5450 dxgi_adapter_AddRef,
5451 dxgi_adapter_Release,
5452 dxgi_adapter_SetPrivateData,
5453 dxgi_adapter_SetPrivateDataInterface,
5454 dxgi_adapter_GetPrivateData,
5455 dxgi_adapter_GetParent,
5456 dxgi_adapter_EnumOutputs,
5457 dxgi_adapter_GetDesc,
5458 dxgi_adapter_CheckInterfaceSupport,
5461 static void test_object_wrapping(void)
5463 struct dxgi_adapter wrapper;
5464 DXGI_ADAPTER_DESC desc;
5465 IDXGIAdapter *adapter;
5466 IDXGIFactory *factory;
5467 ID3D10Device1 *device;
5468 ULONG refcount;
5469 HRESULT hr;
5471 hr = CreateDXGIFactory(&IID_IDXGIFactory, (void **)&factory);
5472 ok(hr == S_OK, "Failed to create DXGI factory, hr %#x.\n", hr);
5474 hr = IDXGIFactory_EnumAdapters(factory, 0, &adapter);
5475 if (hr == DXGI_ERROR_NOT_FOUND)
5477 skip("Could not enumerate adapters.\n");
5478 IDXGIFactory_Release(factory);
5479 return;
5481 ok(hr == S_OK, "Failed to enumerate adapter, hr %#x.\n", hr);
5483 wrapper.IDXGIAdapter_iface.lpVtbl = &dxgi_adapter_vtbl;
5484 wrapper.wrapped_iface = adapter;
5485 wrapper.factory.IDXGIFactory_iface.lpVtbl = &dxgi_factory_vtbl;
5486 wrapper.factory.wrapped_iface = factory;
5487 wrapper.factory.wrapped_adapter_count = 0;
5488 wrapper.wrapped_output_count = 0;
5490 hr = D3D10CreateDevice1(&wrapper.IDXGIAdapter_iface, D3D10_DRIVER_TYPE_HARDWARE, NULL,
5491 0, D3D10_FEATURE_LEVEL_10_0, D3D10_1_SDK_VERSION, &device);
5492 if (SUCCEEDED(hr))
5494 refcount = ID3D10Device1_Release(device);
5495 ok(!refcount, "Device has %u references left.\n", refcount);
5498 hr = IDXGIAdapter_GetDesc(&wrapper.IDXGIAdapter_iface, &desc);
5499 ok(hr == S_OK, "Failed to get adapter desc, hr %#x.\n", hr);
5500 ok(!wrapper.factory.wrapped_adapter_count, "Got unexpected wrapped adapter count %u.\n",
5501 wrapper.factory.wrapped_adapter_count);
5502 ok(!wrapper.wrapped_output_count, "Got unexpected wrapped output count %u.\n", wrapper.wrapped_output_count);
5504 refcount = IDXGIAdapter_Release(&wrapper.IDXGIAdapter_iface);
5505 ok(!refcount, "Adapter has %u references left.\n", refcount);
5506 refcount = IDXGIFactory_Release(factory);
5507 ok(!refcount, "Factory has %u references left.\n", refcount);
5510 struct adapter_info
5512 const WCHAR *name;
5513 HMONITOR monitor;
5516 static BOOL CALLBACK enum_monitor_proc(HMONITOR monitor, HDC dc, RECT *rect, LPARAM lparam)
5518 struct adapter_info *adapter_info = (struct adapter_info *)lparam;
5519 MONITORINFOEXW monitor_info;
5521 monitor_info.cbSize = sizeof(monitor_info);
5522 if (GetMonitorInfoW(monitor, (MONITORINFO *)&monitor_info)
5523 && !lstrcmpiW(adapter_info->name, monitor_info.szDevice))
5525 adapter_info->monitor = monitor;
5526 return FALSE;
5529 return TRUE;
5532 static HMONITOR get_monitor(const WCHAR *adapter_name)
5534 struct adapter_info info = {adapter_name, NULL};
5536 EnumDisplayMonitors(NULL, NULL, enum_monitor_proc, (LPARAM)&info);
5537 return info.monitor;
5540 static void test_multi_adapter(void)
5542 unsigned int output_count = 0, expected_output_count = 0;
5543 unsigned int adapter_index, output_index, device_index;
5544 DXGI_OUTPUT_DESC old_output_desc, output_desc;
5545 DXGI_ADAPTER_DESC1 adapter_desc1;
5546 DXGI_ADAPTER_DESC adapter_desc;
5547 DISPLAY_DEVICEW display_device;
5548 MONITORINFO monitor_info;
5549 DEVMODEW old_mode, mode;
5550 IDXGIAdapter1 *adapter1;
5551 IDXGIFactory *factory;
5552 IDXGIAdapter *adapter;
5553 IDXGIOutput *output;
5554 HMONITOR monitor;
5555 BOOL found;
5556 HRESULT hr;
5557 LONG ret;
5559 if (FAILED(hr = CreateDXGIFactory(&IID_IDXGIFactory, (void **)&factory)))
5561 skip("Failed to create IDXGIFactory, hr %#x.\n", hr);
5562 return;
5565 hr = IDXGIFactory_EnumAdapters(factory, 0, NULL);
5566 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
5568 hr = IDXGIFactory_EnumAdapters(factory, 0, &adapter);
5569 if (hr == DXGI_ERROR_NOT_FOUND)
5571 skip("Could not enumerate adapters.\n");
5572 IDXGIFactory_Release(factory);
5573 return;
5575 ok(hr == S_OK, "Failed to enumerate adapter, hr %#x.\n", hr);
5577 for (adapter_index = 0; SUCCEEDED(IDXGIFactory_EnumAdapters(factory, adapter_index, &adapter)); ++adapter_index)
5579 for (output_index = 0; SUCCEEDED(IDXGIAdapter_EnumOutputs(adapter, output_index, &output)); ++output_index)
5581 hr = IDXGIOutput_GetDesc(output, &output_desc);
5582 ok(hr == S_OK, "Adapter %u output %u: Got unexpected hr %#x.\n", adapter_index,
5583 output_index, hr);
5585 found = FALSE;
5586 display_device.cb = sizeof(display_device);
5587 for (device_index = 0; EnumDisplayDevicesW(NULL, device_index, &display_device, 0); ++device_index)
5589 if (!lstrcmpiW(display_device.DeviceName, output_desc.DeviceName))
5591 found = TRUE;
5592 break;
5595 ok(found, "Adapter %u output %u: Failed to find device %s.\n",
5596 adapter_index, output_index, wine_dbgstr_w(output_desc.DeviceName));
5598 ok(display_device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP,
5599 "Adapter %u output %u: Got unexpected state flags %#x.\n", adapter_index,
5600 output_index, display_device.StateFlags);
5601 if (!adapter_index && !output_index)
5602 ok(display_device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE,
5603 "Adapter %u output %u: Got unexpected state flags %#x.\n", adapter_index,
5604 output_index, display_device.StateFlags);
5605 else
5606 ok(!(display_device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE),
5607 "Adapter %u output %u: Got unexpected state flags %#x.\n", adapter_index,
5608 output_index, display_device.StateFlags);
5610 /* Should have the same monitor handle. */
5611 monitor = get_monitor(display_device.DeviceName);
5612 ok(!!monitor, "Adapter %u output %u: Failed to find monitor %s.\n", adapter_index,
5613 output_index, wine_dbgstr_w(display_device.DeviceName));
5614 ok(monitor == output_desc.Monitor,
5615 "Adapter %u output %u: Got unexpected monitor %p, expected %p.\n",
5616 adapter_index, output_index, monitor, output_desc.Monitor);
5618 /* Should have the same monitor rectangle. */
5619 monitor_info.cbSize = sizeof(monitor_info);
5620 ret = GetMonitorInfoA(monitor, &monitor_info);
5621 ok(ret, "Adapter %u output %u: Failed to get monitor info, error %#x.\n", adapter_index,
5622 output_index, GetLastError());
5623 ok(EqualRect(&monitor_info.rcMonitor, &output_desc.DesktopCoordinates),
5624 "Adapter %u output %u: Got unexpected output rect %s, expected %s.\n",
5625 adapter_index, output_index, wine_dbgstr_rect(&monitor_info.rcMonitor),
5626 wine_dbgstr_rect(&output_desc.DesktopCoordinates));
5628 ++output_count;
5630 /* Test output description after it got detached */
5631 if (display_device.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
5633 IDXGIOutput_Release(output);
5634 continue;
5637 old_output_desc = output_desc;
5639 /* Save current display settings */
5640 memset(&old_mode, 0, sizeof(old_mode));
5641 old_mode.dmSize = sizeof(old_mode);
5642 ret = EnumDisplaySettingsW(display_device.DeviceName, ENUM_CURRENT_SETTINGS, &old_mode);
5643 /* Win10 TestBots may return FALSE but it's actually successful */
5644 ok(ret || broken(!ret),
5645 "Adapter %u output %u: EnumDisplaySettingsW failed for %s, error %#x.\n",
5646 adapter_index, output_index, wine_dbgstr_w(display_device.DeviceName),
5647 GetLastError());
5649 /* Detach */
5650 memset(&mode, 0, sizeof(mode));
5651 mode.dmSize = sizeof(mode);
5652 mode.dmFields = DM_POSITION | DM_PELSWIDTH | DM_PELSHEIGHT;
5653 mode.dmPosition = old_mode.dmPosition;
5654 ret = ChangeDisplaySettingsExW(display_device.DeviceName, &mode, NULL,
5655 CDS_UPDATEREGISTRY | CDS_NORESET, NULL);
5656 ok(ret == DISP_CHANGE_SUCCESSFUL,
5657 "Adapter %u output %u: ChangeDisplaySettingsExW %s returned unexpected %d.\n",
5658 adapter_index, output_index, wine_dbgstr_w(display_device.DeviceName), ret);
5659 ret = ChangeDisplaySettingsExW(display_device.DeviceName, NULL, NULL, 0, NULL);
5660 ok(ret == DISP_CHANGE_SUCCESSFUL,
5661 "Adapter %u output %u: ChangeDisplaySettingsExW %s returned unexpected %d.\n",
5662 adapter_index, output_index, wine_dbgstr_w(display_device.DeviceName), ret);
5664 /* Check if it is really detached */
5665 memset(&mode, 0, sizeof(mode));
5666 mode.dmSize = sizeof(mode);
5667 ret = EnumDisplaySettingsW(display_device.DeviceName, ENUM_CURRENT_SETTINGS, &mode);
5668 /* Win10 TestBots may return FALSE but it's actually successful */
5669 ok(ret || broken(!ret) ,
5670 "Adapter %u output %u: EnumDisplaySettingsW failed for %s, error %#x.\n",
5671 adapter_index, output_index, wine_dbgstr_w(display_device.DeviceName),
5672 GetLastError());
5673 if (mode.dmPelsWidth && mode.dmPelsHeight)
5675 skip("Adapter %u output %u: Failed to detach device %s.\n", adapter_index,
5676 output_index, wine_dbgstr_w(display_device.DeviceName));
5677 IDXGIOutput_Release(output);
5678 continue;
5681 /* Only the AttachedToDesktop field is updated after an output is detached.
5682 * IDXGIAdapter_EnumOutputs() has to be called again to get other fields updated.
5683 * But resolution changes are reflected right away. This weird behaviour is currently
5684 * unimplemented in Wine */
5685 memset(&output_desc, 0, sizeof(output_desc));
5686 hr = IDXGIOutput_GetDesc(output, &output_desc);
5687 ok(hr == S_OK, "Adapter %u output %u: Got unexpected hr %#x.\n", adapter_index,
5688 output_index, hr);
5689 ok(!lstrcmpiW(output_desc.DeviceName, old_output_desc.DeviceName),
5690 "Adapter %u output %u: Expect device name %s, got %s.\n", adapter_index,
5691 output_index, wine_dbgstr_w(old_output_desc.DeviceName),
5692 wine_dbgstr_w(output_desc.DeviceName));
5693 todo_wine
5694 ok(EqualRect(&output_desc.DesktopCoordinates, &old_output_desc.DesktopCoordinates),
5695 "Adapter %u output %u: Expect desktop coordinates %s, got %s.\n",
5696 adapter_index, output_index,
5697 wine_dbgstr_rect(&old_output_desc.DesktopCoordinates),
5698 wine_dbgstr_rect(&output_desc.DesktopCoordinates));
5699 ok(!output_desc.AttachedToDesktop,
5700 "Adapter %u output %u: Expect output not attached to desktop.\n", adapter_index,
5701 output_index);
5702 ok(output_desc.Rotation == old_output_desc.Rotation,
5703 "Adapter %u output %u: Expect rotation %#x, got %#x.\n", adapter_index,
5704 output_index, old_output_desc.Rotation, output_desc.Rotation);
5705 todo_wine
5706 ok(output_desc.Monitor == old_output_desc.Monitor,
5707 "Adapter %u output %u: Expect monitor %p, got %p.\n", adapter_index,
5708 output_index, old_output_desc.Monitor, output_desc.Monitor);
5709 IDXGIOutput_Release(output);
5711 /* Call IDXGIAdapter_EnumOutputs() again to get up-to-date output description */
5712 hr = IDXGIAdapter_EnumOutputs(adapter, output_index, &output);
5713 ok(hr == S_OK, "Adapter %u output %u: Got unexpected hr %#x.\n", adapter_index,
5714 output_index, hr);
5715 memset(&output_desc, 0, sizeof(output_desc));
5716 hr = IDXGIOutput_GetDesc(output, &output_desc);
5717 ok(hr == S_OK, "Adapter %u output %u: Got unexpected hr %#x.\n", adapter_index,
5718 output_index, hr);
5719 ok(!lstrcmpiW(output_desc.DeviceName, display_device.DeviceName),
5720 "Adapter %u output %u: Expect device name %s, got %s.\n", adapter_index,
5721 output_index, wine_dbgstr_w(display_device.DeviceName),
5722 wine_dbgstr_w(output_desc.DeviceName));
5723 ok(IsRectEmpty(&output_desc.DesktopCoordinates),
5724 "Adapter %u output %u: Expect desktop rect empty, got %s.\n", adapter_index,
5725 output_index, wine_dbgstr_rect(&output_desc.DesktopCoordinates));
5726 ok(!output_desc.AttachedToDesktop,
5727 "Adapter %u output %u: Expect output not attached to desktop.\n", adapter_index,
5728 output_index);
5729 ok(output_desc.Rotation == DXGI_MODE_ROTATION_IDENTITY,
5730 "Adapter %u output %u: Expect rotation %#x, got %#x.\n", adapter_index,
5731 output_index, DXGI_MODE_ROTATION_IDENTITY, output_desc.Rotation);
5732 ok(!output_desc.Monitor, "Adapter %u output %u: Expect monitor NULL.\n", adapter_index,
5733 output_index);
5735 /* Restore settings */
5736 ret = ChangeDisplaySettingsExW(display_device.DeviceName, &old_mode, NULL,
5737 CDS_UPDATEREGISTRY | CDS_NORESET, NULL);
5738 ok(ret == DISP_CHANGE_SUCCESSFUL,
5739 "Adapter %u output %u: ChangeDisplaySettingsExW %s returned unexpected %d.\n",
5740 adapter_index, output_index, wine_dbgstr_w(display_device.DeviceName), ret);
5741 ret = ChangeDisplaySettingsExW(display_device.DeviceName, NULL, NULL, 0, NULL);
5742 ok(ret == DISP_CHANGE_SUCCESSFUL,
5743 "Adapter %u output %u: ChangeDisplaySettingsExW %s returned unexpected %d.\n",
5744 adapter_index, output_index, wine_dbgstr_w(display_device.DeviceName), ret);
5746 IDXGIOutput_Release(output);
5749 IDXGIAdapter_Release(adapter);
5752 /* Windows 8+ always have a WARP adapter present at the end. */
5753 todo_wine ok(adapter_index >= 2 || broken(adapter_index < 2) /* Windows 7 and before */,
5754 "Got unexpected adapter count %u.\n", adapter_index);
5755 if (adapter_index < 2)
5757 todo_wine win_skip("WARP adapter missing, skipping tests.\n");
5758 goto done;
5761 hr = IDXGIFactory_EnumAdapters(factory, adapter_index - 1, &adapter);
5762 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
5763 hr = IDXGIAdapter_GetDesc(adapter, &adapter_desc);
5764 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
5765 todo_wine ok(!lstrcmpW(adapter_desc.Description, L"Microsoft Basic Render Driver"),
5766 "Got unexpected description %s.\n", wine_dbgstr_w(adapter_desc.Description));
5767 todo_wine ok(adapter_desc.VendorId == 0x1414,
5768 "Got unexpected vendor ID %#x.\n", adapter_desc.VendorId);
5769 todo_wine ok(adapter_desc.DeviceId == 0x008c,
5770 "Got unexpected device ID %#x.\n", adapter_desc.DeviceId);
5771 ok(adapter_desc.SubSysId == 0x0000,
5772 "Got unexpected sub-system ID %#x.\n", adapter_desc.SubSysId);
5773 ok(adapter_desc.Revision == 0x0000,
5774 "Got unexpected revision %#x.\n", adapter_desc.Revision);
5775 todo_wine ok(!adapter_desc.DedicatedVideoMemory,
5776 "Got unexpected DedicatedVideoMemory %#lx.\n", adapter_desc.DedicatedVideoMemory);
5777 ok(!adapter_desc.DedicatedSystemMemory,
5778 "Got unexpected DedicatedSystemMemory %#lx.\n", adapter_desc.DedicatedSystemMemory);
5780 hr = IDXGIAdapter_QueryInterface(adapter, &IID_IDXGIAdapter1, (void **)&adapter1);
5781 ok(hr == S_OK || broken(hr == E_NOINTERFACE), "Got unexpected hr %#x.\n", hr);
5782 if (SUCCEEDED(hr))
5784 hr = IDXGIAdapter1_GetDesc1(adapter1, &adapter_desc1);
5785 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
5786 todo_wine ok(adapter_desc1.Flags == DXGI_ADAPTER_FLAG_SOFTWARE,
5787 "Got unexpected flags %#x.\n", adapter_desc1.Flags);
5788 IDXGIAdapter1_Release(adapter1);
5791 IDXGIAdapter_Release(adapter);
5793 done:
5794 IDXGIFactory_Release(factory);
5796 expected_output_count = GetSystemMetrics(SM_CMONITORS);
5797 ok(output_count == expected_output_count, "Expect output count %d, got %d\n",
5798 expected_output_count, output_count);
5801 struct message
5803 unsigned int message;
5804 BOOL check_wparam;
5805 WPARAM expect_wparam;
5808 static BOOL expect_no_messages;
5809 static const struct message *expect_messages;
5810 static const struct message *expect_messages_broken;
5812 static BOOL check_message(const struct message *expected,
5813 HWND hwnd, unsigned int message, WPARAM wparam, LPARAM lparam)
5815 if (expected->message != message)
5816 return FALSE;
5818 if (expected->check_wparam)
5820 ok(wparam == expected->expect_wparam,
5821 "Got unexpected wparam %lx for message %x, expected %lx.\n",
5822 wparam, message, expected->expect_wparam);
5825 return TRUE;
5828 static LRESULT CALLBACK test_wndproc(HWND hwnd, unsigned int message, WPARAM wparam, LPARAM lparam)
5830 ok(!expect_no_messages, "Got unexpected message %#x, hwnd %p, wparam %#lx, lparam %#lx.\n",
5831 message, hwnd, wparam, lparam);
5833 if (expect_messages)
5835 if (check_message(expect_messages, hwnd, message, wparam, lparam))
5836 ++expect_messages;
5839 if (expect_messages_broken)
5841 if (check_message(expect_messages_broken, hwnd, message, wparam, lparam))
5842 ++expect_messages_broken;
5845 return DefWindowProcA(hwnd, message, wparam, lparam);
5848 static void test_swapchain_window_messages(void)
5850 DXGI_SWAP_CHAIN_DESC swapchain_desc;
5851 IDXGISwapChain *swapchain;
5852 DXGI_MODE_DESC mode_desc;
5853 IDXGIFactory *factory;
5854 IDXGIAdapter *adapter;
5855 IDXGIDevice *device;
5856 ULONG refcount;
5857 WNDCLASSA wc;
5858 HWND window;
5859 HRESULT hr;
5861 static const struct message enter_fullscreen_messages[] =
5863 {WM_STYLECHANGING, TRUE, GWL_STYLE},
5864 {WM_STYLECHANGED, TRUE, GWL_STYLE},
5865 {WM_STYLECHANGING, TRUE, GWL_EXSTYLE},
5866 {WM_STYLECHANGED, TRUE, GWL_EXSTYLE},
5867 {WM_WINDOWPOSCHANGING, FALSE, 0},
5868 {WM_GETMINMAXINFO, FALSE, 0},
5869 {WM_NCCALCSIZE, FALSE, 0},
5870 {WM_WINDOWPOSCHANGED, FALSE, 0},
5871 {WM_MOVE, FALSE, 0},
5872 {WM_SIZE, FALSE, 0},
5873 {0, FALSE, 0},
5875 static const struct message enter_fullscreen_messages_vista[] =
5877 {WM_STYLECHANGING, TRUE, GWL_STYLE},
5878 {WM_STYLECHANGED, TRUE, GWL_STYLE},
5879 {WM_WINDOWPOSCHANGING, FALSE, 0},
5880 {WM_NCCALCSIZE, FALSE, 0},
5881 {WM_WINDOWPOSCHANGED, FALSE, 0},
5882 {WM_MOVE, FALSE, 0},
5883 {WM_SIZE, FALSE, 0},
5884 {WM_STYLECHANGING, TRUE, GWL_EXSTYLE},
5885 {WM_STYLECHANGED, TRUE, GWL_EXSTYLE},
5886 {WM_WINDOWPOSCHANGING, FALSE, 0},
5887 {WM_GETMINMAXINFO, FALSE, 0},
5888 {WM_NCCALCSIZE, FALSE, 0},
5889 {WM_WINDOWPOSCHANGED, FALSE, 0},
5890 {WM_SIZE, FALSE, 0},
5891 {0, FALSE, 0},
5893 static const struct message leave_fullscreen_messages[] =
5895 {WM_STYLECHANGING, TRUE, GWL_STYLE},
5896 {WM_STYLECHANGED, TRUE, GWL_STYLE},
5897 {WM_STYLECHANGING, TRUE, GWL_EXSTYLE},
5898 {WM_STYLECHANGED, TRUE, GWL_EXSTYLE},
5899 {WM_WINDOWPOSCHANGING, FALSE, 0},
5900 {WM_GETMINMAXINFO, FALSE, 0},
5901 {WM_NCCALCSIZE, FALSE, 0},
5902 {WM_WINDOWPOSCHANGED, FALSE, 0},
5903 {WM_MOVE, FALSE, 0},
5904 {WM_SIZE, FALSE, 0},
5905 {0, FALSE, 0},
5907 static const struct message resize_target_messages[] =
5909 {WM_WINDOWPOSCHANGING, FALSE, 0},
5910 {WM_GETMINMAXINFO, FALSE, 0},
5911 {WM_NCCALCSIZE, FALSE, 0},
5912 {WM_WINDOWPOSCHANGED, FALSE, 0},
5913 {WM_SIZE, FALSE, 0},
5914 {0, FALSE, 0},
5917 if (!(device = create_device(0)))
5919 skip("Failed to create device.\n");
5920 return;
5923 memset(&wc, 0, sizeof(wc));
5924 wc.lpfnWndProc = test_wndproc;
5925 wc.lpszClassName = "dxgi_test_wndproc_wc";
5926 ok(RegisterClassA(&wc), "Failed to register window class.\n");
5927 window = CreateWindowA("dxgi_test_wndproc_wc", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0);
5928 ok(!!window, "Failed to create window.\n");
5930 hr = IDXGIDevice_GetAdapter(device, &adapter);
5931 ok(hr == S_OK, "Failed to get adapter, hr %#x.\n", hr);
5932 hr = IDXGIAdapter_GetParent(adapter, &IID_IDXGIFactory, (void **)&factory);
5933 ok(hr == S_OK, "Failed to get parent, hr %#x.\n", hr);
5934 IDXGIAdapter_Release(adapter);
5936 swapchain_desc.BufferDesc.Width = 800;
5937 swapchain_desc.BufferDesc.Height = 600;
5938 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
5939 swapchain_desc.BufferDesc.RefreshRate.Denominator = 60;
5940 swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
5941 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
5942 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
5943 swapchain_desc.SampleDesc.Count = 1;
5944 swapchain_desc.SampleDesc.Quality = 0;
5945 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
5946 swapchain_desc.BufferCount = 1;
5947 swapchain_desc.OutputWindow = window;
5948 swapchain_desc.Windowed = TRUE;
5949 swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
5950 swapchain_desc.Flags = 0;
5952 /* create swapchain */
5953 flush_events();
5954 expect_no_messages = TRUE;
5955 hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown *)device, &swapchain_desc, &swapchain);
5956 ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
5957 flush_events();
5958 expect_no_messages = FALSE;
5960 /* resize target */
5961 expect_messages = resize_target_messages;
5962 memset(&mode_desc, 0, sizeof(mode_desc));
5963 mode_desc.Width = 800;
5964 mode_desc.Height = 600;
5965 hr = IDXGISwapChain_ResizeTarget(swapchain, &mode_desc);
5966 ok(hr == S_OK, "Failed to resize target, hr %#x.\n", hr);
5967 flush_events();
5968 ok(!expect_messages->message, "Expected message %#x.\n", expect_messages->message);
5970 expect_messages = resize_target_messages;
5971 memset(&mode_desc, 0, sizeof(mode_desc));
5972 mode_desc.Width = 400;
5973 mode_desc.Height = 200;
5974 hr = IDXGISwapChain_ResizeTarget(swapchain, &mode_desc);
5975 ok(hr == S_OK, "Failed to resize target, hr %#x.\n", hr);
5976 flush_events();
5977 ok(!expect_messages->message, "Expected message %#x.\n", expect_messages->message);
5979 /* enter fullscreen */
5980 expect_messages = enter_fullscreen_messages;
5981 expect_messages_broken = enter_fullscreen_messages_vista;
5982 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
5983 ok(hr == S_OK || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE
5984 || broken(hr == DXGI_ERROR_UNSUPPORTED), /* Win 7 testbot */
5985 "Failed to enter fullscreen, hr %#x.\n", hr);
5986 if (FAILED(hr))
5988 skip("Could not change fullscreen state.\n");
5989 goto done;
5991 flush_events();
5992 todo_wine
5993 ok(!expect_messages->message || broken(!expect_messages_broken->message),
5994 "Expected message %#x or %#x.\n",
5995 expect_messages->message, expect_messages_broken->message);
5996 expect_messages_broken = NULL;
5998 /* leave fullscreen */
5999 expect_messages = leave_fullscreen_messages;
6000 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
6001 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6002 flush_events();
6003 ok(!expect_messages->message, "Expected message %#x.\n", expect_messages->message);
6004 expect_messages = NULL;
6006 refcount = IDXGISwapChain_Release(swapchain);
6007 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
6009 /* create fullscreen swapchain */
6010 DestroyWindow(window);
6011 window = CreateWindowA("dxgi_test_wndproc_wc", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0);
6012 ok(!!window, "Failed to create window.\n");
6013 swapchain_desc.OutputWindow = window;
6014 swapchain_desc.Windowed = FALSE;
6015 swapchain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
6016 flush_events();
6018 expect_messages = enter_fullscreen_messages;
6019 expect_messages_broken = enter_fullscreen_messages_vista;
6020 hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown *)device, &swapchain_desc, &swapchain);
6021 ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
6022 flush_events();
6023 todo_wine
6024 ok(!expect_messages->message || broken(!expect_messages_broken->message),
6025 "Expected message %#x or %#x.\n",
6026 expect_messages->message, expect_messages_broken->message);
6027 expect_messages_broken = NULL;
6029 /* leave fullscreen */
6030 expect_messages = leave_fullscreen_messages;
6031 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
6032 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6033 flush_events();
6034 ok(!expect_messages->message, "Expected message %#x.\n", expect_messages->message);
6035 expect_messages = NULL;
6037 done:
6038 refcount = IDXGISwapChain_Release(swapchain);
6039 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
6040 DestroyWindow(window);
6042 refcount = IDXGIDevice_Release(device);
6043 ok(!refcount, "Device has %u references left.\n", refcount);
6044 refcount = IDXGIFactory_Release(factory);
6045 ok(!refcount, "Factory has %u references left.\n", refcount);
6047 UnregisterClassA("dxgi_test_wndproc_wc", GetModuleHandleA(NULL));
6050 static void test_swapchain_window_styles(void)
6052 LONG style, exstyle, fullscreen_style, fullscreen_exstyle;
6053 DXGI_SWAP_CHAIN_DESC swapchain_desc;
6054 IDXGISwapChain *swapchain;
6055 IDXGIFactory *factory;
6056 IDXGIAdapter *adapter;
6057 IDXGIDevice *device;
6058 ULONG refcount;
6059 unsigned int i;
6060 HRESULT hr;
6062 static const struct
6064 LONG style, exstyle;
6065 LONG expected_style, expected_exstyle;
6067 tests[] =
6069 {WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, 0,
6070 WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS,
6071 WS_EX_WINDOWEDGE},
6072 {WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE, 0,
6073 WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CLIPSIBLINGS | WS_VISIBLE,
6074 WS_EX_WINDOWEDGE},
6075 {WS_OVERLAPPED | WS_VISIBLE, 0,
6076 WS_OVERLAPPED | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CAPTION, WS_EX_WINDOWEDGE},
6077 {WS_OVERLAPPED | WS_MAXIMIZE, 0,
6078 WS_OVERLAPPED | WS_MAXIMIZE | WS_CLIPSIBLINGS | WS_CAPTION, WS_EX_WINDOWEDGE},
6079 {WS_OVERLAPPED | WS_MINIMIZE, 0,
6080 WS_OVERLAPPED | WS_MINIMIZE | WS_CLIPSIBLINGS | WS_CAPTION, WS_EX_WINDOWEDGE},
6081 {WS_CAPTION | WS_DISABLED, WS_EX_TOPMOST,
6082 WS_CAPTION | WS_DISABLED | WS_CLIPSIBLINGS, WS_EX_TOPMOST | WS_EX_WINDOWEDGE},
6083 {WS_CAPTION | WS_DISABLED | WS_VISIBLE, WS_EX_TOPMOST,
6084 WS_CAPTION | WS_DISABLED | WS_VISIBLE | WS_CLIPSIBLINGS, WS_EX_TOPMOST | WS_EX_WINDOWEDGE},
6085 {WS_CAPTION | WS_SYSMENU | WS_VISIBLE, WS_EX_APPWINDOW,
6086 WS_CAPTION | WS_SYSMENU | WS_VISIBLE | WS_CLIPSIBLINGS, WS_EX_APPWINDOW | WS_EX_WINDOWEDGE},
6087 {WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_BORDER | WS_DLGFRAME
6088 | WS_VSCROLL | WS_HSCROLL | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
6090 WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_BORDER | WS_DLGFRAME
6091 | WS_VSCROLL | WS_HSCROLL | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,
6092 WS_EX_WINDOWEDGE},
6095 if (!(device = create_device(0)))
6097 skip("Failed to create device.\n");
6098 return;
6101 hr = IDXGIDevice_GetAdapter(device, &adapter);
6102 ok(hr == S_OK, "Failed to get adapter, hr %#x.\n", hr);
6103 hr = IDXGIAdapter_GetParent(adapter, &IID_IDXGIFactory, (void **)&factory);
6104 ok(hr == S_OK, "Failed to get parent, hr %#x.\n", hr);
6105 IDXGIAdapter_Release(adapter);
6107 swapchain_desc.BufferDesc.Width = 800;
6108 swapchain_desc.BufferDesc.Height = 600;
6109 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
6110 swapchain_desc.BufferDesc.RefreshRate.Denominator = 60;
6111 swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
6112 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
6113 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
6114 swapchain_desc.SampleDesc.Count = 1;
6115 swapchain_desc.SampleDesc.Quality = 0;
6116 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
6117 swapchain_desc.BufferCount = 1;
6118 swapchain_desc.Windowed = TRUE;
6119 swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
6120 swapchain_desc.Flags = 0;
6122 for (i = 0; i < ARRAY_SIZE(tests); ++i)
6124 swapchain_desc.OutputWindow = CreateWindowExA(tests[i].exstyle, "static", "dxgi_test",
6125 tests[i].style, 0, 0, 400, 200, 0, 0, 0, 0);
6127 style = GetWindowLongA(swapchain_desc.OutputWindow, GWL_STYLE);
6128 exstyle = GetWindowLongA(swapchain_desc.OutputWindow, GWL_EXSTYLE);
6129 ok(style == tests[i].expected_style, "Test %u: Got style %#x, expected %#x.\n",
6130 i, style, tests[i].expected_style);
6131 ok(exstyle == tests[i].expected_exstyle, "Test %u: Got exstyle %#x, expected %#x.\n",
6132 i, exstyle, tests[i].expected_exstyle);
6134 fullscreen_style = tests[i].expected_style & ~(WS_POPUP | WS_MAXIMIZEBOX
6135 | WS_MINIMIZEBOX | WS_THICKFRAME | WS_SYSMENU | WS_DLGFRAME | WS_BORDER);
6136 fullscreen_exstyle = tests[i].expected_exstyle & ~(WS_EX_DLGMODALFRAME
6137 | WS_EX_TOOLWINDOW | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_CONTEXTHELP);
6138 fullscreen_exstyle |= WS_EX_TOPMOST;
6140 hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown *)device, &swapchain_desc, &swapchain);
6141 ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
6143 style = GetWindowLongA(swapchain_desc.OutputWindow, GWL_STYLE);
6144 exstyle = GetWindowLongA(swapchain_desc.OutputWindow, GWL_EXSTYLE);
6145 ok(style == tests[i].expected_style, "Test %u: Got style %#x, expected %#x.\n",
6146 i, style, tests[i].expected_style);
6147 ok(exstyle == tests[i].expected_exstyle, "Test %u: Got exstyle %#x, expected %#x.\n",
6148 i, exstyle, tests[i].expected_exstyle);
6150 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
6151 ok(hr == S_OK || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE
6152 || broken(hr == DXGI_ERROR_UNSUPPORTED), /* Win 7 testbot */
6153 "Failed to set fullscreen state, hr %#x.\n", hr);
6154 if (SUCCEEDED(hr))
6156 style = GetWindowLongA(swapchain_desc.OutputWindow, GWL_STYLE);
6157 exstyle = GetWindowLongA(swapchain_desc.OutputWindow, GWL_EXSTYLE);
6158 todo_wine
6159 ok(style == fullscreen_style, "Test %u: Got style %#x, expected %#x.\n",
6160 i, style, fullscreen_style);
6161 ok(exstyle == fullscreen_exstyle, "Test %u: Got exstyle %#x, expected %#x.\n",
6162 i, exstyle, fullscreen_exstyle);
6164 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
6165 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6167 else
6169 skip("Test %u: Could not change fullscreen state.\n", i);
6172 style = GetWindowLongA(swapchain_desc.OutputWindow, GWL_STYLE);
6173 exstyle = GetWindowLongA(swapchain_desc.OutputWindow, GWL_EXSTYLE);
6174 ok(style == tests[i].expected_style, "Test %u: Got style %#x, expected %#x.\n",
6175 i, style, tests[i].expected_style);
6176 ok(exstyle == tests[i].expected_exstyle, "Test %u: Got exstyle %#x, expected %#x.\n",
6177 i, exstyle, tests[i].expected_exstyle);
6179 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
6180 ok(hr == S_OK || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE
6181 || broken(hr == DXGI_ERROR_UNSUPPORTED), /* Win 7 testbot */
6182 "Failed to set fullscreen state, hr %#x.\n", hr);
6183 if (SUCCEEDED(hr))
6185 style = GetWindowLongA(swapchain_desc.OutputWindow, GWL_STYLE);
6186 exstyle = GetWindowLongA(swapchain_desc.OutputWindow, GWL_EXSTYLE);
6187 todo_wine
6188 ok(style == fullscreen_style, "Test %u: Got style %#x, expected %#x.\n",
6189 i, style, fullscreen_style);
6190 ok(exstyle == fullscreen_exstyle, "Test %u: Got exstyle %#x, expected %#x.\n",
6191 i, exstyle, fullscreen_exstyle);
6193 SetWindowLongW(swapchain_desc.OutputWindow, GWL_STYLE, fullscreen_style);
6194 SetWindowLongW(swapchain_desc.OutputWindow, GWL_EXSTYLE, fullscreen_exstyle);
6196 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
6197 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6199 style = GetWindowLongA(swapchain_desc.OutputWindow, GWL_STYLE);
6200 exstyle = GetWindowLongA(swapchain_desc.OutputWindow, GWL_EXSTYLE);
6201 todo_wine
6202 ok(style == tests[i].expected_style, "Test %u: Got style %#x, expected %#x.\n",
6203 i, style, tests[i].expected_style);
6204 todo_wine
6205 ok(exstyle == tests[i].expected_exstyle, "Test %u: Got exstyle %#x, expected %#x.\n",
6206 i, exstyle, tests[i].expected_exstyle);
6208 else
6210 skip("Test %u: Could not change fullscreen state.\n", i);
6213 refcount = IDXGISwapChain_Release(swapchain);
6214 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
6216 style = GetWindowLongA(swapchain_desc.OutputWindow, GWL_STYLE);
6217 exstyle = GetWindowLongA(swapchain_desc.OutputWindow, GWL_EXSTYLE);
6218 todo_wine
6219 ok(style == tests[i].expected_style, "Test %u: Got style %#x, expected %#x.\n",
6220 i, style, tests[i].expected_style);
6221 todo_wine
6222 ok(exstyle == tests[i].expected_exstyle, "Test %u: Got exstyle %#x, expected %#x.\n",
6223 i, exstyle, tests[i].expected_exstyle);
6225 DestroyWindow(swapchain_desc.OutputWindow);
6228 refcount = IDXGIDevice_Release(device);
6229 ok(!refcount, "Device has %u references left.\n", refcount);
6230 refcount = IDXGIFactory_Release(factory);
6231 ok(!refcount, "Factory has %u references left.\n", refcount);
6234 static void test_gamma_control(void)
6236 DXGI_GAMMA_CONTROL_CAPABILITIES caps;
6237 DXGI_SWAP_CHAIN_DESC swapchain_desc;
6238 IDXGISwapChain *swapchain;
6239 DXGI_GAMMA_CONTROL gamma;
6240 IDXGIFactory *factory;
6241 IDXGIAdapter *adapter;
6242 IDXGIDevice *device;
6243 IDXGIOutput *output;
6244 unsigned int i;
6245 ULONG refcount;
6246 HRESULT hr;
6248 if (!(device = create_device(0)))
6250 skip("Failed to create device.\n");
6251 return;
6254 hr = IDXGIDevice_GetAdapter(device, &adapter);
6255 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6257 hr = IDXGIAdapter_EnumOutputs(adapter, 0, &output);
6258 if (hr == DXGI_ERROR_NOT_FOUND)
6260 skip("Adapter doesn't have any outputs.\n");
6261 IDXGIAdapter_Release(adapter);
6262 IDXGIDevice_Release(device);
6263 return;
6265 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6267 hr = IDXGIOutput_GetGammaControlCapabilities(output, &caps);
6268 todo_wine
6269 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
6270 IDXGIOutput_Release(output);
6272 swapchain_desc.BufferDesc.Width = 640;
6273 swapchain_desc.BufferDesc.Height = 480;
6274 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
6275 swapchain_desc.BufferDesc.RefreshRate.Denominator = 60;
6276 swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
6277 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
6278 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
6279 swapchain_desc.SampleDesc.Count = 1;
6280 swapchain_desc.SampleDesc.Quality = 0;
6281 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
6282 swapchain_desc.BufferCount = 1;
6283 swapchain_desc.OutputWindow = create_window();
6284 swapchain_desc.Windowed = TRUE;
6285 swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
6286 swapchain_desc.Flags = 0;
6288 hr = IDXGIAdapter_GetParent(adapter, &IID_IDXGIFactory, (void **)&factory);
6289 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6291 hr = IDXGIFactory_CreateSwapChain(factory, (IUnknown *)device, &swapchain_desc, &swapchain);
6292 ok(hr == S_OK, "Failed to create swapchain, hr %#x.\n", hr);
6293 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
6294 ok(hr == S_OK || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE
6295 || broken(hr == DXGI_ERROR_UNSUPPORTED), /* Win 7 testbot */
6296 "Failed to enter fullscreen, hr %#x.\n", hr);
6297 if (FAILED(hr))
6299 skip("Could not change fullscreen state.\n");
6300 goto done;
6303 hr = IDXGISwapChain_GetContainingOutput(swapchain, &output);
6304 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6306 memset(&caps, 0, sizeof(caps));
6307 hr = IDXGIOutput_GetGammaControlCapabilities(output, &caps);
6308 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6310 ok(caps.MaxConvertedValue > caps.MinConvertedValue
6311 || broken(caps.MaxConvertedValue == 0.0f && caps.MinConvertedValue == 1.0f) /* WARP */,
6312 "Expected max gamma value (%.8e) to be bigger than min value (%.8e).\n",
6313 caps.MaxConvertedValue, caps.MinConvertedValue);
6315 for (i = 1; i < caps.NumGammaControlPoints; ++i)
6317 ok(caps.ControlPointPositions[i] > caps.ControlPointPositions[i - 1],
6318 "Expected control point positions to be strictly monotonically increasing (%.8e > %.8e).\n",
6319 caps.ControlPointPositions[i], caps.ControlPointPositions[i - 1]);
6322 memset(&gamma, 0, sizeof(gamma));
6323 hr = IDXGIOutput_GetGammaControl(output, &gamma);
6324 todo_wine
6325 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
6326 hr = IDXGIOutput_SetGammaControl(output, &gamma);
6327 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6329 IDXGIOutput_Release(output);
6331 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
6332 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6334 done:
6335 refcount = IDXGISwapChain_Release(swapchain);
6336 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
6337 DestroyWindow(swapchain_desc.OutputWindow);
6339 IDXGIAdapter_Release(adapter);
6340 refcount = IDXGIDevice_Release(device);
6341 ok(!refcount, "Device has %u references left.\n", refcount);
6342 refcount = IDXGIFactory_Release(factory);
6343 ok(!refcount, "Factory has %u references left.\n", refcount);
6346 static void test_window_association(IUnknown *device, BOOL is_d3d12)
6348 DXGI_SWAP_CHAIN_DESC swapchain_desc;
6349 LONG_PTR original_wndproc, wndproc;
6350 IDXGIFactory *factory, *factory2;
6351 IDXGISwapChain *swapchain;
6352 IDXGIOutput *output;
6353 HWND hwnd, hwnd2;
6354 BOOL fullscreen;
6355 unsigned int i;
6356 ULONG refcount;
6357 HRESULT hr;
6359 static const struct
6361 UINT flag;
6362 BOOL expect_fullscreen;
6363 BOOL broken_d3d10;
6365 tests[] =
6367 /* There are two reasons why VK_TAB and VK_ESC are not tested here:
6369 * - Posting them to the window doesn't exit fullscreen like
6370 * Alt+Enter does. Alt+Tab and Alt+Esc are handled somewhere else.
6371 * E.g., not calling IDXGISwapChain::Present() will break Alt+Tab
6372 * and Alt+Esc while Alt+Enter will still function.
6374 * - Posting them hangs the posting thread. Another thread that keeps
6375 * sending input is needed to avoid the hang. The hang is not
6376 * because of flush_events(). */
6377 {0, TRUE},
6378 {0, FALSE},
6379 {DXGI_MWA_NO_WINDOW_CHANGES, FALSE},
6380 {DXGI_MWA_NO_WINDOW_CHANGES, FALSE},
6381 {DXGI_MWA_NO_ALT_ENTER, FALSE, TRUE},
6382 {DXGI_MWA_NO_ALT_ENTER, FALSE},
6383 {DXGI_MWA_NO_PRINT_SCREEN, TRUE},
6384 {DXGI_MWA_NO_PRINT_SCREEN, FALSE},
6385 {0, TRUE},
6386 {0, FALSE}
6389 swapchain_desc.BufferDesc.Width = 640;
6390 swapchain_desc.BufferDesc.Height = 480;
6391 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
6392 swapchain_desc.BufferDesc.RefreshRate.Denominator = 1;
6393 swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
6394 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
6395 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
6396 swapchain_desc.SampleDesc.Count = 1;
6397 swapchain_desc.SampleDesc.Quality = 0;
6398 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
6399 swapchain_desc.BufferCount = is_d3d12 ? 2 : 1;
6400 swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0);
6401 swapchain_desc.Windowed = TRUE;
6402 swapchain_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
6403 swapchain_desc.Flags = 0;
6405 original_wndproc = GetWindowLongPtrW(swapchain_desc.OutputWindow, GWLP_WNDPROC);
6407 hwnd2 = CreateWindowA("static", "dxgi_test2", 0, 0, 0, 400, 200, 0, 0, 0, 0);
6408 hr = CreateDXGIFactory(&IID_IDXGIFactory, (void **)&factory2);
6409 ok(hr == S_OK, "Failed to create DXGI factory, hr %#x.\n", hr);
6411 get_factory(device, is_d3d12, &factory);
6413 hr = IDXGIFactory_GetWindowAssociation(factory, NULL);
6414 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
6416 for (i = 0; i <= DXGI_MWA_VALID; ++i)
6418 hr = IDXGIFactory_MakeWindowAssociation(factory, NULL, i);
6419 ok(hr == S_OK, "Got unexpected hr %#x for flags %#x.\n", hr, i);
6421 hr = IDXGIFactory_MakeWindowAssociation(factory, swapchain_desc.OutputWindow, i);
6422 ok(hr == S_OK, "Got unexpected hr %#x for flags %#x.\n", hr, i);
6424 wndproc = GetWindowLongPtrW(swapchain_desc.OutputWindow, GWLP_WNDPROC);
6425 ok(wndproc == original_wndproc, "Got unexpected wndproc %#lx, expected %#lx for flags %#x.\n",
6426 wndproc, original_wndproc, i);
6428 hwnd = (HWND)0xdeadbeef;
6429 hr = IDXGIFactory_GetWindowAssociation(factory, &hwnd);
6430 ok(hr == S_OK, "Got unexpected hr %#x for flags %#x.\n", hr, i);
6431 /* Apparently GetWindowAssociation() always returns NULL, even when
6432 * MakeWindowAssociation() and GetWindowAssociation() are both
6433 * successfully called. */
6434 ok(!hwnd, "Expect null associated window.\n");
6437 hr = IDXGIFactory_MakeWindowAssociation(factory, swapchain_desc.OutputWindow, DXGI_MWA_VALID + 1);
6438 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
6440 /* Alt+Enter tests. */
6441 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
6442 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6444 wndproc = GetWindowLongPtrW(swapchain_desc.OutputWindow, GWLP_WNDPROC);
6445 ok(wndproc == original_wndproc, "Got unexpected wndproc %#lx, expected %#lx.\n", wndproc, original_wndproc);
6447 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
6448 ok(hr == S_OK || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE
6449 || broken(hr == DXGI_ERROR_UNSUPPORTED) /* Windows 7 testbot */,
6450 "Got unexpected hr %#x.\n", hr);
6451 if (FAILED(hr))
6453 skip("Could not change fullscreen state.\n");
6455 else
6457 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
6458 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6460 for (i = 0; i < ARRAY_SIZE(tests); ++i)
6462 /* First associate a window with the opposite flags. */
6463 hr = IDXGIFactory_MakeWindowAssociation(factory, hwnd2, ~tests[i].flag & DXGI_MWA_VALID);
6464 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
6466 /* Associate the current test window. */
6467 hwnd = tests[i].flag ? swapchain_desc.OutputWindow : NULL;
6468 hr = IDXGIFactory_MakeWindowAssociation(factory, hwnd, tests[i].flag);
6469 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
6471 /* Associating a new test window doesn't override the old window. */
6472 hr = IDXGIFactory_MakeWindowAssociation(factory, hwnd2, ~tests[i].flag & DXGI_MWA_VALID);
6473 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
6475 /* Associations with a different factory don't affect the existing
6476 * association. */
6477 hr = IDXGIFactory_MakeWindowAssociation(factory2, hwnd, ~tests[i].flag & DXGI_MWA_VALID);
6478 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
6480 /* Post synthesized Alt + VK_RETURN WM_SYSKEYDOWN. */
6481 PostMessageA(swapchain_desc.OutputWindow, WM_SYSKEYDOWN, VK_RETURN,
6482 (MapVirtualKeyA(VK_RETURN, MAPVK_VK_TO_VSC) << 16) | 0x20000001);
6483 flush_events();
6484 output = NULL;
6485 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, &output);
6486 ok(hr == S_OK, "Test %u: Got unexpected hr %#x.\n", i, hr);
6487 ok(fullscreen == tests[i].expect_fullscreen
6488 || broken(tests[i].broken_d3d10 && fullscreen),
6489 "Test %u: Got unexpected fullscreen %#x.\n", i, fullscreen);
6490 ok(fullscreen ? !!output : !output, "Test %u: Got wrong output.\n", i);
6491 if (output)
6492 IDXGIOutput_Release(output);
6494 wndproc = GetWindowLongPtrW(swapchain_desc.OutputWindow, GWLP_WNDPROC);
6495 ok(wndproc == original_wndproc, "Test %u: Got unexpected wndproc %#lx, expected %#lx.\n",
6496 i, wndproc, original_wndproc);
6500 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
6501 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6503 refcount = IDXGIFactory_Release(factory2);
6504 ok(!refcount, "Factory has %u references left.\n", refcount);
6505 DestroyWindow(hwnd2);
6507 refcount = IDXGISwapChain_Release(swapchain);
6508 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
6509 DestroyWindow(swapchain_desc.OutputWindow);
6511 refcount = IDXGIFactory_Release(factory);
6512 ok(refcount == !is_d3d12, "IDXGIFactory has %u references left.\n", refcount);
6515 static void test_output_ownership(IUnknown *device, BOOL is_d3d12)
6517 D3DKMT_OPENADAPTERFROMGDIDISPLAYNAME open_adapter_gdi_desc;
6518 D3DKMT_CHECKVIDPNEXCLUSIVEOWNERSHIP check_ownership_desc;
6519 D3DKMT_CLOSEADAPTER close_adapter_desc;
6520 DXGI_SWAP_CHAIN_DESC swapchain_desc;
6521 DXGI_OUTPUT_DESC output_desc;
6522 IDXGISwapChain *swapchain;
6523 IDXGIFactory *factory;
6524 IDXGIAdapter *adapter;
6525 IDXGIOutput *output;
6526 BOOL fullscreen;
6527 NTSTATUS status;
6528 ULONG refcount;
6529 HRESULT hr;
6531 if (!pD3DKMTCheckVidPnExclusiveOwnership
6532 || pD3DKMTCheckVidPnExclusiveOwnership(NULL) == STATUS_PROCEDURE_NOT_FOUND)
6534 win_skip("D3DKMTCheckVidPnExclusiveOwnership() is unavailable.\n");
6535 return;
6538 get_factory(device, is_d3d12, &factory);
6539 adapter = get_adapter(device, is_d3d12);
6540 if (!adapter)
6542 skip("Failed to get adapter on Direct3D %d.\n", is_d3d12 ? 12 : 10);
6543 IDXGIFactory_Release(factory);
6544 return;
6547 hr = IDXGIAdapter_EnumOutputs(adapter, 0, &output);
6548 IDXGIAdapter_Release(adapter);
6549 if (hr == DXGI_ERROR_NOT_FOUND)
6551 skip("Adapter doesn't have any outputs.\n");
6552 IDXGIFactory_Release(factory);
6553 return;
6555 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6557 hr = IDXGIOutput_GetDesc(output, &output_desc);
6558 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6560 lstrcpyW(open_adapter_gdi_desc.DeviceName, output_desc.DeviceName);
6561 status = pD3DKMTOpenAdapterFromGdiDisplayName(&open_adapter_gdi_desc);
6562 ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status);
6564 check_ownership_desc.hAdapter = open_adapter_gdi_desc.hAdapter;
6565 check_ownership_desc.VidPnSourceId = open_adapter_gdi_desc.VidPnSourceId;
6566 status = get_expected_vidpn_exclusive_ownership(&check_ownership_desc, STATUS_SUCCESS);
6567 ok(status == STATUS_SUCCESS, "Got unexpected status %#x, expected %#x.\n", status,
6568 STATUS_SUCCESS);
6570 swapchain_desc.BufferDesc.Width = 800;
6571 swapchain_desc.BufferDesc.Height = 600;
6572 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
6573 swapchain_desc.BufferDesc.RefreshRate.Denominator = 1;
6574 swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
6575 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
6576 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
6577 swapchain_desc.SampleDesc.Count = 1;
6578 swapchain_desc.SampleDesc.Quality = 0;
6579 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
6580 swapchain_desc.BufferCount = is_d3d12 ? 2 : 1;
6581 swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, NULL, NULL, NULL, NULL);
6582 swapchain_desc.Windowed = TRUE;
6583 swapchain_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
6584 swapchain_desc.Flags = 0;
6585 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
6586 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6588 /* Swapchain in fullscreen mode. */
6589 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, output);
6590 /* DXGI_ERROR_NOT_CURRENTLY_AVAILABLE on some machines.
6591 * DXGI_ERROR_UNSUPPORTED on the Windows 7 testbot. */
6592 if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE || broken(hr == DXGI_ERROR_UNSUPPORTED))
6594 skip("Failed to change fullscreen state.\n");
6595 goto done;
6597 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6598 fullscreen = FALSE;
6599 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
6600 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6601 ok(fullscreen, "Got unexpected fullscreen state.\n");
6602 /* Win10 1909 doesn't seem to grab output exclusive ownership.
6603 * And all output ownership calls return S_OK on D3D10 and D3D12 with 1909. */
6604 if (is_d3d12)
6606 status = get_expected_vidpn_exclusive_ownership(&check_ownership_desc, STATUS_SUCCESS);
6607 ok(status == STATUS_SUCCESS, "Got unexpected status %#x, expected %#x.\n", status,
6608 STATUS_SUCCESS);
6610 else
6612 status = get_expected_vidpn_exclusive_ownership(&check_ownership_desc,
6613 STATUS_GRAPHICS_PRESENT_OCCLUDED);
6614 todo_wine ok(status == STATUS_GRAPHICS_PRESENT_OCCLUDED ||
6615 broken(status == STATUS_SUCCESS), /* Win10 1909 */
6616 "Got unexpected status %#x, expected %#x.\n", status,
6617 STATUS_GRAPHICS_PRESENT_OCCLUDED);
6619 hr = IDXGIOutput_TakeOwnership(output, NULL, FALSE);
6620 ok(hr == DXGI_ERROR_INVALID_CALL || broken(hr == S_OK), /* Win10 1909 */
6621 "Got unexpected hr %#x.\n", hr);
6622 hr = IDXGIOutput_TakeOwnership(output, NULL, TRUE);
6623 ok(hr == DXGI_ERROR_INVALID_CALL || broken(hr == S_OK), /* Win10 1909 */
6624 "Got unexpected hr %#x.\n", hr);
6625 hr = IDXGIOutput_TakeOwnership(output, device, FALSE);
6626 if (is_d3d12)
6627 todo_wine ok(hr == E_NOINTERFACE || hr == S_OK, "Got unexpected hr %#x.\n", hr);
6628 else
6629 todo_wine ok(hr == E_INVALIDARG || broken(hr == S_OK), /* Win10 1909 */
6630 "Got unexpected hr %#x.\n", hr);
6631 hr = IDXGIOutput_TakeOwnership(output, device, TRUE);
6632 ok(hr == E_NOINTERFACE || hr == S_OK, "Got unexpected hr %#x.\n", hr);
6633 IDXGIOutput_ReleaseOwnership(output);
6634 status = get_expected_vidpn_exclusive_ownership(&check_ownership_desc, STATUS_SUCCESS);
6635 ok(status == STATUS_SUCCESS, "Got unexpected status %#x, expected %#x.\n", status,
6636 STATUS_SUCCESS);
6638 /* IDXGIOutput_TakeOwnership always returns E_NOINTERFACE for d3d12. Tests
6639 * finished. */
6640 if (is_d3d12)
6641 goto done;
6643 hr = IDXGIOutput_TakeOwnership(output, device, FALSE);
6644 ok(hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE || broken(hr == S_OK), /* Win10 1909 */
6645 "Got unexpected hr %#x.\n", hr);
6646 IDXGIOutput_ReleaseOwnership(output);
6648 hr = IDXGIOutput_TakeOwnership(output, device, TRUE);
6649 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6650 /* Note that the "exclusive" parameter to IDXGIOutput_TakeOwnership()
6651 * seems to behave opposite to what's described by MSDN. */
6652 status = get_expected_vidpn_exclusive_ownership(&check_ownership_desc,
6653 STATUS_GRAPHICS_PRESENT_OCCLUDED);
6654 ok(status == STATUS_GRAPHICS_PRESENT_OCCLUDED ||
6655 broken(status == STATUS_SUCCESS), /* Win10 1909 */
6656 "Got unexpected status %#x, expected %#x.\n", status, STATUS_GRAPHICS_PRESENT_OCCLUDED);
6657 hr = IDXGIOutput_TakeOwnership(output, device, FALSE);
6658 ok(hr == E_INVALIDARG || broken(hr == S_OK) /* Win10 1909 */, "Got unexpected hr %#x.\n", hr);
6659 IDXGIOutput_ReleaseOwnership(output);
6661 /* Swapchain in windowed mode. */
6662 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
6663 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6664 fullscreen = TRUE;
6665 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
6666 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6667 ok(!fullscreen, "Unexpected fullscreen state.\n");
6668 status = get_expected_vidpn_exclusive_ownership(&check_ownership_desc, STATUS_SUCCESS);
6669 ok(status == STATUS_SUCCESS, "Got unexpected status %#x, expected %#x.\n", status,
6670 STATUS_SUCCESS);
6672 hr = IDXGIOutput_TakeOwnership(output, device, FALSE);
6673 ok(hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE || broken(hr == S_OK), /* Win10 1909 */
6674 "Got unexpected hr %#x.\n", hr);
6676 hr = IDXGIOutput_TakeOwnership(output, device, TRUE);
6677 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6678 status = get_expected_vidpn_exclusive_ownership(&check_ownership_desc,
6679 STATUS_GRAPHICS_PRESENT_OCCLUDED);
6680 ok(status == STATUS_GRAPHICS_PRESENT_OCCLUDED || broken(hr == S_OK), /* Win10 1909 */
6681 "Got unexpected status %#x, expected %#x.\n", status, STATUS_GRAPHICS_PRESENT_OCCLUDED);
6682 IDXGIOutput_ReleaseOwnership(output);
6683 status = get_expected_vidpn_exclusive_ownership(&check_ownership_desc, STATUS_SUCCESS);
6684 ok(status == STATUS_SUCCESS, "Got unexpected status %#x, expected %#x.\n", status,
6685 STATUS_SUCCESS);
6687 done:
6688 IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
6689 wait_device_idle(device);
6691 IDXGIOutput_Release(output);
6692 IDXGISwapChain_Release(swapchain);
6693 DestroyWindow(swapchain_desc.OutputWindow);
6694 refcount = IDXGIFactory_Release(factory);
6695 ok(refcount == !is_d3d12, "Got unexpected refcount %u.\n", refcount);
6697 close_adapter_desc.hAdapter = open_adapter_gdi_desc.hAdapter;
6698 status = pD3DKMTCloseAdapter(&close_adapter_desc);
6699 ok(status == STATUS_SUCCESS, "Got unexpected status %#x.\n", status);
6702 static void test_cursor_clipping(IUnknown *device, BOOL is_d3d12)
6704 unsigned int adapter_idx, output_idx, mode_idx, mode_count;
6705 DXGI_SWAP_CHAIN_DESC swapchain_desc;
6706 DXGI_OUTPUT_DESC output_desc;
6707 IDXGIAdapter *adapter = NULL;
6708 RECT virtual_rect, clip_rect;
6709 unsigned int width, height;
6710 IDXGISwapChain *swapchain;
6711 DXGI_MODE_DESC *modes;
6712 IDXGIFactory *factory;
6713 IDXGIOutput *output;
6714 ULONG refcount;
6715 HRESULT hr;
6716 BOOL ret;
6718 get_factory(device, is_d3d12, &factory);
6720 swapchain_desc.SampleDesc.Count = 1;
6721 swapchain_desc.SampleDesc.Quality = 0;
6722 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
6723 swapchain_desc.BufferCount = is_d3d12 ? 2 : 1;
6724 swapchain_desc.Windowed = TRUE;
6725 swapchain_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
6726 swapchain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
6728 for (adapter_idx = 0; SUCCEEDED(IDXGIFactory_EnumAdapters(factory, adapter_idx, &adapter));
6729 ++adapter_idx)
6731 for (output_idx = 0; SUCCEEDED(IDXGIAdapter_EnumOutputs(adapter, output_idx, &output));
6732 ++output_idx)
6734 hr = IDXGIOutput_GetDisplayModeList(output, DXGI_FORMAT_R8G8B8A8_UNORM, 0, &mode_count,
6735 NULL);
6736 ok(SUCCEEDED(hr) || broken(hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE), /* Win 7 TestBots */
6737 "Adapter %u output %u: GetDisplayModeList failed, hr %#x.\n", adapter_idx,
6738 output_idx, hr);
6739 if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
6741 win_skip("Adapter %u output %u: GetDisplayModeList() not supported.\n", adapter_idx,
6742 output_idx);
6743 IDXGIOutput_Release(output);
6744 continue;
6747 modes = heap_calloc(mode_count, sizeof(*modes));
6748 hr = IDXGIOutput_GetDisplayModeList(output, DXGI_FORMAT_R8G8B8A8_UNORM, 0, &mode_count,
6749 modes);
6750 ok(hr == S_OK, "Adapter %u output %u: GetDisplayModeList failed, hr %#x.\n",
6751 adapter_idx, output_idx, hr);
6753 hr = IDXGIOutput_GetDesc(output, &output_desc);
6754 ok(hr == S_OK, "Adapter %u output %u: GetDesc failed, hr %#x.\n", adapter_idx,
6755 output_idx, hr);
6756 width = output_desc.DesktopCoordinates.right - output_desc.DesktopCoordinates.left;
6757 height = output_desc.DesktopCoordinates.bottom - output_desc.DesktopCoordinates.top;
6758 for (mode_idx = 0; mode_idx < mode_count; ++mode_idx)
6760 if (modes[mode_idx].Width != width && modes[mode_idx].Height != height)
6761 break;
6763 ok(modes[mode_idx].Width != width && modes[mode_idx].Height != height,
6764 "Adapter %u output %u: Failed to find a different mode than %ux%u.\n",
6765 adapter_idx, output_idx, width, height);
6767 ret = ClipCursor(NULL);
6768 ok(ret, "Adapter %u output %u: ClipCursor failed, error %#x.\n",
6769 adapter_idx, output_idx, GetLastError());
6770 get_virtual_rect(&virtual_rect);
6771 ret = GetClipCursor(&clip_rect);
6772 ok(ret, "Adapter %u output %u: GetClipCursor failed, error %#x.\n", adapter_idx,
6773 output_idx, GetLastError());
6774 ok(EqualRect(&clip_rect, &virtual_rect),
6775 "Adapter %u output %u: Expect clip rect %s, got %s.\n", adapter_idx, output_idx,
6776 wine_dbgstr_rect(&virtual_rect), wine_dbgstr_rect(&clip_rect));
6778 swapchain_desc.BufferDesc.Width = modes[mode_idx].Width;
6779 swapchain_desc.BufferDesc.Height = modes[mode_idx].Height;
6780 swapchain_desc.BufferDesc.RefreshRate = modes[mode_idx].RefreshRate;
6781 swapchain_desc.BufferDesc.Format = modes[mode_idx].Format;
6782 swapchain_desc.BufferDesc.ScanlineOrdering = modes[mode_idx].ScanlineOrdering;
6783 swapchain_desc.BufferDesc.Scaling = modes[mode_idx].Scaling;
6784 swapchain_desc.OutputWindow = create_window();
6785 heap_free(modes);
6786 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
6787 ok(hr == S_OK, "Adapter %u output %u: CreateSwapChain failed, hr %#x.\n",
6788 adapter_idx, output_idx, hr);
6790 flush_events();
6791 get_virtual_rect(&virtual_rect);
6792 ret = GetClipCursor(&clip_rect);
6793 ok(ret, "Adapter %u output %u: GetClipCursor failed, error %#x.\n", adapter_idx,
6794 output_idx, GetLastError());
6795 ok(EqualRect(&clip_rect, &virtual_rect),
6796 "Adapter %u output %u: Expect clip rect %s, got %s.\n", adapter_idx, output_idx,
6797 wine_dbgstr_rect(&virtual_rect), wine_dbgstr_rect(&clip_rect));
6799 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
6800 ok(hr == S_OK || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE ||
6801 broken(hr == DXGI_ERROR_UNSUPPORTED), /* Win 7 testbot */
6802 "Adapter %u output %u: SetFullscreenState failed, hr %#x.\n", adapter_idx,
6803 output_idx, hr);
6804 if (FAILED(hr))
6806 skip("Adapter %u output %u: Could not change fullscreen state, hr %#x.\n",
6807 adapter_idx, output_idx, hr);
6808 IDXGISwapChain_Release(swapchain);
6809 IDXGIOutput_Release(output);
6810 DestroyWindow(swapchain_desc.OutputWindow);
6811 continue;
6814 flush_events();
6815 get_virtual_rect(&virtual_rect);
6816 ret = GetClipCursor(&clip_rect);
6817 ok(ret, "Adapter %u output %u: GetClipCursor failed, error %#x.\n", adapter_idx,
6818 output_idx, GetLastError());
6819 ok(EqualRect(&clip_rect, &virtual_rect),
6820 "Adapter %u output %u: Expect clip rect %s, got %s.\n", adapter_idx, output_idx,
6821 wine_dbgstr_rect(&virtual_rect), wine_dbgstr_rect(&clip_rect));
6823 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
6824 ok(hr == S_OK, "Adapter %u output %u: Got unexpected hr %#x.\n", adapter_idx,
6825 output_idx, hr);
6826 refcount = IDXGISwapChain_Release(swapchain);
6827 ok(!refcount, "Adapter %u output %u: IDXGISwapChain has %u references left.\n",
6828 adapter_idx, output_idx, refcount);
6829 refcount = IDXGIOutput_Release(output);
6830 ok(!refcount, "Adapter %u output %u: IDXGIOutput has %u references left.\n",
6831 adapter_idx, output_idx, refcount);
6832 DestroyWindow(swapchain_desc.OutputWindow);
6834 flush_events();
6835 get_virtual_rect(&virtual_rect);
6836 ret = GetClipCursor(&clip_rect);
6837 ok(ret, "Adapter %u output %u: GetClipCursor failed, error %#x.\n", adapter_idx,
6838 output_idx, GetLastError());
6839 ok(EqualRect(&clip_rect, &virtual_rect),
6840 "Adapter %u output %u: Expect clip rect %s, got %s.\n", adapter_idx, output_idx,
6841 wine_dbgstr_rect(&virtual_rect), wine_dbgstr_rect(&clip_rect));
6844 IDXGIAdapter_Release(adapter);
6847 refcount = IDXGIFactory_Release(factory);
6848 ok(refcount == !is_d3d12, "Got unexpected refcount %u.\n", refcount);
6851 static void test_factory_check_feature_support(void)
6853 IDXGIFactory5 *factory;
6854 ULONG ref_count;
6855 HRESULT hr;
6856 BOOL data;
6858 if (FAILED(hr = CreateDXGIFactory(&IID_IDXGIFactory5, (void**)&factory)))
6860 win_skip("IDXGIFactory5 is not available.\n");
6861 return;
6864 hr = IDXGIFactory5_CheckFeatureSupport(factory, 0x12345678, (void *)&data, sizeof(data));
6865 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
6867 /* Crashes on Windows. */
6868 if (0)
6870 hr = IDXGIFactory5_CheckFeatureSupport(factory, DXGI_FEATURE_PRESENT_ALLOW_TEARING, NULL, sizeof(data));
6871 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
6874 hr = IDXGIFactory5_CheckFeatureSupport(factory, DXGI_FEATURE_PRESENT_ALLOW_TEARING, &data, sizeof(data) - 1);
6875 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
6877 hr = IDXGIFactory5_CheckFeatureSupport(factory, DXGI_FEATURE_PRESENT_ALLOW_TEARING, &data, sizeof(data) + 1);
6878 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
6880 data = (BOOL)0xdeadbeef;
6881 hr = IDXGIFactory5_CheckFeatureSupport(factory, DXGI_FEATURE_PRESENT_ALLOW_TEARING, &data, sizeof(data));
6882 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6883 ok(data == TRUE || data == FALSE, "Got unexpected data %#x.\n", data);
6885 ref_count = IDXGIFactory5_Release(factory);
6886 ok(!ref_count, "Factory has %u references left.\n", ref_count);
6889 static void test_frame_latency_event(IUnknown *device, BOOL is_d3d12)
6891 DXGI_SWAP_CHAIN_DESC1 swapchain_desc;
6892 IDXGISwapChain2 *swapchain2;
6893 IDXGISwapChain1 *swapchain1;
6894 IDXGIFactory2 *factory2;
6895 IDXGIFactory *factory;
6896 UINT frame_latency;
6897 DWORD wait_result;
6898 ULONG ref_count;
6899 unsigned int i;
6900 HANDLE event;
6901 HWND window;
6902 HRESULT hr;
6904 get_factory(device, is_d3d12, &factory);
6906 hr = IDXGIFactory_QueryInterface(factory, &IID_IDXGIFactory2, (void**)&factory2);
6907 IDXGIFactory_Release(factory);
6908 if (FAILED(hr))
6910 win_skip("IDXGIFactory2 not available.\n");
6911 return;
6914 window = create_window();
6916 swapchain_desc.Width = 640;
6917 swapchain_desc.Height = 480;
6918 swapchain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
6919 swapchain_desc.Stereo = FALSE;
6920 swapchain_desc.SampleDesc.Count = 1;
6921 swapchain_desc.SampleDesc.Quality = 0;
6922 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
6923 swapchain_desc.BufferCount = 2;
6924 swapchain_desc.Scaling = DXGI_SCALING_STRETCH;
6925 swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
6926 swapchain_desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
6927 swapchain_desc.Flags = 0;
6929 hr = IDXGIFactory2_CreateSwapChainForHwnd(factory2, device,
6930 window, &swapchain_desc, NULL, NULL, &swapchain1);
6931 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6933 hr = IDXGISwapChain1_QueryInterface(swapchain1, &IID_IDXGISwapChain2, (void**)&swapchain2);
6934 IDXGISwapChain1_Release(swapchain1);
6935 if (FAILED(hr))
6937 win_skip("IDXGISwapChain2 not available.\n");
6938 IDXGIFactory2_Release(factory2);
6939 DestroyWindow(window);
6940 return;
6943 /* test swap chain without waitable object */
6944 frame_latency = 0xdeadbeef;
6945 hr = IDXGISwapChain2_GetMaximumFrameLatency(swapchain2, &frame_latency);
6946 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
6947 ok(frame_latency == 0xdeadbeef, "Got unexpected frame latency %#x.\n", frame_latency);
6948 hr = IDXGISwapChain2_SetMaximumFrameLatency(swapchain2, 1);
6949 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
6950 event = IDXGISwapChain2_GetFrameLatencyWaitableObject(swapchain2);
6951 ok(!event, "Got unexpected event %p.\n", event);
6953 ref_count = IDXGISwapChain2_Release(swapchain2);
6954 ok(!ref_count, "Swap chain has %u references left.\n", ref_count);
6956 /* test swap chain with waitable object */
6957 swapchain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
6959 hr = IDXGIFactory2_CreateSwapChainForHwnd(factory2, device,
6960 window, &swapchain_desc, NULL, NULL, &swapchain1);
6961 ok(hr == S_OK, "Failed to create swap chain, hr %#x.\n", hr);
6962 hr = IDXGISwapChain1_QueryInterface(swapchain1, &IID_IDXGISwapChain2, (void**)&swapchain2);
6963 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6964 IDXGISwapChain1_Release(swapchain1);
6966 event = IDXGISwapChain2_GetFrameLatencyWaitableObject(swapchain2);
6967 ok(!!event, "Got unexpected event %p.\n", event);
6969 /* auto-reset event */
6970 wait_result = WaitForSingleObject(event, 0);
6971 ok(!wait_result, "Got unexpected wait result %#x.\n", wait_result);
6972 wait_result = WaitForSingleObject(event, 0);
6973 ok(wait_result == WAIT_TIMEOUT, "Got unexpected wait result %#x.\n", wait_result);
6975 hr = IDXGISwapChain2_GetMaximumFrameLatency(swapchain2, &frame_latency);
6976 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6977 ok(frame_latency == 1, "Got unexpected frame latency %#x.\n", frame_latency);
6979 hr = IDXGISwapChain2_SetMaximumFrameLatency(swapchain2, 0);
6980 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
6981 hr = IDXGISwapChain2_GetMaximumFrameLatency(swapchain2, &frame_latency);
6982 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6983 ok(frame_latency == 1, "Got unexpected frame latency %#x.\n", frame_latency);
6985 hr = IDXGISwapChain2_SetMaximumFrameLatency(swapchain2, 2);
6986 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6987 hr = IDXGISwapChain2_GetMaximumFrameLatency(swapchain2, &frame_latency);
6988 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
6989 ok(frame_latency == 2, "Got unexpected frame latency %#x.\n", frame_latency);
6991 for (i = 0; i < 5; i++)
6993 hr = IDXGISwapChain2_Present(swapchain2, 0, 0);
6994 ok(hr == S_OK, "Present %u failed with hr %#x.\n", i, hr);
6997 wait_result = WaitForSingleObject(event, 1000);
6998 ok(!wait_result, "Got unexpected wait result %#x.\n", wait_result);
7000 ref_count = IDXGISwapChain2_Release(swapchain2);
7001 ok(!ref_count, "Swap chain has %u references left.\n", ref_count);
7002 DestroyWindow(window);
7003 ref_count = IDXGIFactory2_Release(factory2);
7004 ok(ref_count == !is_d3d12, "Factory has %u references left.\n", ref_count);
7007 static void test_colour_space_support(IUnknown *device, BOOL is_d3d12)
7009 DXGI_SWAP_CHAIN_DESC1 swapchain_desc;
7010 IDXGISwapChain3 *swapchain3;
7011 IDXGISwapChain1 *swapchain1;
7012 IDXGIFactory2 *factory2;
7013 IDXGIFactory *factory;
7014 ULONG ref_count;
7015 unsigned int i;
7016 UINT support;
7017 HWND window;
7018 HRESULT hr;
7020 static const DXGI_COLOR_SPACE_TYPE colour_spaces[] =
7022 DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709,
7023 DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709,
7024 DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709,
7025 DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020,
7026 DXGI_COLOR_SPACE_RESERVED,
7027 DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601,
7028 DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601,
7029 DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601,
7030 DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709,
7031 DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709,
7032 DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020,
7033 DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020,
7034 DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020,
7035 DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020,
7036 DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020,
7037 DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020,
7038 DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020,
7039 DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020,
7040 DXGI_COLOR_SPACE_YCBCR_STUDIO_GHLG_TOPLEFT_P2020,
7041 DXGI_COLOR_SPACE_YCBCR_FULL_GHLG_TOPLEFT_P2020,
7044 get_factory(device, is_d3d12, &factory);
7046 hr = IDXGIFactory_QueryInterface(factory, &IID_IDXGIFactory2, (void**)&factory2);
7047 IDXGIFactory_Release(factory);
7048 if (FAILED(hr))
7050 win_skip("IDXGIFactory2 not available.\n");
7051 return;
7054 window = create_window();
7056 swapchain_desc.Width = 640;
7057 swapchain_desc.Height = 480;
7058 swapchain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
7059 swapchain_desc.Stereo = FALSE;
7060 swapchain_desc.SampleDesc.Count = 1;
7061 swapchain_desc.SampleDesc.Quality = 0;
7062 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
7063 swapchain_desc.BufferCount = 2;
7064 swapchain_desc.Scaling = DXGI_SCALING_STRETCH;
7065 swapchain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
7066 swapchain_desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;
7067 swapchain_desc.Flags = 0;
7069 hr = IDXGIFactory2_CreateSwapChainForHwnd(factory2, device,
7070 window, &swapchain_desc, NULL, NULL, &swapchain1);
7071 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
7073 hr = IDXGISwapChain1_QueryInterface(swapchain1, &IID_IDXGISwapChain3, (void**)&swapchain3);
7074 IDXGISwapChain1_Release(swapchain1);
7075 if (FAILED(hr))
7077 win_skip("IDXGISwapChain3 not available.\n");
7078 IDXGIFactory2_Release(factory2);
7079 DestroyWindow(window);
7080 return;
7083 for (i = 0; i < ARRAY_SIZE(colour_spaces); ++i)
7085 support = 0xdeadbeef;
7086 hr = IDXGISwapChain3_CheckColorSpaceSupport(swapchain3, colour_spaces[i], &support);
7087 ok(hr == S_OK, "Got unexpected hr %#x for test %u.\n", hr, i);
7088 ok(!(support & ~DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT),
7089 "Got unexpected support flags %#x for test %u.\n", support, i);
7091 if (colour_spaces[i] == DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709)
7093 ok(support & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT,
7094 "Required colour space not supported for test %u.\n", i);
7096 else if (colour_spaces[i] == DXGI_COLOR_SPACE_RESERVED)
7098 ok(!support, "Invalid colour space supported for test %u.\n", i);
7101 hr = IDXGISwapChain3_SetColorSpace1(swapchain3, colour_spaces[i]);
7102 ok(hr == (support & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT) ? S_OK : E_INVALIDARG,
7103 "Got unexpected hr %#x for text %u.\n", hr, i);
7106 ref_count = IDXGISwapChain3_Release(swapchain3);
7107 ok(!ref_count, "Swap chain has %u references left.\n", ref_count);
7108 DestroyWindow(window);
7109 ref_count = IDXGIFactory2_Release(factory2);
7110 ok(ref_count == !is_d3d12, "Factory has %u references left.\n", ref_count);
7113 static void test_mode_change(IUnknown *device, BOOL is_d3d12)
7115 unsigned int user32_width = 0, user32_height = 0, d3d_width = 0, d3d_height = 0;
7116 unsigned int display_count = 0, mode_idx = 0, adapter_idx, output_idx;
7117 DEVMODEW *original_modes = NULL, old_devmode, devmode, devmode2;
7118 DXGI_SWAP_CHAIN_DESC swapchain_desc, swapchain_desc2;
7119 IDXGIOutput *output, *second_output = NULL;
7120 WCHAR second_monitor_name[CCHDEVICENAME];
7121 IDXGISwapChain *swapchain, *swapchain2;
7122 DXGI_OUTPUT_DESC output_desc;
7123 IDXGIAdapter *adapter;
7124 IDXGIFactory *factory;
7125 BOOL fullscreen, ret;
7126 LONG change_ret;
7127 ULONG refcount;
7128 HRESULT hr;
7130 memset(&devmode, 0, sizeof(devmode));
7131 devmode.dmSize = sizeof(devmode);
7132 ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode);
7133 ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError());
7134 ok(equal_mode_rect(&devmode, &registry_mode), "Got a different mode.\n");
7135 ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode);
7136 ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError());
7137 ok(equal_mode_rect(&devmode, &registry_mode), "Got a different mode.\n");
7139 while (EnumDisplaySettingsW(NULL, mode_idx++, &devmode))
7141 if (devmode.dmPelsWidth == registry_mode.dmPelsWidth
7142 && devmode.dmPelsHeight == registry_mode.dmPelsHeight)
7143 continue;
7145 if (!d3d_width && !d3d_height)
7147 d3d_width = devmode.dmPelsWidth;
7148 d3d_height = devmode.dmPelsHeight;
7149 continue;
7152 if (devmode.dmPelsWidth == d3d_width && devmode.dmPelsHeight == d3d_height)
7153 continue;
7155 user32_width = devmode.dmPelsWidth;
7156 user32_height = devmode.dmPelsHeight;
7157 break;
7159 if (!user32_width || !user32_height)
7161 skip("Failed to find three different display modes for the primary output.\n");
7162 return;
7165 ret = save_display_modes(&original_modes, &display_count);
7166 ok(ret, "Failed to save original display modes.\n");
7168 get_factory(device, is_d3d12, &factory);
7170 /* Test that no mode restorations if no mode changes actually happened */
7171 change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET);
7172 ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret);
7174 swapchain_desc.BufferDesc.Width = registry_mode.dmPelsWidth;
7175 swapchain_desc.BufferDesc.Height = registry_mode.dmPelsHeight;
7176 swapchain_desc.BufferDesc.RefreshRate.Numerator = 60;
7177 swapchain_desc.BufferDesc.RefreshRate.Denominator = 1;
7178 swapchain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
7179 swapchain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
7180 swapchain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
7181 swapchain_desc.SampleDesc.Count = 1;
7182 swapchain_desc.SampleDesc.Quality = 0;
7183 swapchain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
7184 swapchain_desc.BufferCount = is_d3d12 ? 2 : 1;
7185 swapchain_desc.OutputWindow = CreateWindowA("static", "dxgi_test", 0, 0, 0, 400, 200, 0, 0, 0, 0);
7186 swapchain_desc.Windowed = TRUE;
7187 swapchain_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
7188 swapchain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
7190 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
7191 ok(hr == S_OK, "CreateSwapChain failed, hr %#x.\n", hr);
7192 refcount = IDXGISwapChain_Release(swapchain);
7193 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
7195 memset(&devmode2, 0, sizeof(devmode2));
7196 devmode2.dmSize = sizeof(devmode2);
7197 ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2);
7198 ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError());
7199 ok(equal_mode_rect(&devmode2, &registry_mode), "Got a different mode.\n");
7200 ret = restore_display_modes(original_modes, display_count);
7201 ok(ret, "Failed to restore display modes.\n");
7203 /* If current display settings are different than the display settings in registry before
7204 * calling SetFullscreenState() */
7205 change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET);
7206 ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret);
7208 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
7209 ok(hr == S_OK, "CreateSwapChain failed, hr %#x.\n", hr);
7210 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
7211 ok(hr == DXGI_ERROR_UNSUPPORTED /* Win7 */
7212 || hr == S_OK /* Win8~Win10 1909 */
7213 || hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE, /* Win10 2004 */
7214 "Got unexpected hr %#x.\n", hr);
7216 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
7217 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
7218 refcount = IDXGISwapChain_Release(swapchain);
7219 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
7220 ret = restore_display_modes(original_modes, display_count);
7221 ok(ret, "Failed to restore display modes.\n");
7223 /* Test that mode restorations use display settings in the registry with a fullscreen device */
7224 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
7225 ok(hr == S_OK, "CreateSwapChain failed, hr %#x.\n", hr);
7226 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
7227 if (FAILED(hr))
7229 skip("SetFullscreenState failed, hr %#x.\n", hr);
7230 refcount = IDXGISwapChain_Release(swapchain);
7231 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
7232 goto done;
7235 change_ret = ChangeDisplaySettingsW(&devmode, CDS_UPDATEREGISTRY | CDS_NORESET);
7236 ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsW failed with %d.\n", change_ret);
7237 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
7238 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
7240 ret = EnumDisplaySettingsW(NULL, ENUM_CURRENT_SETTINGS, &devmode2);
7241 ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError());
7242 ok(equal_mode_rect(&devmode2, &devmode), "Got a different mode.\n");
7243 ret = EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &devmode2);
7244 ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError());
7245 ok(equal_mode_rect(&devmode2, &devmode), "Got a different mode.\n");
7246 refcount = IDXGISwapChain_Release(swapchain);
7247 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
7248 ret = restore_display_modes(original_modes, display_count);
7249 ok(ret, "Failed to restore display modes.\n");
7251 for (adapter_idx = 0; SUCCEEDED(IDXGIFactory_EnumAdapters(factory, adapter_idx, &adapter)); ++adapter_idx)
7253 for (output_idx = 0; SUCCEEDED(IDXGIAdapter_EnumOutputs(adapter, output_idx, &output)); ++output_idx)
7255 hr = IDXGIOutput_GetDesc(output, &output_desc);
7256 ok(hr == S_OK, "Adapter %u output %u: Got unexpected hr %#x.\n", adapter_idx, output_idx, hr);
7258 if ((adapter_idx || output_idx) && output_desc.AttachedToDesktop)
7260 second_output = output;
7261 break;
7264 IDXGIOutput_Release(output);
7267 IDXGIAdapter_Release(adapter);
7268 if (second_output)
7269 break;
7272 if (!second_output)
7274 skip("Following tests require two monitors.\n");
7275 goto done;
7277 lstrcpyW(second_monitor_name, output_desc.DeviceName);
7279 memset(&old_devmode, 0, sizeof(old_devmode));
7280 old_devmode.dmSize = sizeof(old_devmode);
7281 ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &old_devmode);
7282 ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError());
7284 mode_idx = 0;
7285 d3d_width = 0;
7286 d3d_height = 0;
7287 user32_width = 0;
7288 user32_height = 0;
7289 while (EnumDisplaySettingsW(second_monitor_name, mode_idx++, &devmode))
7291 if (devmode.dmPelsWidth == old_devmode.dmPelsWidth
7292 && devmode.dmPelsHeight == old_devmode.dmPelsHeight)
7293 continue;
7295 if (!d3d_width && !d3d_height)
7297 d3d_width = devmode.dmPelsWidth;
7298 d3d_height = devmode.dmPelsHeight;
7299 continue;
7302 if (devmode.dmPelsWidth == d3d_width && devmode.dmPelsHeight == d3d_height)
7303 continue;
7305 user32_width = devmode.dmPelsWidth;
7306 user32_height = devmode.dmPelsHeight;
7307 break;
7309 if (!user32_width || !user32_height)
7311 skip("Failed to find three different display modes for the second output.\n");
7312 goto done;
7315 /* Test that mode restorations for non-primary outputs upon fullscreen state changes */
7316 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
7317 ok(hr == S_OK, "CreateSwapChain failed, hr %#x.\n", hr);
7318 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
7319 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
7321 change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL, CDS_RESET, NULL);
7322 ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret);
7323 ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2);
7324 ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError());
7325 if (devmode2.dmPelsWidth == old_devmode.dmPelsWidth
7326 && devmode2.dmPelsHeight == old_devmode.dmPelsHeight)
7328 skip("Failed to change display settings of the second monitor.\n");
7329 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
7330 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
7331 refcount = IDXGISwapChain_Release(swapchain);
7332 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
7333 goto done;
7336 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
7337 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
7339 ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2);
7340 ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError());
7341 ok(equal_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n");
7342 ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2);
7343 ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError());
7344 ok(equal_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n");
7345 hr = IDXGIOutput_GetDesc(second_output, &output_desc);
7346 ok(hr == S_OK, "GetDesc failed, hr %#x.\n", hr);
7347 ok(output_desc.DesktopCoordinates.right - output_desc.DesktopCoordinates.left ==
7348 old_devmode.dmPelsWidth, "Expected width %u, got %u.\n", old_devmode.dmPelsWidth,
7349 output_desc.DesktopCoordinates.right - output_desc.DesktopCoordinates.left);
7350 ok(output_desc.DesktopCoordinates.bottom - output_desc.DesktopCoordinates.top ==
7351 old_devmode.dmPelsHeight, "Expected height %u, got %u.\n", old_devmode.dmPelsHeight,
7352 output_desc.DesktopCoordinates.bottom - output_desc.DesktopCoordinates.top);
7354 refcount = IDXGISwapChain_Release(swapchain);
7355 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
7356 ret = restore_display_modes(original_modes, display_count);
7357 ok(ret, "Failed to restore display modes.\n");
7359 /* Test that mode restorations for non-primary outputs use display settings in the registry */
7360 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
7361 ok(hr == S_OK, "CreateSwapChain failed, hr %#x.\n", hr);
7362 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
7363 ok(hr == S_OK, "SetFullscreenState failed, hr %#x.\n", hr);
7365 change_ret = ChangeDisplaySettingsExW(second_monitor_name, &devmode, NULL,
7366 CDS_UPDATEREGISTRY | CDS_NORESET, NULL);
7367 ok(change_ret == DISP_CHANGE_SUCCESSFUL, "ChangeDisplaySettingsExW failed with %d.\n", change_ret);
7368 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
7369 ok(hr == S_OK, "SetFullscreenState failed, hr %#x.\n", hr);
7371 ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2);
7372 ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError());
7373 ok(devmode2.dmPelsWidth == devmode.dmPelsWidth && devmode2.dmPelsHeight == devmode.dmPelsHeight,
7374 "Expected resolution %ux%u, got %ux%u.\n", devmode.dmPelsWidth, devmode.dmPelsHeight,
7375 devmode2.dmPelsWidth, devmode2.dmPelsHeight);
7376 ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2);
7377 ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError());
7378 ok(devmode2.dmPelsWidth == devmode.dmPelsWidth && devmode2.dmPelsHeight == devmode.dmPelsHeight,
7379 "Expected resolution %ux%u, got %ux%u.\n", devmode.dmPelsWidth, devmode.dmPelsHeight,
7380 devmode2.dmPelsWidth, devmode2.dmPelsHeight);
7381 hr = IDXGIOutput_GetDesc(second_output, &output_desc);
7382 ok(hr == S_OK, "GetDesc failed, hr %#x.\n", hr);
7383 ok(output_desc.DesktopCoordinates.right - output_desc.DesktopCoordinates.left ==
7384 devmode.dmPelsWidth, "Expected width %u, got %u.\n", devmode.dmPelsWidth,
7385 output_desc.DesktopCoordinates.right - output_desc.DesktopCoordinates.left);
7386 ok(output_desc.DesktopCoordinates.bottom - output_desc.DesktopCoordinates.top ==
7387 devmode.dmPelsHeight, "Expected height %u, got %u.\n", devmode.dmPelsHeight,
7388 output_desc.DesktopCoordinates.bottom - output_desc.DesktopCoordinates.top);
7390 refcount = IDXGISwapChain_Release(swapchain);
7391 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
7392 ret = restore_display_modes(original_modes, display_count);
7393 ok(ret, "Failed to restore display modes.\n");
7395 /* Test that mode restorations for non-primary outputs on fullscreen state changes when there
7396 * are two fullscreen swapchains on different outputs */
7397 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc, &swapchain);
7398 ok(hr == S_OK, "CreateSwapChain failed, hr %#x.\n", hr);
7400 swapchain_desc2 = swapchain_desc;
7401 swapchain_desc.BufferDesc.Width = d3d_width;
7402 swapchain_desc.BufferDesc.Height = d3d_height;
7403 swapchain_desc2.OutputWindow = CreateWindowA("static", "dxgi_test2", 0,
7404 old_devmode.dmPosition.x, old_devmode.dmPosition.y, 400, 200, 0, 0, 0, 0);
7405 hr = IDXGIFactory_CreateSwapChain(factory, device, &swapchain_desc2, &swapchain2);
7406 ok(hr == S_OK, "CreateSwapChain failed, hr %#x.\n", hr);
7407 hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
7408 ok(hr == S_OK, "SetFullscreenState failed, hr %#x.\n", hr);
7409 hr = IDXGISwapChain_SetFullscreenState(swapchain2, TRUE, NULL);
7410 if (FAILED(hr))
7412 skip("SetFullscreenState failed, hr %#x.\n", hr);
7413 refcount = IDXGISwapChain_Release(swapchain2);
7414 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
7415 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
7416 ok(hr == S_OK, "SetFullscreenState failed, hr %#x.\n", hr);
7417 refcount = IDXGISwapChain_Release(swapchain);
7418 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
7419 goto done;
7422 hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
7423 ok(hr == S_OK, "SetFullscreenState failed, hr %#x.\n", hr);
7424 hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
7425 ok(hr == S_OK, "GetFullscreenState failed, hr %#x.\n", hr);
7426 ok(!fullscreen, "Expected swapchain not fullscreen.\n");
7427 hr = IDXGISwapChain_GetFullscreenState(swapchain2, &fullscreen, NULL);
7428 ok(hr == S_OK, "GetFullscreenState failed, hr %#x.\n", hr);
7429 ok(fullscreen, "Expected swapchain fullscreen.\n");
7431 ret = EnumDisplaySettingsW(second_monitor_name, ENUM_CURRENT_SETTINGS, &devmode2);
7432 ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError());
7433 ok(equal_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n");
7434 ret = EnumDisplaySettingsW(second_monitor_name, ENUM_REGISTRY_SETTINGS, &devmode2);
7435 ok(ret, "EnumDisplaySettingsW failed, error %#x.\n", GetLastError());
7436 ok(equal_mode_rect(&devmode2, &old_devmode), "Got a different mode.\n");
7437 hr = IDXGIOutput_GetDesc(second_output, &output_desc);
7438 ok(hr == S_OK, "GetDesc failed, hr %#x.\n", hr);
7439 ok(output_desc.DesktopCoordinates.right - output_desc.DesktopCoordinates.left ==
7440 old_devmode.dmPelsWidth, "Expected width %u, got %u.\n", old_devmode.dmPelsWidth,
7441 output_desc.DesktopCoordinates.right - output_desc.DesktopCoordinates.left);
7442 ok(output_desc.DesktopCoordinates.bottom - output_desc.DesktopCoordinates.top ==
7443 old_devmode.dmPelsHeight, "Expected height %u, got %u.\n", old_devmode.dmPelsHeight,
7444 output_desc.DesktopCoordinates.bottom - output_desc.DesktopCoordinates.top);
7446 hr = IDXGISwapChain_SetFullscreenState(swapchain2, FALSE, NULL);
7447 ok(hr == S_OK, "SetFullscreenState failed, hr %#x.\n", hr);
7448 refcount = IDXGISwapChain_Release(swapchain2);
7449 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
7450 refcount = IDXGISwapChain_Release(swapchain);
7451 ok(!refcount, "IDXGISwapChain has %u references left.\n", refcount);
7452 DestroyWindow(swapchain_desc2.OutputWindow);
7453 ret = restore_display_modes(original_modes, display_count);
7454 ok(ret, "Failed to restore display modes.\n");
7456 done:
7457 if (second_output)
7458 IDXGIOutput_Release(second_output);
7459 DestroyWindow(swapchain_desc.OutputWindow);
7460 refcount = IDXGIFactory_Release(factory);
7461 ok(refcount == !is_d3d12, "Got unexpected refcount %u.\n", refcount);
7462 ret = restore_display_modes(original_modes, display_count);
7463 ok(ret, "Failed to restore display modes.\n");
7464 heap_free(original_modes);
7467 static void test_swapchain_present_count(IUnknown *device, BOOL is_d3d12)
7469 UINT present_count, expected;
7470 IDXGISwapChain *swapchain;
7471 HWND window;
7472 HRESULT hr;
7474 window = create_window();
7475 swapchain = create_swapchain(device, is_d3d12, window);
7477 present_count = ~0u;
7478 hr = IDXGISwapChain_GetLastPresentCount(swapchain, &present_count);
7479 todo_wine_if(is_d3d12) ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
7480 todo_wine_if(is_d3d12) ok(!present_count, "Got unexpected present count %u.\n", present_count);
7482 hr = IDXGISwapChain_Present(swapchain, 0, 0);
7483 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
7484 expected = present_count + 1;
7485 hr = IDXGISwapChain_GetLastPresentCount(swapchain, &present_count);
7486 todo_wine_if(is_d3d12) ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
7487 todo_wine_if(is_d3d12)
7488 ok(present_count == expected, "Got unexpected present count %u, expected %u.\n", present_count, expected);
7490 hr = IDXGISwapChain_Present(swapchain, 10, 0);
7491 ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#x.\n", hr);
7492 expected = present_count;
7493 hr = IDXGISwapChain_GetLastPresentCount(swapchain, &present_count);
7494 todo_wine_if(is_d3d12) ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
7495 ok(present_count == expected, "Got unexpected present count %u, expected %u.\n", present_count, expected);
7497 hr = IDXGISwapChain_Present(swapchain, 0, DXGI_PRESENT_TEST);
7498 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
7499 expected = present_count;
7500 hr = IDXGISwapChain_GetLastPresentCount(swapchain, &present_count);
7501 todo_wine_if(is_d3d12) ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
7502 ok(present_count == expected, "Got unexpected present count %u, expected %u.\n", present_count, expected);
7504 ShowWindow(window, SW_MINIMIZE);
7505 hr = IDXGISwapChain_Present(swapchain, 0, 0);
7506 ok(hr == (is_d3d12 ? S_OK : DXGI_STATUS_OCCLUDED), "Got unexpected hr %#x.\n", hr);
7507 expected = present_count + !!is_d3d12;
7508 hr = IDXGISwapChain_GetLastPresentCount(swapchain, &present_count);
7509 todo_wine_if(is_d3d12) ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
7510 todo_wine_if(is_d3d12)
7511 ok(present_count == expected, "Got unexpected present count %u, expected %u.\n", present_count, expected);
7513 ShowWindow(window, SW_NORMAL);
7514 hr = IDXGISwapChain_Present(swapchain, 0, 0);
7515 ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
7516 expected = present_count + 1;
7517 hr = IDXGISwapChain_GetLastPresentCount(swapchain, &present_count);
7518 todo_wine_if(is_d3d12) ok(hr == S_OK, "Got unexpected hr %#x.\n", hr);
7519 todo_wine_if(is_d3d12)
7520 ok(present_count == expected, "Got unexpected present count %u, expected %u.\n", present_count, expected);
7522 IDXGISwapChain_Release(swapchain);
7523 DestroyWindow(window);
7526 static void run_on_d3d10(void (*test_func)(IUnknown *device, BOOL is_d3d12))
7528 IDXGIDevice *device;
7529 ULONG refcount;
7531 if (!(device = create_device(0)))
7533 skip("Failed to create Direct3D 10 device.\n");
7534 return;
7537 test_func((IUnknown *)device, FALSE);
7539 refcount = IDXGIDevice_Release(device);
7540 ok(!refcount, "Device has %u references left.\n", refcount);
7543 static void run_on_d3d12(void (*test_func)(IUnknown *device, BOOL is_d3d12))
7545 ID3D12CommandQueue *queue;
7546 ID3D12Device *device;
7547 ULONG refcount;
7549 if (!(device = create_d3d12_device()))
7551 skip("Failed to create Direct3D 12 device.\n");
7552 return;
7555 queue = create_d3d12_direct_queue(device);
7557 test_func((IUnknown *)queue, TRUE);
7559 wait_queue_idle(device, queue);
7561 refcount = ID3D12CommandQueue_Release(queue);
7562 ok(!refcount, "Command queue has %u references left.\n", refcount);
7563 refcount = ID3D12Device_Release(device);
7564 ok(!refcount, "Device has %u references left.\n", refcount);
7567 START_TEST(dxgi)
7569 HMODULE dxgi_module, d3d11_module, d3d12_module, gdi32_module;
7570 BOOL enable_debug_layer = FALSE;
7571 unsigned int argc, i;
7572 ID3D12Debug *debug;
7573 char **argv;
7575 dxgi_module = GetModuleHandleA("dxgi.dll");
7576 pCreateDXGIFactory1 = (void *)GetProcAddress(dxgi_module, "CreateDXGIFactory1");
7577 pCreateDXGIFactory2 = (void *)GetProcAddress(dxgi_module, "CreateDXGIFactory2");
7579 gdi32_module = GetModuleHandleA("gdi32.dll");
7580 pD3DKMTCheckVidPnExclusiveOwnership = (void *)GetProcAddress(gdi32_module, "D3DKMTCheckVidPnExclusiveOwnership");
7581 pD3DKMTCloseAdapter = (void *)GetProcAddress(gdi32_module, "D3DKMTCloseAdapter");
7582 pD3DKMTOpenAdapterFromGdiDisplayName = (void *)GetProcAddress(gdi32_module, "D3DKMTOpenAdapterFromGdiDisplayName");
7584 d3d11_module = LoadLibraryA("d3d11.dll");
7585 pD3D11CreateDevice = (void *)GetProcAddress(d3d11_module, "D3D11CreateDevice");
7587 registry_mode.dmSize = sizeof(registry_mode);
7588 ok(EnumDisplaySettingsW(NULL, ENUM_REGISTRY_SETTINGS, &registry_mode), "Failed to get display mode.\n");
7590 use_mt = !getenv("WINETEST_NO_MT_D3D");
7592 argc = winetest_get_mainargs(&argv);
7593 for (i = 2; i < argc; ++i)
7595 if (!strcmp(argv[i], "--validate"))
7596 enable_debug_layer = TRUE;
7597 else if (!strcmp(argv[i], "--warp"))
7598 use_warp_adapter = TRUE;
7599 else if (!strcmp(argv[i], "--adapter") && i + 1 < argc)
7600 use_adapter_idx = atoi(argv[++i]);
7601 else if (!strcmp(argv[i], "--single"))
7602 use_mt = FALSE;
7605 queue_test(test_adapter_desc);
7606 queue_test(test_adapter_luid);
7607 queue_test(test_query_video_memory_info);
7608 queue_test(test_check_interface_support);
7609 queue_test(test_create_surface);
7610 queue_test(test_parents);
7611 queue_test(test_output);
7612 queue_test(test_find_closest_matching_mode);
7613 queue_test(test_resize_target_wndproc);
7614 queue_test(test_create_factory);
7615 queue_test(test_private_data);
7616 queue_test(test_maximum_frame_latency);
7617 queue_test(test_output_desc);
7618 queue_test(test_object_wrapping);
7619 queue_test(test_factory_check_feature_support);
7621 run_queued_tests();
7623 /* These tests use full-screen swapchains, so shouldn't run in parallel. */
7624 test_create_swapchain();
7625 test_inexact_modes();
7626 test_gamma_control();
7627 test_multi_adapter();
7628 test_swapchain_parameters();
7629 test_swapchain_window_messages();
7630 test_swapchain_window_styles();
7631 run_on_d3d10(test_set_fullscreen);
7632 run_on_d3d10(test_resize_target);
7633 run_on_d3d10(test_swapchain_resize);
7634 run_on_d3d10(test_swapchain_present);
7635 run_on_d3d10(test_swapchain_backbuffer_index);
7636 run_on_d3d10(test_swapchain_formats);
7637 run_on_d3d10(test_output_ownership);
7638 run_on_d3d10(test_cursor_clipping);
7639 run_on_d3d10(test_get_containing_output);
7640 run_on_d3d10(test_window_association);
7641 run_on_d3d10(test_default_fullscreen_target_output);
7642 run_on_d3d10(test_mode_change);
7643 run_on_d3d10(test_swapchain_present_count);
7645 if (!(d3d12_module = LoadLibraryA("d3d12.dll")))
7647 skip("Direct3D 12 is not available.\n");
7648 return;
7651 pD3D12CreateDevice = (void *)GetProcAddress(d3d12_module, "D3D12CreateDevice");
7652 pD3D12GetDebugInterface = (void *)GetProcAddress(d3d12_module, "D3D12GetDebugInterface");
7654 if (enable_debug_layer && SUCCEEDED(pD3D12GetDebugInterface(&IID_ID3D12Debug, (void **)&debug)))
7656 ID3D12Debug_EnableDebugLayer(debug);
7657 ID3D12Debug_Release(debug);
7660 run_on_d3d12(test_set_fullscreen);
7661 run_on_d3d12(test_resize_target);
7662 run_on_d3d12(test_swapchain_resize);
7663 run_on_d3d12(test_swapchain_present);
7664 run_on_d3d12(test_swapchain_backbuffer_index);
7665 run_on_d3d12(test_swapchain_formats);
7666 run_on_d3d12(test_output_ownership);
7667 run_on_d3d12(test_cursor_clipping);
7668 run_on_d3d12(test_frame_latency_event);
7669 run_on_d3d12(test_colour_space_support);
7670 run_on_d3d12(test_get_containing_output);
7671 run_on_d3d12(test_window_association);
7672 run_on_d3d12(test_default_fullscreen_target_output);
7673 run_on_d3d12(test_mode_change);
7674 run_on_d3d12(test_swapchain_present_count);
7676 FreeLibrary(d3d12_module);