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