1 //
2 // Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6
7 // SwapChainPanelNativeWindow.cpp: NativeWindow for managing ISwapChainPanel native window types.
8
9 #include "libANGLE/renderer/d3d/d3d11/winrt/SwapChainPanelNativeWindow.h"
10
11 #include <algorithm>
12 #include <math.h>
13
14 using namespace ABI::Windows::Foundation;
15 using namespace ABI::Windows::Foundation::Collections;
16 using namespace ABI::Windows::UI::Core;
17 using namespace ABI::Windows::UI::Xaml;
18 using namespace Microsoft::WRL;
19
20 namespace rx
21 {
~SwapChainPanelNativeWindow()22 SwapChainPanelNativeWindow::~SwapChainPanelNativeWindow()
23 {
24 unregisterForSizeChangeEvents();
25 }
26
27 template <typename T>
28 struct AddFtmBase
29 {
30 typedef Implements<RuntimeClassFlags<ClassicCom>, T, FtmBase> Type;
31 };
32
33 template <typename CODE>
RunOnUIThread(CODE && code,const ComPtr<ICoreDispatcher> & dispatcher)34 HRESULT RunOnUIThread(CODE &&code, const ComPtr<ICoreDispatcher> &dispatcher)
35 {
36 ComPtr<IAsyncAction> asyncAction;
37 HRESULT result = S_OK;
38
39 boolean hasThreadAccess;
40 result = dispatcher->get_HasThreadAccess(&hasThreadAccess);
41 if (FAILED(result))
42 {
43 return result;
44 }
45
46 if (hasThreadAccess)
47 {
48 return code();
49 }
50 else
51 {
52 Event waitEvent(
53 CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, EVENT_ALL_ACCESS));
54 if (!waitEvent.IsValid())
55 {
56 return E_FAIL;
57 }
58
59 HRESULT codeResult = E_FAIL;
60 auto handler =
61 Callback<AddFtmBase<IDispatchedHandler>::Type>([&codeResult, &code, &waitEvent]
62 {
63 codeResult = code();
64 SetEvent(waitEvent.Get());
65 return S_OK;
66 });
67
68 result = dispatcher->RunAsync(CoreDispatcherPriority_Normal, handler.Get(),
69 asyncAction.GetAddressOf());
70 if (FAILED(result))
71 {
72 return result;
73 }
74
75 auto waitResult = WaitForSingleObjectEx(waitEvent.Get(), 10 * 1000, true);
76 if (waitResult != WAIT_OBJECT_0)
77 {
78 // Wait 10 seconds before giving up. At this point, the application is in an
79 // unrecoverable state (probably deadlocked). We therefore terminate the application
80 // entirely. This also prevents stack corruption if the async operation is eventually
81 // run.
82 ERR()
83 << "Timeout waiting for async action on UI thread. The UI thread might be blocked.";
84 std::terminate();
85 return E_FAIL;
86 }
87
88 return codeResult;
89 }
90 }
91
initialize(EGLNativeWindowType window,IPropertySet * propertySet)92 bool SwapChainPanelNativeWindow::initialize(EGLNativeWindowType window, IPropertySet *propertySet)
93 {
94 ComPtr<IPropertySet> props = propertySet;
95 ComPtr<IInspectable> win = window;
96 SIZE swapChainSize = {};
97 HRESULT result = S_OK;
98
99 // IPropertySet is an optional parameter and can be null.
100 // If one is specified, cache as an IMap and read the properties
101 // used for initial host initialization.
102 if (propertySet)
103 {
104 result = props.As(&mPropertyMap);
105 if (FAILED(result))
106 {
107 return false;
108 }
109
110 // The EGLRenderSurfaceSizeProperty is optional and may be missing. The IPropertySet
111 // was prevalidated to contain the EGLNativeWindowType before being passed to
112 // this host.
113 result = GetOptionalSizePropertyValue(mPropertyMap, EGLRenderSurfaceSizeProperty, &swapChainSize, &mSwapChainSizeSpecified);
114 if (FAILED(result))
115 {
116 return false;
117 }
118
119 // The EGLRenderResolutionScaleProperty is optional and may be missing. The IPropertySet
120 // was prevalidated to contain the EGLNativeWindowType before being passed to
121 // this host.
122 result = GetOptionalSinglePropertyValue(mPropertyMap, EGLRenderResolutionScaleProperty, &mSwapChainScale, &mSwapChainScaleSpecified);
123 if (FAILED(result))
124 {
125 return false;
126 }
127
128 if (!mSwapChainScaleSpecified)
129 {
130 // Default value for the scale is 1.0f
131 mSwapChainScale = 1.0f;
132 }
133
134 // A EGLRenderSurfaceSizeProperty and a EGLRenderResolutionScaleProperty can't both be specified
135 if (mSwapChainScaleSpecified && mSwapChainSizeSpecified)
136 {
137 ERR() << "It is invalid to specify both an EGLRenderSurfaceSizeProperty and a "
138 "EGLRenderResolutionScaleProperty.";
139 return false;
140 }
141 }
142
143 if (SUCCEEDED(result))
144 {
145 result = win.As(&mSwapChainPanel);
146 }
147
148 ComPtr<IDependencyObject> swapChainPanelDependencyObject;
149 if (SUCCEEDED(result))
150 {
151 result = mSwapChainPanel.As(&swapChainPanelDependencyObject);
152 }
153
154 if (SUCCEEDED(result))
155 {
156 result = swapChainPanelDependencyObject->get_Dispatcher(
157 mSwapChainPanelDispatcher.GetAddressOf());
158 }
159
160 if (SUCCEEDED(result))
161 {
162 // If a swapchain size is specfied, then the automatic resize
163 // behaviors implemented by the host should be disabled. The swapchain
164 // will be still be scaled when being rendered to fit the bounds
165 // of the host.
166 // Scaling of the swapchain output needs to be handled by the
167 // host for swapchain panels even though the scaling mode setting
168 // DXGI_SCALING_STRETCH is configured on the swapchain.
169 if (mSwapChainSizeSpecified)
170 {
171 mClientRect = { 0, 0, swapChainSize.cx, swapChainSize.cy };
172 }
173 else
174 {
175 Size swapChainPanelSize;
176 result = GetSwapChainPanelSize(mSwapChainPanel, mSwapChainPanelDispatcher,
177 &swapChainPanelSize);
178
179 if (SUCCEEDED(result))
180 {
181 // Update the client rect to account for any swapchain scale factor
182 mClientRect = clientRect(swapChainPanelSize);
183 }
184 }
185 }
186
187 if (SUCCEEDED(result))
188 {
189 mNewClientRect = mClientRect;
190 mClientRectChanged = false;
191 return registerForSizeChangeEvents();
192 }
193
194 return false;
195 }
196
registerForSizeChangeEvents()197 bool SwapChainPanelNativeWindow::registerForSizeChangeEvents()
198 {
199 ComPtr<ISizeChangedEventHandler> sizeChangedHandler;
200 ComPtr<IFrameworkElement> frameworkElement;
201 HRESULT result = Microsoft::WRL::MakeAndInitialize<SwapChainPanelSizeChangedHandler>(sizeChangedHandler.ReleaseAndGetAddressOf(), this->shared_from_this());
202
203 if (SUCCEEDED(result))
204 {
205 result = mSwapChainPanel.As(&frameworkElement);
206 }
207
208 if (SUCCEEDED(result))
209 {
210 result = RunOnUIThread(
211 [this, frameworkElement, sizeChangedHandler]
212 {
213 return frameworkElement->add_SizeChanged(sizeChangedHandler.Get(),
214 &mSizeChangedEventToken);
215 },
216 mSwapChainPanelDispatcher);
217 }
218
219 if (SUCCEEDED(result))
220 {
221 return true;
222 }
223
224 return false;
225 }
226
unregisterForSizeChangeEvents()227 void SwapChainPanelNativeWindow::unregisterForSizeChangeEvents()
228 {
229 ComPtr<IFrameworkElement> frameworkElement;
230 if (mSwapChainPanel && SUCCEEDED(mSwapChainPanel.As(&frameworkElement)))
231 {
232 RunOnUIThread(
233 [this, frameworkElement]
234 {
235 return frameworkElement->remove_SizeChanged(mSizeChangedEventToken);
236 },
237 mSwapChainPanelDispatcher);
238 }
239
240 mSizeChangedEventToken.value = 0;
241 }
242
createSwapChain(ID3D11Device * device,IDXGIFactory2 * factory,DXGI_FORMAT format,unsigned int width,unsigned int height,bool containsAlpha,IDXGISwapChain1 ** swapChain)243 HRESULT SwapChainPanelNativeWindow::createSwapChain(ID3D11Device *device,
244 IDXGIFactory2 *factory,
245 DXGI_FORMAT format,
246 unsigned int width,
247 unsigned int height,
248 bool containsAlpha,
249 IDXGISwapChain1 **swapChain)
250 {
251 if (device == nullptr || factory == nullptr || swapChain == nullptr || width == 0 ||
252 height == 0)
253 {
254 return E_INVALIDARG;
255 }
256
257 DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 };
258 swapChainDesc.Width = width;
259 swapChainDesc.Height = height;
260 swapChainDesc.Format = format;
261 swapChainDesc.Stereo = FALSE;
262 swapChainDesc.SampleDesc.Count = 1;
263 swapChainDesc.SampleDesc.Quality = 0;
264 swapChainDesc.BufferUsage =
265 DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_BACK_BUFFER;
266 swapChainDesc.BufferCount = 2;
267 swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
268 swapChainDesc.Scaling = DXGI_SCALING_STRETCH;
269 swapChainDesc.AlphaMode =
270 containsAlpha ? DXGI_ALPHA_MODE_PREMULTIPLIED : DXGI_ALPHA_MODE_IGNORE;
271
272 *swapChain = nullptr;
273
274 ComPtr<IDXGISwapChain1> newSwapChain;
275 ComPtr<ISwapChainPanelNative> swapChainPanelNative;
276 Size currentPanelSize = {};
277
278 HRESULT result = factory->CreateSwapChainForComposition(device, &swapChainDesc, nullptr, newSwapChain.ReleaseAndGetAddressOf());
279
280 if (SUCCEEDED(result))
281 {
282 result = mSwapChainPanel.As(&swapChainPanelNative);
283 }
284
285 if (SUCCEEDED(result))
286 {
287 result = RunOnUIThread(
288 [swapChainPanelNative, newSwapChain]
289 {
290 return swapChainPanelNative->SetSwapChain(newSwapChain.Get());
291 },
292 mSwapChainPanelDispatcher);
293 }
294
295 if (SUCCEEDED(result))
296 {
297 // The swapchain panel host requires an instance of the swapchain set on the SwapChainPanel
298 // to perform the runtime-scale behavior. This swapchain is cached here because there are
299 // no methods for retreiving the currently configured on from ISwapChainPanelNative.
300 mSwapChain = newSwapChain;
301 result = newSwapChain.CopyTo(swapChain);
302 }
303
304 // If the host is responsible for scaling the output of the swapchain, then
305 // scale it now before returning an instance to the caller. This is done by
306 // first reading the current size of the swapchain panel, then scaling
307 if (SUCCEEDED(result))
308 {
309 if (mSwapChainSizeSpecified || mSwapChainScaleSpecified)
310 {
311 result = GetSwapChainPanelSize(mSwapChainPanel, mSwapChainPanelDispatcher,
312 ¤tPanelSize);
313
314 // Scale the swapchain to fit inside the contents of the panel.
315 if (SUCCEEDED(result))
316 {
317 result = scaleSwapChain(currentPanelSize, mClientRect);
318 }
319 }
320 }
321
322 return result;
323 }
324
scaleSwapChain(const Size & windowSize,const RECT & clientRect)325 HRESULT SwapChainPanelNativeWindow::scaleSwapChain(const Size &windowSize, const RECT &clientRect)
326 {
327 Size renderScale = {windowSize.Width / std::max(LONG(1), clientRect.right),
328 windowSize.Height / std::max(LONG(1), clientRect.bottom)};
329 // Setup a scale matrix for the swap chain
330 DXGI_MATRIX_3X2_F scaleMatrix = {};
331 scaleMatrix._11 = renderScale.Width;
332 scaleMatrix._22 = renderScale.Height;
333
334 ComPtr<IDXGISwapChain2> swapChain2;
335 HRESULT result = mSwapChain.As(&swapChain2);
336 if (SUCCEEDED(result))
337 {
338 result = swapChain2->SetMatrixTransform(&scaleMatrix);
339 }
340
341 return result;
342 }
343
GetSwapChainPanelSize(const ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> & swapChainPanel,const ComPtr<ICoreDispatcher> & dispatcher,Size * windowSize)344 HRESULT GetSwapChainPanelSize(
345 const ComPtr<ABI::Windows::UI::Xaml::Controls::ISwapChainPanel> &swapChainPanel,
346 const ComPtr<ICoreDispatcher> &dispatcher,
347 Size *windowSize)
348 {
349 ComPtr<IUIElement> uiElement;
350 HRESULT result = swapChainPanel.As(&uiElement);
351 if (SUCCEEDED(result))
352 {
353 result = RunOnUIThread(
354 [uiElement, windowSize] { return uiElement->get_RenderSize(windowSize); }, dispatcher);
355 }
356
357 return result;
358 }
359 }
360