| // |
| // Copyright (c) 2014 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // SwapChainPanelNativeWindow.cpp: NativeWindow for managing ISwapChainPanel native window types. |
| |
| #include "libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.h" |
| |
| #include <algorithm> |
| #include <math.h> |
| |
| using namespace ABI::Windows::Foundation; |
| using namespace ABI::Windows::Foundation::Collections; |
| using namespace ABI::Windows::UI::Core; |
| using namespace ABI::Windows::UI::Xaml; |
| using namespace Microsoft::WRL; |
| |
| namespace rx |
| { |
| SwapChainPanelNativeWindow::~SwapChainPanelNativeWindow() |
| { |
| unregisterForSizeChangeEvents(); |
| } |
| |
| template <typename T> |
| struct AddFtmBase |
| { |
| typedef Implements<RuntimeClassFlags<ClassicCom>, T, FtmBase> Type; |
| }; |
| |
| template <typename CODE> |
| HRESULT RunOnUIThread(CODE &&code, const ComPtr<ICoreDispatcher> &dispatcher) |
| { |
| ComPtr<IAsyncAction> asyncAction; |
| HRESULT result = S_OK; |
| |
| boolean hasThreadAccess; |
| result = dispatcher->get_HasThreadAccess(&hasThreadAccess); |
| if (FAILED(result)) |
| { |
| return result; |
| } |
| |
| if (hasThreadAccess) |
| { |
| return code(); |
| } |
| else |
| { |
| Event waitEvent( |
| CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS)); |
| if (!waitEvent.IsValid()) |
| { |
| return E_FAIL; |
| } |
| |
| HRESULT codeResult = E_FAIL; |
| auto handler = |
| Callback<AddFtmBase<IDispatchedHandler>::Type>([&codeResult, &code, &waitEvent] |
| { |
| codeResult = code(); |
| SetEvent(waitEvent.Get()); |
| return S_OK; |
| }); |
| |
| result = dispatcher->RunAsync(CoreDispatcherPriority_Normal, handler.Get(), |
| asyncAction.GetAddressOf()); |
| if (FAILED(result)) |
| { |
| return result; |
| } |
| |
| auto waitResult = WaitForSingleObjectEx(waitEvent.Get(), 10 * 1000, true); |
| if (waitResult != WAIT_OBJECT_0) |
| { |
| // Wait 10 seconds before giving up. At this point, the application is in an |
| // unrecoverable state (probably deadlocked). We therefore terminate the application |
| // entirely. This also prevents stack corruption if the async operation is eventually |
| // run. |
| ERR() |
| << "Timeout waiting for async action on UI thread. The UI thread might be blocked."; |
| std::terminate(); |
| return E_FAIL; |
| } |
| |
| return codeResult; |
| } |
| } |
| |
| bool SwapChainPanelNativeWindow::initialize(EGLNativeWindowType window, IPropertySet *propertySet) |
| { |
| ComPtr<IPropertySet> props = propertySet; |
| ComPtr<IInspectable> win = static_cast<IInspectable *>(window); |
| SIZE swapChainSize = {}; |
| HRESULT result = S_OK; |
| |
| // IPropertySet is an optional parameter and can be null. |
| // If one is specified, cache as an IMap and read the properties |
| // used for initial host initialization. |
| if (propertySet) |
| { |
| result = props.As(&mPropertyMap); |
| if (FAILED(result)) |
| { |
| return false; |
| } |
| |
| // The EGLRenderSurfaceSizeProperty is optional and may be missing. The IPropertySet |
| // was prevalidated to contain the EGLNativeWindowType before being passed to |
| // this host. |
| result = GetOptionalSizePropertyValue(mPropertyMap, EGLRenderSurfaceSizeProperty, &swapChainSize, &mSwapChainSizeSpecified); |
| if (FAILED(result)) |
| { |
| return false; |
| } |
| |
| // The EGLRenderResolutionScaleProperty is optional and may be missing. The IPropertySet |
| // was prevalidated to contain the EGLNativeWindowType before being passed to |
| // this host. |
| result = GetOptionalSinglePropertyValue(mPropertyMap, EGLRenderResolutionScaleProperty, &mSwapChainScale, &mSwapChainScaleSpecified); |
| if (FAILED(result)) |
| { |
| return false; |
| } |
| |
| if (!mSwapChainScaleSpecified) |
| { |
| // Default value for the scale is 1.0f |
| mSwapChainScale = 1.0f; |
| } |
| |
| // A EGLRenderSurfaceSizeProperty and a EGLRenderResolutionScaleProperty can't both be specified |
| if (mSwapChainScaleSpecified && mSwapChainSizeSpecified) |
| { |
| ERR() << "It is invalid to specify both an EGLRenderSurfaceSizeProperty and a " |
| "EGLRenderResolutionScaleProperty."; |
| return false; |
| } |
| } |
| |
| if (SUCCEEDED(result)) |
| { |
| result = win.As(&mSwapChainPanel); |
| } |
| |
| ComPtr<IDependencyObject> swapChainPanelDependencyObject; |
| if (SUCCEEDED(result)) |
| { |
| result = mSwapChainPanel.As(&swapChainPanelDependencyObject); |
| } |
| |
| if (SUCCEEDED(result)) |
| { |
| result = swapChainPanelDependencyObject->get_Dispatcher( |
| mSwapChainPanelDispatcher.GetAddressOf()); |
| } |
| |
| if (SUCCEEDED(result)) |
| { |
| // If a swapchain size is specfied, then the automatic resize |
| // behaviors implemented by the host should be disabled. The swapchain |
| // will be still be scaled when being rendered to fit the bounds |
| // of the host. |
| // Scaling of the swapchain output needs to be handled by the |
| // host for swapchain panels even though the scaling mode setting |
| // DXGI_SCALING_STRETCH is configured on the swapchain. |
| if (mSwapChainSizeSpecified) |
| { |
| mClientRect = { 0, 0, swapChainSize.cx, swapChainSize.cy }; |
| } |
| else |
| { |
| Size swapChainPanelSize; |
| result = GetSwapChainPanelSize(mSwapChainPanel, mSwapChainPanelDispatcher, |
| &swapChainPanelSize); |
| |
| if (SUCCEEDED(result)) |
| { |
| // Update the client rect to account for any swapchain scale factor |
| mClientRect = clientRect(swapChainPanelSize); |
| } |
| } |
| } |
| |
| if (SUCCEEDED(result)) |
| { |
| mNewClientRect = mClientRect; |
| mClientRectChanged = false; |
| return registerForSizeChangeEvents(); |
| } |
| |
| return false; |
| } |
| |
| bool SwapChainPanelNativeWindow::registerForSizeChangeEvents() |
| { |
| ComPtr<ISizeChangedEventHandler> sizeChangedHandler; |
| ComPtr<IFrameworkElement> frameworkElement; |
| HRESULT result = Microsoft::WRL::MakeAndInitialize<SwapChainPanelSizeChangedHandler>(sizeChangedHandler.ReleaseAndGetAddressOf(), this->shared_from_this()); |
| |
| if (SUCCEEDED(result)) |
| { |
| result = mSwapChainPanel.As(&frameworkElement); |
| } |
| |
| if (SUCCEEDED(result)) |
| { |
| result = RunOnUIThread( |
| [this, frameworkElement, sizeChangedHandler] |
| { |
| return frameworkElement->add_SizeChanged(sizeChangedHandler.Get(), |
| &mSizeChangedEventToken); |
| }, |
| mSwapChainPanelDispatcher); |
| } |
| |
| if (SUCCEEDED(result)) |
| { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void SwapChainPanelNativeWindow::unregisterForSizeChangeEvents() |
| { |
| ComPtr<IFrameworkElement> frameworkElement; |
| if (mSwapChainPanel && SUCCEEDED(mSwapChainPanel.As(&frameworkElement))) |
| { |
| RunOnUIThread( |
| [this, frameworkElement] |
| { |
| return frameworkElement->remove_SizeChanged(mSizeChangedEventToken); |
| }, |
| mSwapChainPanelDispatcher); |
| } |
| |
| mSizeChangedEventToken.value = 0; |
| } |
| |
| HRESULT SwapChainPanelNativeWindow::createSwapChain(ID3D11Device *device, |
| IDXGIFactory2 *factory, |
| DXGI_FORMAT format, |
| unsigned int width, |
| unsigned int height, |
| bool containsAlpha, |
| IDXGISwapChain1 **swapChain) |
| { |
| if (device == nullptr || factory == nullptr || swapChain == nullptr || width == 0 || |
| height == 0) |
| { |
| return E_INVALIDARG; |
| } |
| |
| DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 }; |
| swapChainDesc.Width = width; |
| swapChainDesc.Height = height; |
| swapChainDesc.Format = format; |
| swapChainDesc.Stereo = FALSE; |
| swapChainDesc.SampleDesc.Count = 1; |
| swapChainDesc.SampleDesc.Quality = 0; |
| swapChainDesc.BufferUsage = |
| DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER; |
| swapChainDesc.BufferCount = 2; |
| swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; |
| swapChainDesc.Scaling = DXGI_SCALING_STRETCH; |
| swapChainDesc.AlphaMode = |
| containsAlpha ? DXGI_ALPHA_MODE_PREMULTIPLIED : DXGI_ALPHA_MODE_IGNORE; |
| |
| *swapChain = nullptr; |
| |
| ComPtr<IDXGISwapChain1> newSwapChain; |
| ComPtr<ISwapChainPanelNative> swapChainPanelNative; |
| Size currentPanelSize = {}; |
| |
| HRESULT result = factory->CreateSwapChainForComposition(device, &swapChainDesc, nullptr, newSwapChain.ReleaseAndGetAddressOf()); |
| |
| if (SUCCEEDED(result)) |
| { |
| result = mSwapChainPanel.As(&swapChainPanelNative); |
| } |
| |
| if (SUCCEEDED(result)) |
| { |
| result = RunOnUIThread( |
| [swapChainPanelNative, newSwapChain] |
| { |
| return swapChainPanelNative->SetSwapChain(newSwapChain.Get()); |
| }, |
| mSwapChainPanelDispatcher); |
| } |
| |
| if (SUCCEEDED(result)) |
| { |
| // The swapchain panel host requires an instance of the swapchain set on the SwapChainPanel |
| // to perform the runtime-scale behavior. This swapchain is cached here because there are |
| // no methods for retreiving the currently configured on from ISwapChainPanelNative. |
| mSwapChain = newSwapChain; |
| result = newSwapChain.CopyTo(swapChain); |
| } |
| |
| // If the host is responsible for scaling the output of the swapchain, then |
| // scale it now before returning an instance to the caller. This is done by |
| // first reading the current size of the swapchain panel, then scaling |
| if (SUCCEEDED(result)) |
| { |
| if (mSwapChainSizeSpecified || mSwapChainScaleSpecified) |
| { |
| result = GetSwapChainPanelSize(mSwapChainPanel, mSwapChainPanelDispatcher, |
| ¤tPanelSize); |
| |
| // Scale the swapchain to fit inside the contents of the panel. |
| if (SUCCEEDED(result)) |
| { |
| result = scaleSwapChain(currentPanelSize, mClientRect); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| HRESULT SwapChainPanelNativeWindow::scaleSwapChain(const Size &windowSize, const RECT &clientRect) |
| { |
| Size renderScale = {windowSize.Width / (float)clientRect.right, |
| windowSize.Height / (float)clientRect.bottom}; |
| // Setup a scale matrix for the swap chain |
| DXGI_MATRIX_3X2_F scaleMatrix = {}; |
| scaleMatrix._11 = renderScale.Width; |
| scaleMatrix._22 = renderScale.Height; |
| |
| ComPtr<IDXGISwapChain2> swapChain2; |
| HRESULT result = mSwapChain.As(&swapChain2); |
| if (SUCCEEDED(result)) |
| { |
| result = swapChain2->SetMatrixTransform(&scaleMatrix); |
| } |
| |
| return result; |
| } |
| |
| HRESULT GetSwapChainPanelSize( |
| const ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> &swapChainPanel, |
| const ComPtr<ICoreDispatcher> &dispatcher, |
| Size *windowSize) |
| { |
| ComPtr<IUIElement> uiElement; |
| HRESULT result = swapChainPanel.As(&uiElement); |
| if (SUCCEEDED(result)) |
| { |
| result = RunOnUIThread( |
| [uiElement, windowSize] { return uiElement->get_RenderSize(windowSize); }, dispatcher); |
| } |
| |
| return result; |
| } |
| } |