1 //
2 // Copyright 2016 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 // EGLSyncControlTest.cpp:
8 // Tests pertaining to eglGetSyncValuesCHROMIUM.
9
10 #include <d3d11.h>
11
12 #include "test_utils/ANGLETest.h"
13 #include "util/OSWindow.h"
14 #include "util/com_utils.h"
15
16 using namespace angle;
17
18 class EGLSyncControlTest : public testing::Test
19 {
20 protected:
EGLSyncControlTest()21 EGLSyncControlTest() {}
22
SetUp()23 void SetUp() override
24 {
25 mD3D11Module = LoadLibrary(TEXT("d3d11.dll"));
26 if (mD3D11Module == nullptr)
27 {
28 std::cout << "Unable to LoadLibrary D3D11" << std::endl;
29 return;
30 }
31
32 mD3D11CreateDevice = reinterpret_cast<PFN_D3D11_CREATE_DEVICE>(
33 GetProcAddress(mD3D11Module, "D3D11CreateDevice"));
34 if (mD3D11CreateDevice == nullptr)
35 {
36 std::cout << "Could not retrieve D3D11CreateDevice from d3d11.dll" << std::endl;
37 return;
38 }
39
40 mD3D11Available = true;
41
42 const char *extensionString =
43 static_cast<const char *>(eglQueryString(EGL_NO_DISPLAY, EGL_EXTENSIONS));
44 if (strstr(extensionString, "EGL_ANGLE_device_creation"))
45 {
46 if (strstr(extensionString, "EGL_ANGLE_device_creation_d3d11"))
47 {
48 mDeviceCreationD3D11ExtAvailable = true;
49 }
50 }
51 }
52
TearDown()53 void TearDown() override
54 {
55 SafeRelease(mDevice);
56 SafeRelease(mDeviceContext);
57
58 OSWindow::Delete(&mOSWindow);
59
60 if (mSurface != EGL_NO_SURFACE)
61 {
62 eglDestroySurface(mDisplay, mSurface);
63 mSurface = EGL_NO_SURFACE;
64 }
65
66 if (mContext != EGL_NO_CONTEXT)
67 {
68 eglDestroyContext(mDisplay, mContext);
69 mContext = EGL_NO_CONTEXT;
70 }
71
72 if (mDisplay != EGL_NO_DISPLAY)
73 {
74 eglTerminate(mDisplay);
75 mDisplay = EGL_NO_DISPLAY;
76 }
77 }
78
CreateD3D11Device()79 void CreateD3D11Device()
80 {
81 ASSERT_TRUE(mD3D11Available);
82 ASSERT_EQ(nullptr, mDevice); // The device shouldn't be created twice
83
84 HRESULT hr =
85 mD3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, 0, 0, nullptr, 0,
86 D3D11_SDK_VERSION, &mDevice, &mFeatureLevel, &mDeviceContext);
87
88 ASSERT_TRUE(SUCCEEDED(hr));
89 }
90
InitializeDisplay()91 void InitializeDisplay()
92 {
93 EGLint displayAttribs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE,
94 EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
95 EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE,
96 EGL_DONT_CARE,
97 EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE,
98 EGL_DONT_CARE,
99 EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
100 EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE,
101 EGL_NONE};
102
103 // Create an OS Window
104 mOSWindow = OSWindow::New();
105 mOSWindow->initialize("EGLSyncControlTest", 64, 64);
106 mOSWindow->setVisible(true);
107
108 // Create an EGLDisplay using the EGLDevice
109 mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
110 reinterpret_cast<void *>(mOSWindow->getNativeDisplay()),
111 displayAttribs);
112 ASSERT_TRUE(mDisplay != EGL_NO_DISPLAY);
113
114 EGLint majorVersion, minorVersion;
115 ASSERT_TRUE(eglInitialize(mDisplay, &majorVersion, &minorVersion) == EGL_TRUE);
116 }
117
CreateWindowSurface()118 void CreateWindowSurface()
119 {
120 eglBindAPI(EGL_OPENGL_ES_API);
121 ASSERT_EGL_SUCCESS();
122
123 // Choose a config
124 const EGLint configAttributes[] = {EGL_NONE};
125
126 EGLint configCount = 0;
127 ASSERT_EGL_TRUE(eglChooseConfig(mDisplay, configAttributes, &mConfig, 1, &configCount));
128
129 const EGLint surfaceAttributes[] = {EGL_DIRECT_COMPOSITION_ANGLE, EGL_TRUE, EGL_NONE};
130
131 // Create window surface
132 mSurface = eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(),
133 surfaceAttributes);
134 if (mSurface == nullptr)
135 {
136 std::cout << "Unable to create window surface with Direct Composition" << std::endl;
137 return;
138 }
139
140 mDirectCompositionSurfaceAvailable = true;
141
142 // Create EGL context
143 EGLint contextAttibutes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
144
145 mContext = eglCreateContext(mDisplay, mConfig, nullptr, contextAttibutes);
146 ASSERT_EGL_SUCCESS();
147
148 // Make the surface current
149 eglMakeCurrent(mDisplay, mSurface, mSurface, mContext);
150 ASSERT_EGL_SUCCESS();
151 }
152
153 bool mD3D11Available = false;
154 HMODULE mD3D11Module = nullptr;
155 PFN_D3D11_CREATE_DEVICE mD3D11CreateDevice = nullptr;
156
157 ID3D11Device *mDevice = nullptr;
158 ID3D11DeviceContext *mDeviceContext = nullptr;
159 D3D_FEATURE_LEVEL mFeatureLevel;
160
161 bool mDeviceCreationD3D11ExtAvailable = false;
162
163 bool mDirectCompositionSurfaceAvailable = false;
164
165 OSWindow *mOSWindow = nullptr;
166
167 EGLDisplay mDisplay = EGL_NO_DISPLAY;
168 EGLSurface mSurface = EGL_NO_SURFACE;
169 EGLContext mContext = EGL_NO_CONTEXT;
170 EGLConfig mConfig = 0;
171 };
172
173 // Basic test for eglGetSyncValuesCHROMIUM extension. Verifies that eglGetSyncValuesCHROMIUM
174 // can be called on DX11 with direct composition and that it returns reasonable enough values.
TEST_F(EGLSyncControlTest,DISABLED_SyncValuesTest)175 TEST_F(EGLSyncControlTest, DISABLED_SyncValuesTest)
176 {
177 static const DWORD kPollInterval = 10;
178 static const int kNumPollIterations = 100;
179
180 if (!mD3D11Available)
181 {
182 std::cout << "D3D11 not available, skipping test" << std::endl;
183 return;
184 }
185
186 CreateD3D11Device();
187 InitializeDisplay();
188 CreateWindowSurface();
189
190 if (!mDirectCompositionSurfaceAvailable)
191 {
192 std::cout << "Direct Composition surface not available, skipping test" << std::endl;
193 return;
194 }
195
196 const char *extensionString =
197 static_cast<const char *>(eglQueryString(mDisplay, EGL_EXTENSIONS));
198 ASSERT_TRUE(strstr(extensionString, "EGL_CHROMIUM_sync_control"));
199
200 EGLuint64KHR ust = 0, msc = 0, sbc = 0;
201 // It appears there is a race condition so the very first call to eglGetSyncValuesCHROMIUM
202 // can fail within D3D with DXGI_ERROR_FRAME_STATISTICS_DISJOINT.
203 // Should that be handled inside eglGetSyncValuesCHROMIUM?
204 eglGetSyncValuesCHROMIUM(mDisplay, mSurface, &ust, &msc, &sbc);
205
206 ASSERT_EGL_TRUE(eglGetSyncValuesCHROMIUM(mDisplay, mSurface, &ust, &msc, &sbc));
207 // Initial msc and sbc value should be true. Initial ust value is unspecified.
208 ASSERT_EQ(0ull, msc);
209 ASSERT_EQ(0ull, sbc);
210
211 // Perform some very basic rendering.
212 glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
213 glClear(GL_COLOR_BUFFER_BIT);
214 ASSERT_GL_NO_ERROR();
215
216 ASSERT_EGL_TRUE(eglSwapBuffers(mDisplay, mSurface));
217
218 // Poll until sbc value increases. Normally it should change within 16-17 ms.
219 for (int i = 0; i < kNumPollIterations; i++)
220 {
221 ::Sleep(kPollInterval);
222 ASSERT_EGL_TRUE(eglGetSyncValuesCHROMIUM(mDisplay, mSurface, &ust, &msc, &sbc));
223 if (sbc > 0)
224 break;
225 }
226
227 // sbc should change to 1. msc and ust to some non-zero values.
228 ASSERT_EQ(1ull, sbc);
229 ASSERT_GT(ust, 0ull);
230 ASSERT_GT(msc, 0ull);
231
232 // Perform more rendering.
233 glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
234 glClear(GL_COLOR_BUFFER_BIT);
235 ASSERT_GL_NO_ERROR();
236
237 ASSERT_EGL_TRUE(eglSwapBuffers(mDisplay, mSurface));
238
239 // Poll until sbc value increases. Normally it should change within 16-17 ms.
240 EGLuint64KHR ust2 = 0, msc2 = 0, sbc2 = 0;
241 for (int i = 0; i < kNumPollIterations; i++)
242 {
243 ::Sleep(kPollInterval);
244 ASSERT_EGL_TRUE(eglGetSyncValuesCHROMIUM(mDisplay, mSurface, &ust2, &msc2, &sbc2));
245 if (sbc2 > sbc)
246 break;
247 }
248
249 // sbc2 should be 2. msc2 and ust2 should be greater than previous msc and ust values.
250 ASSERT_EQ(2ull, sbc2);
251 ASSERT_GT(ust2, ust);
252 ASSERT_GT(msc2, msc);
253 }
254