wined3d: Recreate swapchain textures in wined3d_swapchain_resize_buffers().

Instead of using wined3d_texture_update_desc(). This is safe, because:

* ddraw never exposes wined3d textures directly, and always retrieves them
  directly from wined3d when rendering.

* d3d8 and d3d9 (non-extended) will only resize buffers during a reset, and
  resetting is forbidden if the application holds any references to the
  backbuffers. RTVs are also replaced during a reset, so there is no concern
  about retrieving the old RTVs from the device state.

* d3d9ex allows resetting while holding references to the backbuffers, but tests
  (fixed by this patch) show that the backbuffers should in fact be recreated.

* dxgi forbids holding references to back buffers during ResizeBuffers(),
  including indirect references via command lists or device contexts.
This commit is contained in:
Zebediah Figura 2022-12-27 13:05:49 -06:00 committed by Alexandre Julliard
parent 037be380b2
commit ebaa0a9426
5 changed files with 137 additions and 30 deletions

View file

@ -940,8 +940,8 @@ static HRESULT CDECL reset_enum_callback(struct wined3d_resource *resource)
static HRESULT WINAPI d3d8_device_Reset(IDirect3DDevice8 *iface,
D3DPRESENT_PARAMETERS *present_parameters)
{
struct wined3d_swapchain_desc swapchain_desc, old_swapchain_desc;
struct d3d8_device *device = impl_from_IDirect3DDevice8(iface);
struct wined3d_swapchain_desc swapchain_desc;
struct d3d8_swapchain *implicit_swapchain;
unsigned int output_idx;
HRESULT hr;
@ -964,16 +964,26 @@ static HRESULT WINAPI d3d8_device_Reset(IDirect3DDevice8 *iface,
wined3d_streaming_buffer_cleanup(&device->vertex_buffer);
wined3d_streaming_buffer_cleanup(&device->index_buffer);
wined3d_swapchain_get_desc(device->implicit_swapchain, &old_swapchain_desc);
if (device->recording)
wined3d_stateblock_decref(device->recording);
device->recording = NULL;
device->update_state = device->state;
wined3d_stateblock_reset(device->state);
/* wined3d_device_reset() may recreate swapchain textures.
* We do not need to remove the reference to the wined3d swapchain from the
* old d3d8 surfaces: we will fail the reset if they have 0 references,
* and therefore they are not actually holding a reference to the wined3d
* swapchain, and will not do anything with it when they are destroyed. */
if (SUCCEEDED(hr = wined3d_device_reset(device->wined3d_device, &swapchain_desc,
NULL, reset_enum_callback, TRUE)))
{
struct wined3d_rendertarget_view *rtv;
struct d3d8_surface *surface;
unsigned int i;
present_parameters->BackBufferCount = swapchain_desc.backbuffer_count;
implicit_swapchain = wined3d_swapchain_get_parent(device->implicit_swapchain);
@ -984,10 +994,19 @@ static HRESULT WINAPI d3d8_device_Reset(IDirect3DDevice8 *iface,
!!swapchain_desc.enable_auto_depth_stencil);
device_reset_viewport_state(device);
/* FIXME: This should be the new backbuffer count, but we don't support
* changing the backbuffer count in wined3d yet. */
for (i = 0; i < old_swapchain_desc.backbuffer_count; ++i)
{
struct wined3d_texture *backbuffer = wined3d_swapchain_get_back_buffer(device->implicit_swapchain, i);
if ((surface = d3d8_surface_create(backbuffer, 0, (IUnknown *)&device->IDirect3DDevice8_iface)))
surface->parent_device = &device->IDirect3DDevice8_iface;
}
if ((rtv = wined3d_device_context_get_depth_stencil_view(device->immediate_context)))
{
struct wined3d_resource *resource = wined3d_rendertarget_view_get_resource(rtv);
struct d3d8_surface *surface;
if ((surface = d3d8_surface_create(wined3d_texture_from_resource(resource), 0,
(IUnknown *)&device->IDirect3DDevice8_iface)))

View file

@ -1045,8 +1045,9 @@ static HRESULT d3d9_device_get_swapchains(struct d3d9_device *device)
static HRESULT d3d9_device_reset(struct d3d9_device *device,
D3DPRESENT_PARAMETERS *present_parameters, D3DDISPLAYMODEEX *mode)
{
struct wined3d_swapchain_desc swapchain_desc, old_swapchain_desc;
struct d3d9_surface **old_backbuffers = NULL;
BOOL extended = device->d3d_parent->extended;
struct wined3d_swapchain_desc swapchain_desc;
struct wined3d_display_mode wined3d_mode;
struct wined3d_rendertarget_view *rtv;
struct d3d9_swapchain *d3d9_swapchain;
@ -1089,9 +1090,53 @@ static HRESULT d3d9_device_reset(struct d3d9_device *device,
wined3d_stateblock_reset(device->state);
}
if (FAILED(hr = d3d9_device_get_swapchains(device)))
{
wined3d_mutex_unlock();
return hr;
}
d3d9_swapchain = wined3d_swapchain_get_parent(device->implicit_swapchains[0]);
wined3d_swapchain_get_desc(d3d9_swapchain->wined3d_swapchain, &old_swapchain_desc);
/* wined3d_device_reset() may recreate swapchain textures.
*
* If the device is not extended, we don't need to remove the reference to
* the wined3d swapchain from the old d3d9 surfaces: we will fail the reset
* if they have 0 references, and therefore they are not actually holding a
* reference to the wined3d swapchain, and will not do anything with it when
* they are destroyed.
*
* If the device is extended, those swapchain textures can survive the
* reset, and need to be detached from the implicit swapchain here. To add
* some complexity, we need to add a temporary reference, but we can only do
* that in the extended case, otherwise we'll try to validate that there are
* 0 reference and fail. */
if (extended)
{
if (!(old_backbuffers = malloc(old_swapchain_desc.backbuffer_count * sizeof(*old_backbuffers))))
{
wined3d_mutex_unlock();
return hr;
}
for (i = 0; i < old_swapchain_desc.backbuffer_count; ++i)
{
old_backbuffers[i] = wined3d_texture_get_sub_resource_parent(
wined3d_swapchain_get_back_buffer(d3d9_swapchain->wined3d_swapchain, i), 0);
/* Resetting might drop the last reference, so grab an extra one here.
* This also lets us always decref the swapchain, without needing to
* worry about whether it's externally referenced. */
IDirect3DSurface9_AddRef(&old_backbuffers[i]->IDirect3DSurface9_iface);
}
}
if (SUCCEEDED(hr = wined3d_device_reset(device->wined3d_device, &swapchain_desc,
mode ? &wined3d_mode : NULL, reset_enum_callback, !extended)))
{
struct d3d9_surface *surface;
heap_free(device->implicit_swapchains);
if (!extended)
@ -1117,6 +1162,29 @@ static HRESULT d3d9_device_reset(struct d3d9_device *device,
present_parameters->BackBufferFormat = d3dformat_from_wined3dformat(swapchain_desc.backbuffer_format);
present_parameters->BackBufferCount = swapchain_desc.backbuffer_count;
if (extended)
{
for (i = 0; i < old_swapchain_desc.backbuffer_count; ++i)
{
surface = old_backbuffers[i];
/* We have a reference to this surface, so we can assert that it's
* currently holding a reference to the swapchain. */
wined3d_swapchain_decref(surface->swapchain);
surface->swapchain = NULL;
surface->container = (IUnknown *)&device->IDirect3DDevice9Ex_iface;
}
}
/* FIXME: This should be the new backbuffer count, but we don't support
* changing the backbuffer count in wined3d yet. */
for (i = 0; i < old_swapchain_desc.backbuffer_count; ++i)
{
struct wined3d_texture *backbuffer = wined3d_swapchain_get_back_buffer(d3d9_swapchain->wined3d_swapchain, i);
if ((surface = d3d9_surface_create(backbuffer, 0, (IUnknown *)&device->IDirect3DDevice9Ex_iface)))
surface->parent_device = &device->IDirect3DDevice9Ex_iface;
}
device->device_state = D3D9_DEVICE_STATE_OK;
if (extended)
@ -1146,7 +1214,6 @@ static HRESULT d3d9_device_reset(struct d3d9_device *device,
if ((rtv = wined3d_device_context_get_depth_stencil_view(device->immediate_context)))
{
struct wined3d_resource *resource = wined3d_rendertarget_view_get_resource(rtv);
struct d3d9_surface *surface;
if ((surface = d3d9_surface_create(wined3d_texture_from_resource(resource), 0,
(IUnknown *)&device->IDirect3DDevice9Ex_iface)))
@ -1158,6 +1225,13 @@ static HRESULT d3d9_device_reset(struct d3d9_device *device,
device->device_state = D3D9_DEVICE_STATE_NOT_RESET;
}
if (extended)
{
for (i = 0; i < old_swapchain_desc.backbuffer_count; ++i)
IDirect3DSurface9_Release(&old_backbuffers[i]->IDirect3DSurface9_iface);
free(old_backbuffers);
}
wined3d_mutex_unlock();
return hr;

View file

@ -3887,8 +3887,8 @@ static void test_backbuffer_resize(void)
old_backbuffer = backbuffer;
hr = IDirect3DSurface9_GetDesc(old_backbuffer, &surface_desc);
ok(SUCCEEDED(hr), "Failed to get surface desc, hr %#lx.\n", hr);
todo_wine ok(surface_desc.Width == 640, "Got unexpected width %u.\n", surface_desc.Width);
todo_wine ok(surface_desc.Height == 480, "Got unexpected height %u.\n", surface_desc.Height);
ok(surface_desc.Width == 640, "Got unexpected width %u.\n", surface_desc.Width);
ok(surface_desc.Height == 480, "Got unexpected height %u.\n", surface_desc.Height);
hr = IDirect3DDevice9_GetSwapChain(device, 0, &swapchain);
ok(hr == S_OK, "Got hr %#lx.\n", hr);
@ -3897,25 +3897,20 @@ static void test_backbuffer_resize(void)
IDirect3DSwapChain9_Release(old_swapchain);
hr = IDirect3DSurface9_GetContainer(old_backbuffer, &IID_IDirect3DSwapChain9, (void **)&swapchain);
todo_wine ok(hr == E_NOINTERFACE, "Got hr %#lx.\n", hr);
if (hr == S_OK)
IDirect3DSwapChain9_Release(swapchain);
ok(hr == E_NOINTERFACE, "Got hr %#lx.\n", hr);
hr = IDirect3DSurface9_GetContainer(old_backbuffer, &IID_IDirect3DBaseTexture9, (void **)&texture);
ok(hr == E_NOINTERFACE, "Got hr %#lx.\n", hr);
hr = IDirect3DSurface9_GetContainer(old_backbuffer, &IID_IDirect3DDevice9, (void **)&device2);
todo_wine ok(hr == S_OK, "Got hr %#lx.\n", hr);
if (hr == S_OK)
{
ok(device2 == device, "Devices didn't match.\n");
IDirect3DDevice9_Release(device2);
}
ok(hr == S_OK, "Got hr %#lx.\n", hr);
ok(device2 == device, "Devices didn't match.\n");
IDirect3DDevice9_Release(device2);
refcount = IDirect3DSurface9_Release(old_backbuffer);
ok(!refcount, "Surface has %lu references left.\n", refcount);
hr = IDirect3DDevice9_GetBackBuffer(device, 0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);
ok(SUCCEEDED(hr), "Failed to get backbuffer, hr %#lx.\n", hr);
todo_wine ok(backbuffer != old_backbuffer, "Expected new backbuffer surface.\n");
ok(backbuffer != old_backbuffer, "Expected new backbuffer surface.\n");
hr = IDirect3DDevice9_SetRenderTarget(device, 0, backbuffer);
ok(SUCCEEDED(hr), "Failed to set render target, hr %#lx.\n", hr);

View file

@ -507,6 +507,9 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDesc(IDXGISwapChain1 *iface,
return S_OK;
}
static HRESULT d3d11_swapchain_create_d3d11_textures(struct d3d11_swapchain *swapchain,
IWineDXGIDevice *device, struct wined3d_swapchain_desc *desc);
static HRESULT STDMETHODCALLTYPE d3d11_swapchain_ResizeBuffers(IDXGISwapChain1 *iface,
UINT buffer_count, UINT width, UINT height, DXGI_FORMAT format, UINT flags)
{
@ -540,6 +543,12 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_ResizeBuffers(IDXGISwapChain1 *
wined3d_desc.backbuffer_format = wined3dformat_from_dxgi_format(format);
hr = wined3d_swapchain_resize_buffers(swapchain->wined3d_swapchain, buffer_count, width, height,
wined3d_desc.backbuffer_format, wined3d_desc.multisample_type, wined3d_desc.multisample_quality);
/* wined3d_swapchain_resize_buffers() may recreate swapchain textures.
* We do not need to remove the reference to the wined3d swapchain from the
* old d3d11 textures: we just validated above that they have 0 references,
* and therefore they are not actually holding a reference to the wined3d
* swapchain, and will not do anything with it when they are destroyed. */
d3d11_swapchain_create_d3d11_textures(swapchain, swapchain->device, &wined3d_desc);
wined3d_mutex_unlock();
return hr;
@ -827,14 +836,14 @@ static const struct wined3d_swapchain_state_parent_ops d3d11_swapchain_state_par
};
static HRESULT d3d11_swapchain_create_d3d11_textures(struct d3d11_swapchain *swapchain,
struct dxgi_device *device, struct wined3d_swapchain_desc *desc)
IWineDXGIDevice *device, struct wined3d_swapchain_desc *desc)
{
IWineDXGIDeviceParent *dxgi_device_parent;
unsigned int texture_flags = 0;
unsigned int i;
HRESULT hr;
if (FAILED(hr = IWineDXGIDevice_QueryInterface(&device->IWineDXGIDevice_iface,
if (FAILED(hr = IWineDXGIDevice_QueryInterface(device,
&IID_IWineDXGIDeviceParent, (void **)&dxgi_device_parent)))
{
ERR("Device should implement IWineDXGIDeviceParent.\n");
@ -898,7 +907,7 @@ HRESULT d3d11_swapchain_init(struct d3d11_swapchain *swapchain, struct dxgi_devi
goto cleanup;
}
if (FAILED(hr = d3d11_swapchain_create_d3d11_textures(swapchain, device, desc)))
if (FAILED(hr = d3d11_swapchain_create_d3d11_textures(swapchain, &device->IWineDXGIDevice_iface, desc)))
{
ERR("Failed to create d3d11 textures, hr %#lx.\n", hr);
goto cleanup;

View file

@ -1929,7 +1929,7 @@ HRESULT CDECL wined3d_swapchain_resize_buffers(struct wined3d_swapchain *swapcha
enum wined3d_multisample_type multisample_type, unsigned int multisample_quality)
{
struct wined3d_swapchain_desc *desc = &swapchain->state.desc;
BOOL update_desc = FALSE;
bool recreate = false;
TRACE("swapchain %p, buffer_count %u, width %u, height %u, format %s, "
"multisample_type %#x, multisample_quality %#x.\n",
@ -1968,7 +1968,7 @@ HRESULT CDECL wined3d_swapchain_resize_buffers(struct wined3d_swapchain *swapcha
{
desc->backbuffer_width = width;
desc->backbuffer_height = height;
update_desc = TRUE;
recreate = true;
}
if (format_id == WINED3DFMT_UNKNOWN)
@ -1981,7 +1981,7 @@ HRESULT CDECL wined3d_swapchain_resize_buffers(struct wined3d_swapchain *swapcha
if (format_id != desc->backbuffer_format)
{
desc->backbuffer_format = format_id;
update_desc = TRUE;
recreate = true;
}
if (multisample_type != desc->multisample_type
@ -1989,25 +1989,35 @@ HRESULT CDECL wined3d_swapchain_resize_buffers(struct wined3d_swapchain *swapcha
{
desc->multisample_type = multisample_type;
desc->multisample_quality = multisample_quality;
update_desc = TRUE;
recreate = true;
}
if (update_desc)
if (recreate)
{
struct wined3d_texture *new_texture;
HRESULT hr;
UINT i;
if (FAILED(hr = wined3d_texture_update_desc(swapchain->front_buffer, 0, desc->backbuffer_width,
desc->backbuffer_height, desc->backbuffer_format,
desc->multisample_type, desc->multisample_quality, NULL, 0)))
TRACE("Recreating swapchain textures.\n");
if (FAILED(hr = swapchain_create_texture(swapchain, true, false, &new_texture)))
return hr;
wined3d_texture_set_swapchain(swapchain->front_buffer, NULL);
if (wined3d_texture_decref(swapchain->front_buffer))
ERR("Something's still holding the front buffer (%p).\n", swapchain->front_buffer);
swapchain->front_buffer = new_texture;
wined3d_texture_validate_location(swapchain->front_buffer, 0, WINED3D_LOCATION_DRAWABLE);
wined3d_texture_invalidate_location(swapchain->front_buffer, 0, ~WINED3D_LOCATION_DRAWABLE);
for (i = 0; i < desc->backbuffer_count; ++i)
{
if (FAILED(hr = wined3d_texture_update_desc(swapchain->back_buffers[i], 0, desc->backbuffer_width,
desc->backbuffer_height, desc->backbuffer_format,
desc->multisample_type, desc->multisample_quality, NULL, 0)))
if (FAILED(hr = swapchain_create_texture(swapchain, false, false, &new_texture)))
return hr;
wined3d_texture_set_swapchain(swapchain->back_buffers[i], NULL);
if (wined3d_texture_decref(swapchain->back_buffers[i]))
ERR("Something's still holding back buffer %u (%p).\n", i, swapchain->back_buffers[i]);
swapchain->back_buffers[i] = new_texture;
}
}