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