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