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