dxgi: real composition swapchains (CreateSwapChainForComposition) on top of 02. LGPL-2.1-or-later. diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h index ed82d36..9ac7e20 100644 --- a/dlls/dxgi/dxgi_private.h +++ b/dlls/dxgi/dxgi_private.h @@ -185,8 +185,16 @@ struct d3d11_swapchain IDXGIOutput *target; LONG present_count; LONG in_set_fullscreen_state; + + /* Hidden window backing a CreateSwapChainForComposition() swapchain. */ + HWND composition_window; + DXGI_ALPHA_MODE alpha_mode; + DXGI_SWAP_EFFECT swap_effect; }; +BOOL d3d11_swapchain_set_composition_window(IDXGISwapChain1 *iface, HWND window, + DXGI_ALPHA_MODE alpha_mode, DXGI_SWAP_EFFECT swap_effect); + HRESULT d3d11_swapchain_init(struct d3d11_swapchain *swapchain, struct dxgi_device *device, struct wined3d_swapchain_desc *desc, const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc); diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c index d1015dd..bc717c6 100644 --- a/dlls/dxgi/factory.c +++ b/dlls/dxgi/factory.c @@ -390,13 +390,66 @@ static void STDMETHODCALLTYPE dxgi_factory_UnregisterOcclusionStatus(IWineDXGIFa static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForComposition(IWineDXGIFactory *iface, IUnknown *device, const DXGI_SWAP_CHAIN_DESC1 *desc, IDXGIOutput *output, IDXGISwapChain1 **swapchain) { - HWND hwnd; + IWineDXGISwapChainFactory *swapchain_factory; + DXGI_SWAP_CHAIN_DESC1 composition_desc; + IDXGISwapChain1 *object; + HWND window; + HRESULT hr; - FIXME("iface %p, device %p, desc %p, output %p, swapchain %p stub!\n", + TRACE("iface %p, device %p, desc %p, output %p, swapchain %p.\n", iface, device, desc, output, swapchain); - hwnd = CreateWindowA("static", NULL, WS_POPUP, 0, 0, desc->Width, desc->Height, 0, 0, 0, NULL); - return dxgi_factory_CreateSwapChainForHwnd(iface, device, hwnd, desc, NULL, output, swapchain); + if (!device || !desc || !swapchain) + return DXGI_ERROR_INVALID_CALL; + *swapchain = NULL; + + if (desc->SwapEffect != DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL && desc->SwapEffect != DXGI_SWAP_EFFECT_FLIP_DISCARD) + { + WARN("Invalid swap effect %#x for a composition swapchain.\n", desc->SwapEffect); + return DXGI_ERROR_INVALID_CALL; + } + if (!desc->Width || !desc->Height) + { + WARN("Invalid size %ux%u for a composition swapchain.\n", desc->Width, desc->Height); + return DXGI_ERROR_INVALID_CALL; + } + + /* Only D3D10/11 devices; dcomp reads composition content back through D3D11. */ + if (FAILED(IUnknown_QueryInterface(device, &IID_IWineDXGISwapChainFactory, (void **)&swapchain_factory))) + { + FIXME("Composition swapchains are only implemented for D3D10/11 devices.\n"); + return E_NOTIMPL; + } + IWineDXGISwapChainFactory_Release(swapchain_factory); + + /* There is no compositor to hand the buffers to. Present into a hidden + * window instead and let dcomp.dll read the presented buffers back. */ + /* A message-only window: it is never shown, and broadcasts do not wait on + * the thread that created the swapchain. */ + if (!(window = CreateWindowExW(0, L"static", NULL, WS_DISABLED, 0, 0, + desc->Width, desc->Height, HWND_MESSAGE, NULL, NULL, NULL))) + return HRESULT_FROM_WIN32(GetLastError()); + + /* wined3d discards a FLIP_DISCARD buffer once it has been presented, but + * that is the buffer dcomp.dll reads the frame from. */ + composition_desc = *desc; + composition_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + if (FAILED(hr = dxgi_factory_CreateSwapChainForHwnd(iface, device, window, &composition_desc, NULL, output, &object))) + { + DestroyWindow(window); + return hr; + } + + if (!d3d11_swapchain_set_composition_window(object, window, desc->AlphaMode, desc->SwapEffect)) + { + ERR("Unexpected swapchain implementation.\n"); + IDXGISwapChain1_Release(object); + DestroyWindow(window); + return E_FAIL; + } + + *swapchain = object; + return S_OK; } static UINT STDMETHODCALLTYPE dxgi_factory_GetCreationFlags(IWineDXGIFactory *iface) diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c index 1186527..ad4335b 100644 --- a/dlls/dxgi/swapchain.c +++ b/dlls/dxgi/swapchain.c @@ -256,6 +256,8 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain4 *iface) if (!refcount) { IWineDXGIDevice *device = swapchain->device; + HWND composition_window = swapchain->composition_window; + if (swapchain->target) { WARN("Releasing fullscreen swapchain.\n"); @@ -264,6 +266,16 @@ static ULONG STDMETHODCALLTYPE d3d11_swapchain_Release(IDXGISwapChain4 *iface) IWineDXGIFactory_Release(swapchain->factory); wined3d_swapchain_decref(swapchain->wined3d_swapchain); IWineDXGIDevice_Release(device); + /* The wined3d swapchain owned the memory of "swapchain". */ + if (composition_window) + { + /* Only the creating thread may destroy the window; elsewhere let + * the static control destroy itself when it handles WM_CLOSE. */ + if (GetWindowThreadProcessId(composition_window, NULL) == GetCurrentThreadId()) + DestroyWindow(composition_window); + else + PostMessageW(composition_window, WM_CLOSE, 0, 0); + } } return refcount; @@ -537,7 +549,8 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDesc(IDXGISwapChain4 *iface, desc->BufferCount = wined3d_desc.backbuffer_count; desc->OutputWindow = wined3d_desc.device_window; desc->Windowed = wined3d_desc.windowed; - desc->SwapEffect = dxgi_swap_effect_from_wined3d(wined3d_desc.swap_effect); + desc->SwapEffect = swapchain->composition_window ? swapchain->swap_effect + : dxgi_swap_effect_from_wined3d(wined3d_desc.swap_effect); desc->Flags = dxgi_swapchain_flags_from_wined3d(wined3d_desc.flags); return S_OK; @@ -681,8 +694,9 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetDesc1(IDXGISwapChain4 *iface desc->BufferUsage = dxgi_usage_from_wined3d_bind_flags(wined3d_desc.backbuffer_bind_flags); desc->BufferCount = wined3d_desc.backbuffer_count; desc->Scaling = DXGI_SCALING_STRETCH; - desc->SwapEffect = dxgi_swap_effect_from_wined3d(wined3d_desc.swap_effect); - desc->AlphaMode = DXGI_ALPHA_MODE_IGNORE; + desc->SwapEffect = swapchain->composition_window ? swapchain->swap_effect + : dxgi_swap_effect_from_wined3d(wined3d_desc.swap_effect); + desc->AlphaMode = swapchain->composition_window ? swapchain->alpha_mode : DXGI_ALPHA_MODE_IGNORE; desc->Flags = dxgi_swapchain_flags_from_wined3d(wined3d_desc.flags); return S_OK; @@ -741,6 +755,56 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetCoreWindow(IDXGISwapChain4 * return DXGI_ERROR_INVALID_CALL; } +/* A flip-model Present1() with dirty rectangles shows the previously presented + * frame with only the dirty rectangles replaced, and applications such as + * JUCE 8 only redraw those rectangles. wined3d rotates its back buffers on + * present, so back buffer 0 holds an older frame by then. Rebuild the frame + * here: patch the dirty rectangles into the last presented buffer and copy the + * result into back buffer 0 before presenting it. + * + * This is limited to composition swapchains for now, where dcomp.dll shows + * the last presented buffer; HWND swapchains have the same problem. */ +static void d3d11_swapchain_merge_dirty_rects(struct d3d11_swapchain *swapchain, + const DXGI_PRESENT_PARAMETERS *parameters) +{ + struct wined3d_resource *back_buffer, *previous; + struct wined3d_device_context *context; + struct wined3d_swapchain_desc desc; + struct wined3d_box box; + unsigned int i; + RECT r; + + wined3d_mutex_lock(); + wined3d_swapchain_get_desc(swapchain->wined3d_swapchain, &desc); + if (!swapchain->composition_window || desc.swap_effect != WINED3D_SWAP_EFFECT_FLIP_SEQUENTIAL + || desc.backbuffer_count < 2 || !swapchain->present_count || !parameters->pDirtyRects) + { + wined3d_mutex_unlock(); + return; + } + + back_buffer = wined3d_texture_get_resource(wined3d_swapchain_get_back_buffer(swapchain->wined3d_swapchain, 0)); + previous = wined3d_texture_get_resource(wined3d_swapchain_get_back_buffer(swapchain->wined3d_swapchain, + desc.backbuffer_count - 1)); + context = wined3d_device_get_immediate_context(wined3d_swapchain_get_device(swapchain->wined3d_swapchain)); + + for (i = 0; i < parameters->DirtyRectsCount; ++i) + { + r = parameters->pDirtyRects[i]; + r.left = max(r.left, 0); + r.top = max(r.top, 0); + r.right = min(r.right, (LONG)desc.backbuffer_width); + r.bottom = min(r.bottom, (LONG)desc.backbuffer_height); + if (r.left >= r.right || r.top >= r.bottom) + continue; + wined3d_box_set(&box, r.left, r.top, r.right, r.bottom, 0, 1); + wined3d_device_context_copy_sub_resource_region(context, previous, 0, + r.left, r.top, 0, back_buffer, 0, &box, 0); + } + wined3d_device_context_copy_resource(context, back_buffer, previous); + wined3d_mutex_unlock(); +} + static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain4 *iface, UINT sync_interval, UINT flags, const DXGI_PRESENT_PARAMETERS *present_parameters) { @@ -750,7 +814,13 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_Present1(IDXGISwapChain4 *iface iface, sync_interval, flags, present_parameters); if (present_parameters) - FIXME("Ignored present parameters %p.\n", present_parameters); + { + if (present_parameters->pScrollRect || present_parameters->pScrollOffset) + FIXME("Ignoring scroll rectangle %p, offset %p.\n", + present_parameters->pScrollRect, present_parameters->pScrollOffset); + if (present_parameters->DirtyRectsCount && !(flags & DXGI_PRESENT_TEST)) + d3d11_swapchain_merge_dirty_rects(swapchain, present_parameters); + } return d3d11_swapchain_present(swapchain, sync_interval, flags); } @@ -1043,6 +1113,21 @@ static HRESULT d3d11_swapchain_create_d3d11_textures(struct d3d11_swapchain *swa return hr; } +BOOL d3d11_swapchain_set_composition_window(IDXGISwapChain1 *iface, HWND window, + DXGI_ALPHA_MODE alpha_mode, DXGI_SWAP_EFFECT swap_effect) +{ + struct d3d11_swapchain *swapchain; + + if (iface->lpVtbl != (const IDXGISwapChain1Vtbl *)&d3d11_swapchain_vtbl) + return FALSE; + + swapchain = d3d11_swapchain_from_IDXGISwapChain4((IDXGISwapChain4 *)iface); + swapchain->composition_window = window; + swapchain->alpha_mode = alpha_mode; + swapchain->swap_effect = swap_effect; + return TRUE; +} + HRESULT d3d11_swapchain_init(struct d3d11_swapchain *swapchain, struct dxgi_device *device, struct wined3d_swapchain_desc *desc, const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc) {