From: Zebediah Figura zfigura@codeweavers.com
Garou: Mark of the Wolves calls IDirect3D9::GetAdapterModeCount() on every frame. This results in calling EnumDisplaySettingsExW() once per available mode, which is a very slow operation, both on Windows and Wine.
Manual testing shows that Windows caches the mode list (as well as the adapter list, which is already cached in Wine) in Direct3D 9 and lower. Calls to GetAdapterModeCount() and EnumAdapterDisplayModes() are fast, and they also do not change if monitors are added or removed.
DXGI behaves differently, however. The list of outputs attached to an adapter is cached—that is, calls to IDXGIAdapter::EnumOutputs() are fast, and return stale data. However, at least some other calls are slow and do not seem to be cached, including IDXGIOutput::GetDisplayModeList() and IDXGIOutput::GetDesc().
ddraw is also slow and uncached. Since all testing was done on Windows 10 (for lack of available older hardware to test with) it is not unlikely that ddraw was reimplemented over dxgi on newer Windows, and that older Windows versions would be fast and cached, but this is speculation. In any case I have not included patches to cache ddraw modes.
Tests were done on Windows 10 21H2, both on real hardware with NVidia drivers and on software drivers via qemu/KVM. In the latter case only speed could be tested, but this was consistent with the results from the NVidia machine. --- dlls/d3d8/directx.c | 4 ++-- dlls/d3d9/directx.c | 11 +++++------ dlls/ddraw/ddraw.c | 2 +- dlls/dxgi/output.c | 4 ++-- dlls/wined3d/directx.c | 21 +++++++++++++-------- dlls/wined3d/wined3d.spec | 4 ++-- dlls/wined3d/wined3d_private.h | 1 + include/wine/wined3d.h | 4 ++-- 8 files changed, 28 insertions(+), 23 deletions(-)
diff --git a/dlls/d3d8/directx.c b/dlls/d3d8/directx.c index 3818cb79952..6fcd53abbdd 100644 --- a/dlls/d3d8/directx.c +++ b/dlls/d3d8/directx.c @@ -152,7 +152,7 @@ static UINT WINAPI d3d8_GetAdapterModeCount(IDirect3D8 *iface, UINT adapter)
wined3d_mutex_lock(); count = wined3d_output_get_mode_count(d3d8->wined3d_outputs[output_idx], - WINED3DFMT_UNKNOWN, WINED3D_SCANLINE_ORDERING_UNKNOWN); + WINED3DFMT_UNKNOWN, WINED3D_SCANLINE_ORDERING_UNKNOWN, false); wined3d_mutex_unlock();
return count; @@ -174,7 +174,7 @@ static HRESULT WINAPI d3d8_EnumAdapterModes(IDirect3D8 *iface, UINT adapter, UIN
wined3d_mutex_lock(); hr = wined3d_output_get_mode(d3d8->wined3d_outputs[output_idx], WINED3DFMT_UNKNOWN, - WINED3D_SCANLINE_ORDERING_UNKNOWN, mode_idx, &wined3d_mode); + WINED3D_SCANLINE_ORDERING_UNKNOWN, mode_idx, &wined3d_mode, false); wined3d_mutex_unlock();
if (SUCCEEDED(hr)) diff --git a/dlls/d3d9/directx.c b/dlls/d3d9/directx.c index a1ebc986226..46cbd1fb174 100644 --- a/dlls/d3d9/directx.c +++ b/dlls/d3d9/directx.c @@ -178,7 +178,7 @@ static UINT WINAPI d3d9_GetAdapterModeCount(IDirect3D9Ex *iface, UINT adapter, D
wined3d_mutex_lock(); count = wined3d_output_get_mode_count(d3d9->wined3d_outputs[output_idx], - wined3dformat_from_d3dformat(format), WINED3D_SCANLINE_ORDERING_UNKNOWN); + wined3dformat_from_d3dformat(format), WINED3D_SCANLINE_ORDERING_UNKNOWN, true); wined3d_mutex_unlock();
return count; @@ -203,9 +203,8 @@ static HRESULT WINAPI d3d9_EnumAdapterModes(IDirect3D9Ex *iface, UINT adapter, return D3DERR_INVALIDCALL;
wined3d_mutex_lock(); - hr = wined3d_output_get_mode(d3d9->wined3d_outputs[output_idx], - wined3dformat_from_d3dformat(format), WINED3D_SCANLINE_ORDERING_UNKNOWN, mode_idx, - &wined3d_mode); + hr = wined3d_output_get_mode(d3d9->wined3d_outputs[output_idx], wined3dformat_from_d3dformat(format), + WINED3D_SCANLINE_ORDERING_UNKNOWN, mode_idx, &wined3d_mode, true); wined3d_mutex_unlock();
if (SUCCEEDED(hr)) @@ -524,7 +523,7 @@ static UINT WINAPI d3d9_GetAdapterModeCountEx(IDirect3D9Ex *iface,
wined3d_mutex_lock(); count = wined3d_output_get_mode_count(d3d9->wined3d_outputs[output_idx], - wined3dformat_from_d3dformat(filter->Format), wined3d_scanline_ordering_from_d3d(filter->ScanLineOrdering)); + wined3dformat_from_d3dformat(filter->Format), wined3d_scanline_ordering_from_d3d(filter->ScanLineOrdering), true); wined3d_mutex_unlock();
return count; @@ -550,7 +549,7 @@ static HRESULT WINAPI d3d9_EnumAdapterModesEx(IDirect3D9Ex *iface,
wined3d_mutex_lock(); hr = wined3d_output_get_mode(d3d9->wined3d_outputs[output_idx], wined3dformat_from_d3dformat(filter->Format), - wined3d_scanline_ordering_from_d3d(filter->ScanLineOrdering), mode_idx, &wined3d_mode); + wined3d_scanline_ordering_from_d3d(filter->ScanLineOrdering), mode_idx, &wined3d_mode, true); wined3d_mutex_unlock();
if (SUCCEEDED(hr)) diff --git a/dlls/ddraw/ddraw.c b/dlls/ddraw/ddraw.c index f6770e59fb6..bb244222018 100644 --- a/dlls/ddraw/ddraw.c +++ b/dlls/ddraw/ddraw.c @@ -2447,7 +2447,7 @@ static HRESULT WINAPI ddraw7_EnumDisplayModes(IDirectDraw7 *iface, DWORD Flags, { modenum = 0; while (wined3d_output_get_mode(ddraw->wined3d_output, checkFormatList[fmt], - WINED3D_SCANLINE_ORDERING_UNKNOWN, modenum++, &mode) == WINED3D_OK) + WINED3D_SCANLINE_ORDERING_UNKNOWN, modenum++, &mode, false) == WINED3D_OK) { BOOL found = FALSE; unsigned i; diff --git a/dlls/dxgi/output.c b/dlls/dxgi/output.c index 797851c67f1..7c8431ffcd5 100644 --- a/dlls/dxgi/output.c +++ b/dlls/dxgi/output.c @@ -124,7 +124,7 @@ static HRESULT dxgi_output_get_display_mode_list(struct dxgi_output *output,
wined3d_mutex_lock(); max_count = wined3d_output_get_mode_count(output->wined3d_output, - wined3d_format, WINED3D_SCANLINE_ORDERING_UNKNOWN); + wined3d_format, WINED3D_SCANLINE_ORDERING_UNKNOWN, false);
if (!modes) { @@ -144,7 +144,7 @@ static HRESULT dxgi_output_get_display_mode_list(struct dxgi_output *output, for (i = 0; i < *mode_count; ++i) { if (FAILED(hr = wined3d_output_get_mode(output->wined3d_output, wined3d_format, - WINED3D_SCANLINE_ORDERING_UNKNOWN, i, &mode))) + WINED3D_SCANLINE_ORDERING_UNKNOWN, i, &mode, false))) { WARN("Failed to get output mode %u, hr %#lx.\n", i, hr); wined3d_mutex_unlock(); diff --git a/dlls/wined3d/directx.c b/dlls/wined3d/directx.c index 7f6aef43e2f..6a59955b513 100644 --- a/dlls/wined3d/directx.c +++ b/dlls/wined3d/directx.c @@ -1228,12 +1228,15 @@ HRESULT CDECL wined3d_output_get_desc(const struct wined3d_output *output, /* FIXME: GetAdapterModeCount and EnumAdapterModes currently only returns modes of the same bpp but different resolutions */
-static void wined3d_output_update_modes(struct wined3d_output *output) +static void wined3d_output_update_modes(struct wined3d_output *output, bool cached) { struct wined3d_display_mode *wined3d_mode; unsigned int i; DEVMODEW mode;
+ if (output->modes_valid && cached) + return; + output->mode_count = 0;
for (i = 0; EnumDisplaySettingsExW(output->device_name, i, &mode, 0); ++i) @@ -1264,6 +1267,8 @@ static void wined3d_output_update_modes(struct wined3d_output *output) wined3d_mode->scanline_ordering = WINED3D_SCANLINE_ORDERING_UNKNOWN; } } + + output->modes_valid = true; }
static bool mode_matches_filter(const struct wined3d_adapter *adapter, const struct wined3d_display_mode *mode, @@ -1294,7 +1299,7 @@ static bool mode_matches_filter(const struct wined3d_adapter *adapter, const str
/* Note: dx9 supplies a format. Calls from d3d8 supply WINED3DFMT_UNKNOWN */ unsigned int CDECL wined3d_output_get_mode_count(struct wined3d_output *output, - enum wined3d_format_id format_id, enum wined3d_scanline_ordering scanline_ordering) + enum wined3d_format_id format_id, enum wined3d_scanline_ordering scanline_ordering, bool cached) { const struct wined3d_adapter *adapter; const struct wined3d_format *format; @@ -1307,7 +1312,7 @@ unsigned int CDECL wined3d_output_get_mode_count(struct wined3d_output *output, adapter = output->adapter; format = wined3d_get_format(adapter, format_id, WINED3D_BIND_RENDER_TARGET);
- wined3d_output_update_modes(output); + wined3d_output_update_modes(output, cached);
for (i = 0; i < output->mode_count; ++i) { @@ -1323,7 +1328,7 @@ unsigned int CDECL wined3d_output_get_mode_count(struct wined3d_output *output, /* Note: dx9 supplies a format. Calls from d3d8 supply WINED3DFMT_UNKNOWN */ HRESULT CDECL wined3d_output_get_mode(struct wined3d_output *output, enum wined3d_format_id format_id, enum wined3d_scanline_ordering scanline_ordering, - unsigned int mode_idx, struct wined3d_display_mode *mode) + unsigned int mode_idx, struct wined3d_display_mode *mode, bool cached) { const struct wined3d_adapter *adapter; const struct wined3d_format *format; @@ -1338,7 +1343,7 @@ HRESULT CDECL wined3d_output_get_mode(struct wined3d_output *output, adapter = output->adapter; format = wined3d_get_format(adapter, format_id, WINED3D_BIND_RENDER_TARGET);
- wined3d_output_update_modes(output); + wined3d_output_update_modes(output, cached);
for (i = 0; i < output->mode_count; ++i) { @@ -1372,7 +1377,7 @@ HRESULT CDECL wined3d_output_find_closest_matching_mode(struct wined3d_output *o TRACE("output %p, mode %p.\n", output, mode);
if (!(mode_count = wined3d_output_get_mode_count(output, mode->format_id, - WINED3D_SCANLINE_ORDERING_UNKNOWN))) + WINED3D_SCANLINE_ORDERING_UNKNOWN, false))) { WARN("Output has 0 matching modes.\n"); return E_FAIL; @@ -1389,7 +1394,7 @@ HRESULT CDECL wined3d_output_find_closest_matching_mode(struct wined3d_output *o for (i = 0; i < mode_count; ++i) { if (FAILED(hr = wined3d_output_get_mode(output, mode->format_id, - WINED3D_SCANLINE_ORDERING_UNKNOWN, i, &modes[i]))) + WINED3D_SCANLINE_ORDERING_UNKNOWN, i, &modes[i], false))) { heap_free(matching_modes); heap_free(modes); @@ -2204,7 +2209,7 @@ HRESULT CDECL wined3d_check_device_type(const struct wined3d *wined3d, { /* If the requested display format is not available, don't continue. */ if (!wined3d_output_get_mode_count(output, display_format, - WINED3D_SCANLINE_ORDERING_UNKNOWN)) + WINED3D_SCANLINE_ORDERING_UNKNOWN, false)) { TRACE("No available modes for display format %s.\n", debug_d3dformat(display_format)); return WINED3DERR_NOTAVAILABLE; diff --git a/dlls/wined3d/wined3d.spec b/dlls/wined3d/wined3d.spec index 1f9c398a5c5..a5cf7eca22c 100644 --- a/dlls/wined3d/wined3d.spec +++ b/dlls/wined3d/wined3d.spec @@ -152,8 +152,8 @@ @ cdecl wined3d_output_get_adapter(ptr) @ cdecl wined3d_output_get_desc(ptr ptr) @ cdecl wined3d_output_get_display_mode(ptr ptr ptr) -@ cdecl wined3d_output_get_mode(ptr long long long ptr) -@ cdecl wined3d_output_get_mode_count(ptr long long) +@ cdecl wined3d_output_get_mode(ptr long long long ptr long) +@ cdecl wined3d_output_get_mode_count(ptr long long long) @ cdecl wined3d_output_get_raster_status(ptr ptr) @ cdecl wined3d_output_release_ownership(ptr) @ cdecl wined3d_output_set_display_mode(ptr ptr) diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h index 36274ee24bc..95af2775dfe 100644 --- a/dlls/wined3d/wined3d_private.h +++ b/dlls/wined3d/wined3d_private.h @@ -3505,6 +3505,7 @@ struct wined3d_output
struct wined3d_display_mode *modes; SIZE_T mode_count, modes_size; + bool modes_valid; };
HRESULT wined3d_output_get_gamma_ramp(struct wined3d_output *output, struct wined3d_gamma_ramp *ramp) DECLSPEC_HIDDEN; diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h index 44e8c7d6546..5d51de70863 100644 --- a/include/wine/wined3d.h +++ b/include/wine/wined3d.h @@ -2595,9 +2595,9 @@ HRESULT __cdecl wined3d_output_get_display_mode(const struct wined3d_output *out struct wined3d_display_mode *mode, enum wined3d_display_rotation *rotation); HRESULT __cdecl wined3d_output_get_mode(struct wined3d_output *output, enum wined3d_format_id format_id, enum wined3d_scanline_ordering scanline_ordering, - unsigned int mode_idx, struct wined3d_display_mode *mode); + unsigned int mode_idx, struct wined3d_display_mode *mode, bool cached); unsigned int __cdecl wined3d_output_get_mode_count(struct wined3d_output *output, - enum wined3d_format_id format_id, enum wined3d_scanline_ordering scanline_ordering); + enum wined3d_format_id format_id, enum wined3d_scanline_ordering scanline_ordering, bool cached); HRESULT __cdecl wined3d_output_get_raster_status(const struct wined3d_output *output, struct wined3d_raster_status *raster_status); void __cdecl wined3d_output_release_ownership(const struct wined3d_output *output);