• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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