| // | |
| // Copyright 2016 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. | |
| // | |
| // EGLSyncControlTest.cpp: | |
| // Tests pertaining to eglGetSyncValuesCHROMIUM. | |
| #include <d3d11.h> | |
| #include "test_utils/ANGLETest.h" | |
| #include "util/OSWindow.h" | |
| #include "util/com_utils.h" | |
| using namespace angle; | |
| class EGLSyncControlTest : public testing::Test | |
| { | |
| protected: | |
| EGLSyncControlTest() {} | |
| void SetUp() override | |
| { | |
| mD3D11Module = LoadLibrary(TEXT("d3d11.dll")); | |
| if (mD3D11Module == nullptr) | |
| { | |
| std::cout << "Unable to LoadLibrary D3D11" << std::endl; | |
| return; | |
| } | |
| mD3D11CreateDevice = reinterpret_cast<PFN_D3D11_CREATE_DEVICE>( | |
| GetProcAddress(mD3D11Module, "D3D11CreateDevice")); | |
| if (mD3D11CreateDevice == nullptr) | |
| { | |
| std::cout << "Could not retrieve D3D11CreateDevice from d3d11.dll" << std::endl; | |
| return; | |
| } | |
| mD3D11Available = true; | |
| const char *extensionString = | |
| static_cast<const char *>(eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS)); | |
| if (strstr(extensionString, "EGL_ANGLE_device_creation")) | |
| { | |
| if (strstr(extensionString, "EGL_ANGLE_device_creation_d3d11")) | |
| { | |
| mDeviceCreationD3D11ExtAvailable = true; | |
| } | |
| } | |
| } | |
| void TearDown() override | |
| { | |
| SafeRelease(mDevice); | |
| SafeRelease(mDeviceContext); | |
| OSWindow::Delete(&mOSWindow); | |
| if (mSurface != EGL_NO_SURFACE) | |
| { | |
| eglDestroySurface(mDisplay, mSurface); | |
| mSurface = EGL_NO_SURFACE; | |
| } | |
| if (mContext != EGL_NO_CONTEXT) | |
| { | |
| eglDestroyContext(mDisplay, mContext); | |
| mContext = EGL_NO_CONTEXT; | |
| } | |
| if (mDisplay != EGL_NO_DISPLAY) | |
| { | |
| eglTerminate(mDisplay); | |
| mDisplay = EGL_NO_DISPLAY; | |
| } | |
| } | |
| void CreateD3D11Device() | |
| { | |
| ASSERT_TRUE(mD3D11Available); | |
| ASSERT_EQ(nullptr, mDevice); // The device shouldn't be created twice | |
| HRESULT hr = | |
| mD3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, 0, nullptr, 0, | |
| D3D11_SDK_VERSION, &mDevice, &mFeatureLevel, &mDeviceContext); | |
| ASSERT_TRUE(SUCCEEDED(hr)); | |
| } | |
| void InitializeDisplay() | |
| { | |
| EGLint displayAttribs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, | |
| EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE, | |
| EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, | |
| EGL_DONT_CARE, | |
| EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, | |
| EGL_DONT_CARE, | |
| EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, | |
| EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE, | |
| EGL_NONE}; | |
| // Create an OS Window | |
| mOSWindow = OSWindow::New(); | |
| mOSWindow->initialize("EGLSyncControlTest", 64, 64); | |
| mOSWindow->setVisible(true); | |
| // Create an EGLDisplay using the EGLDevice | |
| mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE, | |
| reinterpret_cast<void *>(mOSWindow->getNativeDisplay()), | |
| displayAttribs); | |
| ASSERT_TRUE(mDisplay != EGL_NO_DISPLAY); | |
| EGLint majorVersion, minorVersion; | |
| ASSERT_TRUE(eglInitialize(mDisplay, &majorVersion, &minorVersion) == EGL_TRUE); | |
| } | |
| void CreateWindowSurface() | |
| { | |
| eglBindAPI(EGL_OPENGL_ES_API); | |
| ASSERT_EGL_SUCCESS(); | |
| // Choose a config | |
| const EGLint configAttributes[] = {EGL_NONE}; | |
| EGLint configCount = 0; | |
| ASSERT_EGL_TRUE(eglChooseConfig(mDisplay, configAttributes, &mConfig, 1, &configCount)); | |
| const EGLint surfaceAttributes[] = {EGL_DIRECT_COMPOSITION_ANGLE, EGL_TRUE, EGL_NONE}; | |
| // Create window surface | |
| mSurface = eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(), | |
| surfaceAttributes); | |
| if (mSurface == nullptr) | |
| { | |
| std::cout << "Unable to create window surface with Direct Composition" << std::endl; | |
| return; | |
| } | |
| mDirectCompositionSurfaceAvailable = true; | |
| // Create EGL context | |
| EGLint contextAttibutes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; | |
| mContext = eglCreateContext(mDisplay, mConfig, nullptr, contextAttibutes); | |
| ASSERT_EGL_SUCCESS(); | |
| // Make the surface current | |
| eglMakeCurrent(mDisplay, mSurface, mSurface, mContext); | |
| ASSERT_EGL_SUCCESS(); | |
| } | |
| bool mD3D11Available = false; | |
| HMODULE mD3D11Module = nullptr; | |
| PFN_D3D11_CREATE_DEVICE mD3D11CreateDevice = nullptr; | |
| ID3D11Device *mDevice = nullptr; | |
| ID3D11DeviceContext *mDeviceContext = nullptr; | |
| D3D_FEATURE_LEVEL mFeatureLevel; | |
| bool mDeviceCreationD3D11ExtAvailable = false; | |
| bool mDirectCompositionSurfaceAvailable = false; | |
| OSWindow *mOSWindow = nullptr; | |
| EGLDisplay mDisplay = EGL_NO_DISPLAY; | |
| EGLSurface mSurface = EGL_NO_SURFACE; | |
| EGLContext mContext = EGL_NO_CONTEXT; | |
| EGLConfig mConfig = 0; | |
| }; | |
| // Basic test for eglGetSyncValuesCHROMIUM extension. Verifies that eglGetSyncValuesCHROMIUM | |
| // can be called on DX11 with direct composition and that it returns reasonable enough values. | |
| TEST_F(EGLSyncControlTest, DISABLED_SyncValuesTest) | |
| { | |
| static const DWORD kPollInterval = 10; | |
| static const int kNumPollIterations = 100; | |
| if (!mD3D11Available) | |
| { | |
| std::cout << "D3D11 not available, skipping test" << std::endl; | |
| return; | |
| } | |
| CreateD3D11Device(); | |
| InitializeDisplay(); | |
| CreateWindowSurface(); | |
| if (!mDirectCompositionSurfaceAvailable) | |
| { | |
| std::cout << "Direct Composition surface not available, skipping test" << std::endl; | |
| return; | |
| } | |
| const char *extensionString = | |
| static_cast<const char *>(eglQueryString(mDisplay, EGL_EXTENSIONS)); | |
| ASSERT_TRUE(strstr(extensionString, "EGL_CHROMIUM_sync_control")); | |
| EGLuint64KHR ust = 0, msc = 0, sbc = 0; | |
| // It appears there is a race condition so the very first call to eglGetSyncValuesCHROMIUM | |
| // can fail within D3D with DXGI_ERROR_FRAME_STATISTICS_DISJOINT. | |
| // Should that be handled inside eglGetSyncValuesCHROMIUM? | |
| eglGetSyncValuesCHROMIUM(mDisplay, mSurface, &ust, &msc, &sbc); | |
| ASSERT_EGL_TRUE(eglGetSyncValuesCHROMIUM(mDisplay, mSurface, &ust, &msc, &sbc)); | |
| // Initial msc and sbc value should be true. Initial ust value is unspecified. | |
| ASSERT_EQ(0ull, msc); | |
| ASSERT_EQ(0ull, sbc); | |
| // Perform some very basic rendering. | |
| glClearColor(1.0f, 0.0f, 1.0f, 1.0f); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| ASSERT_GL_NO_ERROR(); | |
| ASSERT_EGL_TRUE(eglSwapBuffers(mDisplay, mSurface)); | |
| // Poll until sbc value increases. Normally it should change within 16-17 ms. | |
| for (int i = 0; i < kNumPollIterations; i++) | |
| { | |
| ::Sleep(kPollInterval); | |
| ASSERT_EGL_TRUE(eglGetSyncValuesCHROMIUM(mDisplay, mSurface, &ust, &msc, &sbc)); | |
| if (sbc > 0) | |
| break; | |
| } | |
| // sbc should change to 1. msc and ust to some non-zero values. | |
| ASSERT_EQ(1ull, sbc); | |
| ASSERT_GT(ust, 0ull); | |
| ASSERT_GT(msc, 0ull); | |
| // Perform more rendering. | |
| glClearColor(0.0f, 0.0f, 1.0f, 1.0f); | |
| glClear(GL_COLOR_BUFFER_BIT); | |
| ASSERT_GL_NO_ERROR(); | |
| ASSERT_EGL_TRUE(eglSwapBuffers(mDisplay, mSurface)); | |
| // Poll until sbc value increases. Normally it should change within 16-17 ms. | |
| EGLuint64KHR ust2 = 0, msc2 = 0, sbc2 = 0; | |
| for (int i = 0; i < kNumPollIterations; i++) | |
| { | |
| ::Sleep(kPollInterval); | |
| ASSERT_EGL_TRUE(eglGetSyncValuesCHROMIUM(mDisplay, mSurface, &ust2, &msc2, &sbc2)); | |
| if (sbc2 > sbc) | |
| break; | |
| } | |
| // sbc2 should be 2. msc2 and ust2 should be greater than previous msc and ust values. | |
| ASSERT_EQ(2ull, sbc2); | |
| ASSERT_GT(ust2, ust); | |
| ASSERT_GT(msc2, msc); | |
| } |