xref: /aosp_15_r20/external/angle/src/tests/egl_tests/EGLDirectCompositionTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2018 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 // EGLDirectCompositionTest.cpp:
8 //   Tests pertaining to DirectComposition and WindowsUIComposition.
9 
10 #ifdef ANGLE_ENABLE_D3D11_COMPOSITOR_NATIVE_WINDOW
11 
12 #    include <d3d11.h>
13 #    include "test_utils/ANGLETest.h"
14 
15 #    include <DispatcherQueue.h>
16 #    include <VersionHelpers.h>
17 #    include <Windows.Foundation.h>
18 #    include <windows.ui.composition.Desktop.h>
19 #    include <windows.ui.composition.h>
20 #    include <windows.ui.composition.interop.h>
21 #    include <wrl.h>
22 #    include <memory>
23 
24 #    include "libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.h"
25 #    include "util/OSWindow.h"
26 #    include "util/com_utils.h"
27 #    include "util/test_utils.h"
28 
29 using namespace angle;
30 using namespace ABI::Windows::System;
31 using namespace ABI::Windows::UI::Composition;
32 using namespace ABI::Windows::UI::Composition::Desktop;
33 using namespace ABI::Windows::Foundation;
34 using namespace Microsoft::WRL;
35 using namespace Microsoft::WRL::Wrappers;
36 
37 const int WINDOWWIDTH = 200, WINDOWHEIGHT = 200;
38 
39 class EGLDirectCompositionTest : public ANGLETest<>
40 {
41   protected:
EGLDirectCompositionTest()42     EGLDirectCompositionTest() : mOSWindow(nullptr) {}
43 
testSetUp()44     void testSetUp() override
45     {
46         if (!mRoHelper.SupportedWindowsRelease())
47         {
48             return;
49         }
50 
51         // Create an OS Window
52         mOSWindow = OSWindow::New();
53 
54         mOSWindow->initialize("EGLDirectCompositionTest", WINDOWWIDTH, WINDOWHEIGHT);
55         auto nativeWindow = mOSWindow->getNativeWindow();
56         setWindowVisible(mOSWindow, true);
57 
58         // Create DispatcherQueue for window to process compositor callbacks
59         CreateDispatcherQueue(mDispatcherController);
60 
61         HSTRING act;
62         HSTRING_HEADER header;
63 
64         auto hr = mRoHelper.GetStringReference(RuntimeClass_Windows_UI_Composition_Compositor, &act,
65                                                &header);
66 
67         ASSERT_TRUE(SUCCEEDED(hr));
68 
69         void *fac = nullptr;
70         hr        = mRoHelper.GetActivationFactory(act, __uuidof(IActivationFactory), &fac);
71         ASSERT_TRUE(SUCCEEDED(hr));
72 
73         ComPtr<IActivationFactory> compositorFactory;
74 
75         compositorFactory.Attach((IActivationFactory *)fac);
76 
77         hr = compositorFactory->ActivateInstance(&mCompositor);
78         ASSERT_TRUE(SUCCEEDED(hr));
79 
80         // Create a DesktopWindowTarget against native window (HWND)
81         CreateDesktopWindowTarget(mCompositor, static_cast<HWND>(nativeWindow), mDesktopTarget);
82 
83         ASSERT_TRUE(SUCCEEDED(mCompositor->CreateSpriteVisual(mAngleHost.GetAddressOf())));
84 
85         ComPtr<IVisual> angleVis;
86         ASSERT_TRUE(SUCCEEDED(mAngleHost.As(&angleVis)));
87 
88         ASSERT_TRUE(SUCCEEDED(angleVis->put_Size(
89             {static_cast<FLOAT>(WINDOWWIDTH), static_cast<FLOAT>(WINDOWHEIGHT)})));
90 
91         ASSERT_TRUE(SUCCEEDED(angleVis->put_Offset({0, 0, 0})));
92 
93         ComPtr<ICompositionTarget> compTarget;
94         ASSERT_TRUE(SUCCEEDED(mDesktopTarget.As(&compTarget)));
95         ASSERT_TRUE(SUCCEEDED(compTarget->put_Root(angleVis.Get())));
96 
97         Init();
98     }
99 
CreateDispatcherQueue(ComPtr<IDispatcherQueueController> & controller)100     void CreateDispatcherQueue(ComPtr<IDispatcherQueueController> &controller)
101     {
102         DispatcherQueueOptions options{sizeof(DispatcherQueueOptions), DQTYPE_THREAD_CURRENT,
103                                        DQTAT_COM_STA};
104 
105         auto hr = mRoHelper.CreateDispatcherQueueController(options, controller.GetAddressOf());
106 
107         ASSERT_TRUE(SUCCEEDED(hr));
108     }
109 
CreateDesktopWindowTarget(ComPtr<ICompositor> const & compositor,const HWND window,ComPtr<IDesktopWindowTarget> & target)110     void CreateDesktopWindowTarget(ComPtr<ICompositor> const &compositor,
111                                    const HWND window,
112                                    ComPtr<IDesktopWindowTarget> &target)
113     {
114         namespace abi = ABI::Windows::UI::Composition::Desktop;
115 
116         ComPtr<ICompositorDesktopInterop> interop;
117         ASSERT_TRUE(SUCCEEDED(compositor.As(&interop)));
118 
119         ASSERT_TRUE(SUCCEEDED(interop->CreateDesktopWindowTarget(
120             window, true, reinterpret_cast<abi::IDesktopWindowTarget **>(target.GetAddressOf()))));
121     }
122 
Init()123     void Init()
124     {
125         if (!mRoHelper.SupportedWindowsRelease())
126         {
127             return;
128         }
129 
130         DPI_AWARENESS_CONTEXT
131         WINAPI
132         SetThreadDpiAwarenessContext(_In_ DPI_AWARENESS_CONTEXT dpiContext);
133 
134         auto userModule = LoadLibraryA("user32.dll");
135 
136         if (userModule == nullptr)
137         {
138             return;
139         }
140 
141         auto temp = GetProcAddress(userModule, "SetThreadDpiAwarenessContext");
142 
143         mFpSetThreadDpiAwarenessContext = reinterpret_cast<_SetThreadDpiAwarenessContext *>(temp);
144 
145         const EGLint configAttributes[] = {
146             EGL_RED_SIZE,   8, EGL_GREEN_SIZE,   8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8,
147             EGL_DEPTH_SIZE, 8, EGL_STENCIL_SIZE, 8, EGL_NONE};
148 
149         const EGLint defaultDisplayAttributes[] = {
150             EGL_PLATFORM_ANGLE_TYPE_ANGLE,
151             EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
152             EGL_NONE,
153         };
154 
155         PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT =
156             reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
157                 eglGetProcAddress("eglGetPlatformDisplayEXT"));
158         ASSERT_TRUE(eglGetPlatformDisplayEXT != nullptr);
159 
160         mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
161                                                reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY),
162                                                defaultDisplayAttributes);
163         ASSERT_TRUE(mEglDisplay != EGL_NO_DISPLAY);
164 
165         ASSERT_EGL_TRUE(eglInitialize(mEglDisplay, nullptr, nullptr));
166 
167         EGLint nConfigs = 0;
168 
169         ASSERT_EGL_TRUE(eglGetConfigs(mEglDisplay, nullptr, 0, &nConfigs));
170         ASSERT_TRUE(nConfigs != 0);
171 
172         ASSERT_EGL_TRUE(eglChooseConfig(mEglDisplay, configAttributes, &mEglConfig, 1, &nConfigs));
173     }
174 
CreateSurface(ComPtr<ABI::Windows::UI::Composition::ISpriteVisual> visual,EGLSurface & surface)175     void CreateSurface(ComPtr<ABI::Windows::UI::Composition::ISpriteVisual> visual,
176                        EGLSurface &surface)
177     {
178         auto displayExtensions = eglQueryString(mEglDisplay, EGL_EXTENSIONS);
179 
180         // Check that the EGL_ANGLE_windows_ui_composition display extension is available
181         ASSERT_TRUE(strstr(displayExtensions, "EGL_ANGLE_windows_ui_composition") != nullptr);
182 
183         const EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
184 
185         // Use a spritevisual as the nativewindowtype
186         surface =
187             eglCreateWindowSurface(mEglDisplay, mEglConfig,
188                                    static_cast<EGLNativeWindowType>((void *)visual.Get()), nullptr);
189         ASSERT_TRUE(surface != EGL_NO_SURFACE);
190 
191         mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, contextAttributes);
192         ASSERT_TRUE(mEglContext != EGL_NO_CONTEXT);
193 
194         ASSERT_TRUE(eglMakeCurrent(mEglDisplay, surface, surface, mEglContext) != EGL_FALSE);
195     }
196 
testTearDown()197     void testTearDown() override
198     {
199         if (!mRoHelper.SupportedWindowsRelease())
200         {
201             return;
202         }
203         if (mEglDisplay != EGL_NO_DISPLAY)
204         {
205             ASSERT_EGL_TRUE(eglTerminate(mEglDisplay));
206             mEglDisplay = EGL_NO_DISPLAY;
207         }
208 
209         OSWindow::Delete(&mOSWindow);
210     }
211 
212     OSWindow *mOSWindow;
213     ComPtr<ICompositor> mCompositor;
214     ComPtr<IDispatcherQueueController> mDispatcherController;
215     ComPtr<ICompositionColorBrush> mColorBrush;
216     ComPtr<IDesktopWindowTarget> mDesktopTarget;
217     ComPtr<ISpriteVisual> mAngleHost;
218 
219     EGLDisplay mEglDisplay;
220     EGLContext mEglContext;
221     EGLConfig mEglConfig;
222     rx::RoHelper mRoHelper;
223 
224     using _SetThreadDpiAwarenessContext =
225         DPI_AWARENESS_CONTEXT WINAPI(DPI_AWARENESS_CONTEXT dpiContext);
226 
227     _SetThreadDpiAwarenessContext *mFpSetThreadDpiAwarenessContext;
228 };
229 
230 // This tests that a surface created using a SpriteVisual as container has the expected dimensions
231 // which should match the dimensions of the SpriteVisual passed in
TEST_P(EGLDirectCompositionTest,SurfaceSizeFromSpriteSize)232 TEST_P(EGLDirectCompositionTest, SurfaceSizeFromSpriteSize)
233 {
234     // Only attempt this test when on Windows 10 1803+
235     ANGLE_SKIP_TEST_IF(!mRoHelper.SupportedWindowsRelease());
236 
237     EGLSurface s{nullptr};
238     CreateSurface(mAngleHost, s);
239 
240     EGLint surfacewidth = 0, surfaceheight = 0;
241     eglQuerySurface(mEglDisplay, s, EGL_WIDTH, &surfacewidth);
242     eglQuerySurface(mEglDisplay, s, EGL_HEIGHT, &surfaceheight);
243 
244     ComPtr<IVisual> angleVis;
245     ASSERT_TRUE(SUCCEEDED(mAngleHost.As(&angleVis)));
246 
247     ABI::Windows::Foundation::Numerics::Vector2 visualsize{0, 0};
248 
249     ASSERT_TRUE(SUCCEEDED(angleVis->get_Size(&visualsize)));
250 
251     ASSERT_TRUE(surfacewidth == static_cast<int>(visualsize.X));
252     ASSERT_TRUE(surfaceheight == static_cast<int>(visualsize.Y));
253 
254     ASSERT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) !=
255                 EGL_FALSE);
256     ASSERT_EGL_TRUE(eglDestroySurface(mEglDisplay, s));
257     ASSERT_EGL_TRUE(eglDestroyContext(mEglDisplay, mEglContext));
258     mEglContext = EGL_NO_CONTEXT;
259 }
260 
261 // This tests that a WindowSurface can be created using a SpriteVisual as the containing window
262 // and that pixels can be successfully rendered into the resulting WindowSurface
TEST_P(EGLDirectCompositionTest,RenderSolidColor)263 TEST_P(EGLDirectCompositionTest, RenderSolidColor)
264 {
265     // Only attempt this test when on Windows 10 1803+
266     ANGLE_SKIP_TEST_IF(!mRoHelper.SupportedWindowsRelease());
267 
268     EGLSurface s{nullptr};
269     CreateSurface(mAngleHost, s);
270 
271     glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
272 
273     glViewport(0, 0, WINDOWWIDTH, WINDOWHEIGHT);
274     glClear(GL_COLOR_BUFFER_BIT);
275 
276     ASSERT_EGL_TRUE(eglSwapBuffers(mEglDisplay, s));
277 
278     // ensure user/DWM have a chance to paint the window and kick it to the top of the desktop
279     // zorder before we attempt to sample
280     angle::Sleep(200);
281     mOSWindow->messageLoop();
282 
283     uint8_t *pixelBuffer = static_cast<uint8_t *>(malloc(WINDOWWIDTH * WINDOWHEIGHT * 4));
284     ZeroMemory(pixelBuffer, WINDOWWIDTH * WINDOWHEIGHT * 4);
285 
286     // In order to accurately capture a bitmap, we need to temporarily shift into per-monitor DPI
287     // mode in order to get the window offset from desktop correct
288     auto previous = mFpSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE);
289     bool success  = mOSWindow->takeScreenshot(pixelBuffer);
290     mFpSetThreadDpiAwarenessContext(previous);
291     ASSERT_EGL_TRUE(success);
292 
293     ASSERT_EGL_TRUE(pixelBuffer[(50 * 50 * 4)] == 255);
294     ASSERT_EGL_TRUE(pixelBuffer[(50 * 50 * 4) + 1] == 0);
295     ASSERT_EGL_TRUE(pixelBuffer[(50 * 50 * 4) + 2] == 0);
296     ASSERT_EGL_TRUE(pixelBuffer[(50 * 50 * 4) + 3] == 255);
297 
298     ASSERT_TRUE(eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) !=
299                 EGL_FALSE);
300     ASSERT_EGL_TRUE(eglDestroySurface(mEglDisplay, s));
301     ASSERT_EGL_TRUE(eglDestroyContext(mEglDisplay, mEglContext));
302     mEglContext = EGL_NO_CONTEXT;
303 }
304 
305 ANGLE_INSTANTIATE_TEST(EGLDirectCompositionTest, WithNoFixture(ES2_D3D11()));
306 
307 #endif  // ANGLE_ENABLE_D3D11_COMPOSITOR_NATIVE_WINDOW
308