1 //
2 // Copyright (c) 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 // EGLRobustnessTest.cpp: tests for EGL_EXT_create_context_robustness
8 
9 #include <gtest/gtest.h>
10 
11 #include <EGL/egl.h>
12 #include <EGL/eglext.h>
13 
14 #include "OSWindow.h"
15 #include "test_utils/ANGLETest.h"
16 
17 using namespace angle;
18 
19 class EGLRobustnessTest : public ::testing::TestWithParam<angle::PlatformParameters>
20 {
21   public:
SetUp()22     void SetUp() override
23     {
24         mOSWindow = CreateOSWindow();
25         mOSWindow->initialize("EGLRobustnessTest", 500, 500);
26         mOSWindow->setVisible(true);
27 
28         auto eglGetPlatformDisplayEXT = reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(
29             eglGetProcAddress("eglGetPlatformDisplayEXT"));
30 
31         const auto &platform = GetParam().eglParameters;
32 
33         std::vector<EGLint> displayAttributes;
34         displayAttributes.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
35         displayAttributes.push_back(platform.renderer);
36         displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE);
37         displayAttributes.push_back(platform.majorVersion);
38         displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE);
39         displayAttributes.push_back(platform.minorVersion);
40 
41         if (platform.deviceType != EGL_DONT_CARE)
42         {
43             displayAttributes.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
44             displayAttributes.push_back(platform.deviceType);
45         }
46 
47         displayAttributes.push_back(EGL_NONE);
48 
49         mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
50                                             reinterpret_cast<void *>(mOSWindow->getNativeDisplay()),
51                                             &displayAttributes[0]);
52         ASSERT_NE(EGL_NO_DISPLAY, mDisplay);
53 
54         ASSERT_TRUE(eglInitialize(mDisplay, nullptr, nullptr) == EGL_TRUE);
55 
56         const char *extensions = eglQueryString(mDisplay, EGL_EXTENSIONS);
57         if (strstr(extensions, "EGL_EXT_create_context_robustness") == nullptr)
58         {
59             std::cout << "Test skipped due to missing EGL_EXT_create_context_robustness"
60                       << std::endl;
61             return;
62         }
63 
64         int nConfigs = 0;
65         ASSERT_TRUE(eglGetConfigs(mDisplay, nullptr, 0, &nConfigs) == EGL_TRUE);
66         ASSERT_LE(1, nConfigs);
67 
68         int nReturnedConfigs = 0;
69         ASSERT_TRUE(eglGetConfigs(mDisplay, &mConfig, 1, &nReturnedConfigs) == EGL_TRUE);
70         ASSERT_EQ(1, nReturnedConfigs);
71 
72         mWindow = eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(), nullptr);
73         ASSERT_EGL_SUCCESS();
74 
75         mInitialized = true;
76     }
77 
TearDown()78     void TearDown() override
79     {
80         eglDestroySurface(mDisplay, mWindow);
81         eglDestroyContext(mDisplay, mContext);
82         eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
83         eglTerminate(mDisplay);
84         EXPECT_EGL_SUCCESS();
85 
86         SafeDelete(mOSWindow);
87     }
88 
createContext(EGLint resetStrategy)89     void createContext(EGLint resetStrategy)
90     {
91         const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2,
92                                          EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT,
93                                          resetStrategy, EGL_NONE};
94         mContext = eglCreateContext(mDisplay, mConfig, EGL_NO_CONTEXT, contextAttribs);
95         ASSERT_NE(EGL_NO_CONTEXT, mContext);
96 
97         eglMakeCurrent(mDisplay, mWindow, mWindow, mContext);
98         ASSERT_EGL_SUCCESS();
99 
100         const char *extensionString = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS));
101         ASSERT_NE(nullptr, strstr(extensionString, "GL_ANGLE_instanced_arrays"));
102 
103         mDrawElementsInstancedANGLE =
104             (PFNGLDRAWELEMENTSINSTANCEDANGLEPROC)eglGetProcAddress("glDrawElementsInstancedANGLE");
105         ASSERT_NE(nullptr, mDrawElementsInstancedANGLE);
106     }
107 
forceContextReset()108     void forceContextReset()
109     {
110         // Cause a GPU reset by drawing 100,000,000 fullscreen quads
111         GLuint program = CompileProgram(
112             "attribute vec4 pos;\n"
113             "void main() {gl_Position = pos;}\n",
114             "precision mediump float;\n"
115             "void main() {gl_FragColor = vec4(1.0);}\n");
116         ASSERT_NE(0u, program);
117         glUseProgram(program);
118 
119         GLfloat vertices[] = {
120             -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
121             1.0f,  -1.0f, 0.0f, 1.0f, 1.0f,  1.0f, 0.0f, 1.0f,
122         };
123 
124         const int kNumQuads = 10000;
125         std::vector<GLushort> indices(6 * kNumQuads);
126 
127         for (size_t i = 0; i < kNumQuads; i++)
128         {
129             indices[i * 6 + 0] = 0;
130             indices[i * 6 + 1] = 1;
131             indices[i * 6 + 2] = 2;
132             indices[i * 6 + 3] = 1;
133             indices[i * 6 + 4] = 2;
134             indices[i * 6 + 5] = 3;
135         }
136 
137         glBindAttribLocation(program, 0, "pos");
138         glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, vertices);
139         glEnableVertexAttribArray(0);
140 
141         glViewport(0, 0, mOSWindow->getWidth(), mOSWindow->getHeight());
142         glClearColor(1.0, 0.0, 0.0, 1.0);
143         glClear(GL_COLOR_BUFFER_BIT);
144         mDrawElementsInstancedANGLE(GL_TRIANGLES, kNumQuads * 6, GL_UNSIGNED_SHORT, indices.data(),
145                                     10000);
146 
147         glFinish();
148     }
149 
150   protected:
151     EGLDisplay mDisplay                                             = EGL_NO_DISPLAY;
152     EGLSurface mWindow                                              = EGL_NO_SURFACE;
153     bool mInitialized                                               = false;
154     PFNGLDRAWELEMENTSINSTANCEDANGLEPROC mDrawElementsInstancedANGLE = nullptr;
155 
156   private:
157     EGLContext mContext = EGL_NO_CONTEXT;
158     EGLConfig mConfig   = 0;
159     OSWindow *mOSWindow = nullptr;
160 };
161 
162 // Check glGetGraphicsResetStatusEXT returns GL_NO_ERROR if we did nothing
TEST_P(EGLRobustnessTest,NoErrorByDefault)163 TEST_P(EGLRobustnessTest, NoErrorByDefault)
164 {
165     if (!mInitialized)
166     {
167         return;
168     }
169     ASSERT_TRUE(glGetGraphicsResetStatusEXT() == GL_NO_ERROR);
170 }
171 
172 // Checks that the application gets no loss with NO_RESET_NOTIFICATION
TEST_P(EGLRobustnessTest,DISABLED_NoResetNotification)173 TEST_P(EGLRobustnessTest, DISABLED_NoResetNotification)
174 {
175     if (!mInitialized)
176     {
177         return;
178     }
179 
180     createContext(EGL_NO_RESET_NOTIFICATION_EXT);
181 
182     if (!IsWindows())
183     {
184         std::cout << "Test disabled on non Windows platforms because drivers can't recover. "
185                   << "See " << __FILE__ << ":" << __LINE__ << std::endl;
186         return;
187     }
188     std::cout << "Causing a GPU reset, brace for impact." << std::endl;
189 
190     forceContextReset();
191     ASSERT_TRUE(glGetGraphicsResetStatusEXT() == GL_NO_ERROR);
192 }
193 
194 // Checks that resetting the ANGLE display allows to get rid of the context loss.
195 // Also checks that the application gets notified of the loss of the display.
196 // We coalesce both tests to reduce the number of TDRs done on Windows: by default
197 // having more than 5 TDRs in a minute will cause Windows to disable the GPU until
198 // the computer is rebooted.
TEST_P(EGLRobustnessTest,DISABLED_ResettingDisplayWorks)199 TEST_P(EGLRobustnessTest, DISABLED_ResettingDisplayWorks)
200 {
201     if (!mInitialized)
202     {
203         return;
204     }
205 
206     createContext(EGL_LOSE_CONTEXT_ON_RESET_EXT);
207 
208     if (!IsWindows())
209     {
210         std::cout << "Test disabled on non Windows platforms because drivers can't recover. "
211                   << "See " << __FILE__ << ":" << __LINE__ << std::endl;
212         return;
213     }
214     std::cout << "Causing a GPU reset, brace for impact." << std::endl;
215 
216     forceContextReset();
217     ASSERT_TRUE(glGetGraphicsResetStatusEXT() != GL_NO_ERROR);
218 
219     TearDown();
220     SetUp();
221     ASSERT_TRUE(glGetGraphicsResetStatusEXT() == GL_NO_ERROR);
222 }
223 
224 // Tests causing GPU resets are disabled, use the following args to run them:
225 // --gtest_also_run_disabled_tests --gtest_filter=EGLRobustnessTest\*
226 
227 // Note that on Windows the OpenGL driver fails hard (popup that closes the application)
228 // if there was a TDR caused by D3D so we don't run D3D tests at the same time as the OpenGL tests.
229 #define D3D_HAS_PRIORITY 1
230 #if D3D_HAS_PRIORITY && (defined(ANGLE_ENABLE_D3D9) || defined(ANGLE_ENABLE_D3D11))
231 ANGLE_INSTANTIATE_TEST(EGLRobustnessTest, ES2_D3D9(), ES2_D3D11());
232 #else
233 ANGLE_INSTANTIATE_TEST(EGLRobustnessTest, ES2_OPENGL());
234 #endif
235