// // Copyright 2018 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // EGLDirectCompositionTest.cpp: // Tests pertaining to DirectComposition and WindowsUIComposition. #ifdef ANGLE_ENABLE_D3D11_COMPOSITOR_NATIVE_WINDOW # include # include "test_utils/ANGLETest.h" # include # include # include # include # include # include # include # include # include "libANGLE/renderer/d3d/d3d11/converged/CompositorNativeWindow11.h" # include "util/OSWindow.h" # include "util/com_utils.h" # include "util/test_utils.h" using namespace angle; using namespace ABI::Windows::System; using namespace ABI::Windows::UI::Composition; using namespace ABI::Windows::UI::Composition::Desktop; using namespace ABI::Windows::Foundation; using namespace Microsoft::WRL; using namespace Microsoft::WRL::Wrappers; const int WINDOWWIDTH = 200, WINDOWHEIGHT = 200; class EGLDirectCompositionTest : public ANGLETest { protected: EGLDirectCompositionTest() : mOSWindow(nullptr) {} void testSetUp() override { if (!mRoHelper.SupportedWindowsRelease()) { return; } // Create an OS Window mOSWindow = OSWindow::New(); mOSWindow->initialize("EGLDirectCompositionTest", WINDOWWIDTH, WINDOWHEIGHT); auto nativeWindow = mOSWindow->getNativeWindow(); setWindowVisible(mOSWindow, true); // Create DispatcherQueue for window to process compositor callbacks CreateDispatcherQueue(mDispatcherController); HSTRING act; HSTRING_HEADER header; auto hr = mRoHelper.GetStringReference(RuntimeClass_Windows_UI_Composition_Compositor, &act, &header); ASSERT_TRUE(SUCCEEDED(hr)); void *fac = nullptr; hr = mRoHelper.GetActivationFactory(act, __uuidof(IActivationFactory), &fac); ASSERT_TRUE(SUCCEEDED(hr)); ComPtr compositorFactory; compositorFactory.Attach((IActivationFactory *)fac); hr = compositorFactory->ActivateInstance(&mCompositor); ASSERT_TRUE(SUCCEEDED(hr)); // Create a DesktopWindowTarget against native window (HWND) CreateDesktopWindowTarget(mCompositor, static_cast(nativeWindow), mDesktopTarget); ASSERT_TRUE(SUCCEEDED(mCompositor->CreateSpriteVisual(mAngleHost.GetAddressOf()))); ComPtr angleVis; ASSERT_TRUE(SUCCEEDED(mAngleHost.As(&angleVis))); ASSERT_TRUE(SUCCEEDED(angleVis->put_Size( {static_cast(WINDOWWIDTH), static_cast(WINDOWHEIGHT)}))); ASSERT_TRUE(SUCCEEDED(angleVis->put_Offset({0, 0, 0}))); ComPtr compTarget; ASSERT_TRUE(SUCCEEDED(mDesktopTarget.As(&compTarget))); ASSERT_TRUE(SUCCEEDED(compTarget->put_Root(angleVis.Get()))); Init(); } void CreateDispatcherQueue(ComPtr &controller) { DispatcherQueueOptions options{sizeof(DispatcherQueueOptions), DQTYPE_THREAD_CURRENT, DQTAT_COM_STA}; auto hr = mRoHelper.CreateDispatcherQueueController(options, controller.GetAddressOf()); ASSERT_TRUE(SUCCEEDED(hr)); } void CreateDesktopWindowTarget(ComPtr const &compositor, const HWND window, ComPtr &target) { namespace abi = ABI::Windows::UI::Composition::Desktop; ComPtr interop; ASSERT_TRUE(SUCCEEDED(compositor.As(&interop))); ASSERT_TRUE(SUCCEEDED(interop->CreateDesktopWindowTarget( window, true, reinterpret_cast(target.GetAddressOf())))); } void Init() { if (!mRoHelper.SupportedWindowsRelease()) { return; } DPI_AWARENESS_CONTEXT WINAPI SetThreadDpiAwarenessContext(_In_ DPI_AWARENESS_CONTEXT dpiContext); auto userModule = LoadLibraryA("user32.dll"); if (userModule == nullptr) { return; } auto temp = GetProcAddress(userModule, "SetThreadDpiAwarenessContext"); mFpSetThreadDpiAwarenessContext = reinterpret_cast<_SetThreadDpiAwarenessContext *>(temp); const EGLint configAttributes[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 8, EGL_STENCIL_SIZE, 8, EGL_NONE}; const EGLint defaultDisplayAttributes[] = { EGL_PLATFORM_ANGLE_TYPE_ANGLE, EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, EGL_NONE, }; PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT = reinterpret_cast( eglGetProcAddress("eglGetPlatformDisplayEXT")); ASSERT_TRUE(eglGetPlatformDisplayEXT != nullptr); mEglDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast(EGL_DEFAULT_DISPLAY), defaultDisplayAttributes); ASSERT_TRUE(mEglDisplay != EGL_NO_DISPLAY); ASSERT_EGL_TRUE(eglInitialize(mEglDisplay, nullptr, nullptr)); EGLint nConfigs = 0; ASSERT_EGL_TRUE(eglGetConfigs(mEglDisplay, nullptr, 0, &nConfigs)); ASSERT_TRUE(nConfigs != 0); ASSERT_EGL_TRUE(eglChooseConfig(mEglDisplay, configAttributes, &mEglConfig, 1, &nConfigs)); } void CreateSurface(ComPtr visual, EGLSurface &surface) { auto displayExtensions = eglQueryString(mEglDisplay, EGL_EXTENSIONS); // Check that the EGL_ANGLE_windows_ui_composition display extension is available ASSERT_TRUE(strstr(displayExtensions, "EGL_ANGLE_windows_ui_composition") != nullptr); const EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; // Use a spritevisual as the nativewindowtype surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, static_cast((void *)visual.Get()), nullptr); ASSERT_TRUE(surface != EGL_NO_SURFACE); mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, contextAttributes); ASSERT_TRUE(mEglContext != EGL_NO_CONTEXT); ASSERT_TRUE(eglMakeCurrent(mEglDisplay, surface, surface, mEglContext) != EGL_FALSE); } void testTearDown() override { if (!mRoHelper.SupportedWindowsRelease()) { return; } ASSERT_EGL_TRUE(eglTerminate(mEglDisplay)); OSWindow::Delete(&mOSWindow); } OSWindow *mOSWindow; ComPtr mCompositor; ComPtr mDispatcherController; ComPtr mColorBrush; ComPtr mDesktopTarget; ComPtr mAngleHost; EGLDisplay mEglDisplay; EGLContext mEglContext; EGLConfig mEglConfig; rx::RoHelper mRoHelper; using _SetThreadDpiAwarenessContext = DPI_AWARENESS_CONTEXT WINAPI(DPI_AWARENESS_CONTEXT dpiContext); _SetThreadDpiAwarenessContext *mFpSetThreadDpiAwarenessContext; }; // This tests that a surface created using a SpriteVisual as container has the expected dimensions // which should match the dimensions of the SpriteVisual passed in TEST_P(EGLDirectCompositionTest, SurfaceSizeFromSpriteSize) { // Only attempt this test when on Windows 10 1803+ ANGLE_SKIP_TEST_IF(!mRoHelper.SupportedWindowsRelease()); EGLSurface s{nullptr}; CreateSurface(mAngleHost, s); EGLint surfacewidth = 0, surfaceheight = 0; eglQuerySurface(mEglDisplay, s, EGL_WIDTH, &surfacewidth); eglQuerySurface(mEglDisplay, s, EGL_HEIGHT, &surfaceheight); ComPtr angleVis; ASSERT_TRUE(SUCCEEDED(mAngleHost.As(&angleVis))); ABI::Windows::Foundation::Numerics::Vector2 visualsize{0, 0}; ASSERT_TRUE(SUCCEEDED(angleVis->get_Size(&visualsize))); ASSERT_TRUE(surfacewidth == static_cast(visualsize.X)); ASSERT_TRUE(surfaceheight == static_cast(visualsize.Y)); ASSERT_EGL_TRUE(eglDestroySurface(mEglDisplay, s)); ASSERT_EGL_TRUE(eglDestroyContext(mEglDisplay, mEglContext)); } // This tests that a WindowSurface can be created using a SpriteVisual as the containing window // and that pixels can be successfully rendered into the resulting WindowSurface TEST_P(EGLDirectCompositionTest, RenderSolidColor) { // Only attempt this test when on Windows 10 1803+ ANGLE_SKIP_TEST_IF(!mRoHelper.SupportedWindowsRelease()); // http://crbug.com/1063962 ANGLE_SKIP_TEST_IF(isD3D11Renderer() && IsIntel()); EGLSurface s{nullptr}; CreateSurface(mAngleHost, s); glClearColor(0.0f, 0.0f, 1.0f, 1.0f); glViewport(0, 0, WINDOWWIDTH, WINDOWHEIGHT); glClear(GL_COLOR_BUFFER_BIT); ASSERT_EGL_TRUE(eglSwapBuffers(mEglDisplay, s)); // ensure user/DWM have a chance to paint the window and kick it to the top of the desktop // zorder before we attempt to sample angle::Sleep(200); mOSWindow->messageLoop(); uint8_t *pixelBuffer = static_cast(malloc(WINDOWWIDTH * WINDOWHEIGHT * 4)); ZeroMemory(pixelBuffer, WINDOWWIDTH * WINDOWHEIGHT * 4); // In order to accurately capture a bitmap, we need to temporarily shift into per-monitor DPI // mode in order to get the window offset from desktop correct auto previous = mFpSetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE); bool success = mOSWindow->takeScreenshot(pixelBuffer); mFpSetThreadDpiAwarenessContext(previous); ASSERT_EGL_TRUE(success); ASSERT_EGL_TRUE(pixelBuffer[(50 * 50 * 4)] == 255); ASSERT_EGL_TRUE(pixelBuffer[(50 * 50 * 4) + 1] == 0); ASSERT_EGL_TRUE(pixelBuffer[(50 * 50 * 4) + 2] == 0); ASSERT_EGL_TRUE(pixelBuffer[(50 * 50 * 4) + 3] == 255); ASSERT_EGL_TRUE(eglDestroySurface(mEglDisplay, s)); ASSERT_EGL_TRUE(eglDestroyContext(mEglDisplay, mEglContext)); } ANGLE_INSTANTIATE_TEST(EGLDirectCompositionTest, WithNoFixture(ES2_D3D11())); #endif // ANGLE_ENABLE_D3D11_COMPOSITOR_NATIVE_WINDOW