d3d9: Cache the output mode list.

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.
This commit is contained in:
Zebediah Figura 2022-07-26 18:28:42 -05:00 committed by Alexandre Julliard
parent 95352c096f
commit 0ee74e9f81
8 changed files with 28 additions and 23 deletions

View file

@ -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))

View file

@ -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))

View file

@ -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;

View file

@ -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();

View file

@ -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;
DEVMODEW mode = {.dmSize = sizeof(mode)};
unsigned int i;
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;

View file

@ -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)

View file

@ -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;

View file

@ -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);