xref: /aosp_15_r20/external/angle/src/tests/egl_tests/EGLRobustnessTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 // EGLRobustnessTest.cpp: tests for EGL_EXT_create_context_robustness
8 //
9 // Tests causing GPU resets are disabled, use the following args to run them:
10 // --gtest_also_run_disabled_tests --gtest_filter=EGLRobustnessTest\*
11 
12 #include <gtest/gtest.h>
13 
14 #include "common/debug.h"
15 #include "test_utils/ANGLETest.h"
16 #include "test_utils/gl_raii.h"
17 #include "util/OSWindow.h"
18 
19 using namespace angle;
20 
21 class EGLRobustnessTest : public ANGLETest<>
22 {
23   public:
24     enum class eglContextOpenglRobustAccess : bool
25     {
26         enable  = true,
27         disable = false,
28     };
29 
testSetUp()30     void testSetUp() override
31     {
32         mOSWindow = OSWindow::New();
33         mOSWindow->initialize("EGLRobustnessTest", 500, 500);
34         setWindowVisible(mOSWindow, true);
35 
36         const auto &platform = GetParam().eglParameters;
37 
38         std::vector<EGLint> displayAttributes;
39         displayAttributes.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
40         displayAttributes.push_back(platform.renderer);
41         displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE);
42         displayAttributes.push_back(platform.majorVersion);
43         displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE);
44         displayAttributes.push_back(platform.minorVersion);
45 
46         if (platform.deviceType != EGL_DONT_CARE)
47         {
48             displayAttributes.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
49             displayAttributes.push_back(platform.deviceType);
50         }
51 
52         displayAttributes.push_back(EGL_NONE);
53 
54         mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
55                                             reinterpret_cast<void *>(mOSWindow->getNativeDisplay()),
56                                             &displayAttributes[0]);
57         ASSERT_NE(EGL_NO_DISPLAY, mDisplay);
58 
59         ASSERT_TRUE(eglInitialize(mDisplay, nullptr, nullptr) == EGL_TRUE);
60 
61         int nConfigs = 0;
62         ASSERT_TRUE(eglGetConfigs(mDisplay, nullptr, 0, &nConfigs) == EGL_TRUE);
63         ASSERT_LE(1, nConfigs);
64 
65         std::vector<EGLConfig> allConfigs(nConfigs);
66         int nReturnedConfigs = 0;
67         ASSERT_TRUE(eglGetConfigs(mDisplay, allConfigs.data(), nConfigs, &nReturnedConfigs) ==
68                     EGL_TRUE);
69         ASSERT_EQ(nConfigs, nReturnedConfigs);
70 
71         for (const EGLConfig &config : allConfigs)
72         {
73             EGLint surfaceType;
74             eglGetConfigAttrib(mDisplay, config, EGL_SURFACE_TYPE, &surfaceType);
75 
76             if ((surfaceType & EGL_WINDOW_BIT) != 0)
77             {
78                 mConfig      = config;
79                 mInitialized = true;
80                 break;
81             }
82         }
83 
84         if (mInitialized)
85         {
86             mWindow =
87                 eglCreateWindowSurface(mDisplay, mConfig, mOSWindow->getNativeWindow(), nullptr);
88             ASSERT_EGL_SUCCESS();
89         }
90     }
91 
testTearDown()92     void testTearDown() override
93     {
94         eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
95         eglDestroySurface(mDisplay, mWindow);
96         destroyContext();
97         eglTerminate(mDisplay);
98         EXPECT_EGL_SUCCESS();
99 
100         OSWindow::Delete(&mOSWindow);
101     }
102 
createContext(EGLint resetStrategy)103     void createContext(EGLint resetStrategy)
104     {
105         std::vector<EGLint> contextAttribs = {
106             EGL_CONTEXT_CLIENT_VERSION,
107             2,
108         };
109 
110         if (IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"))
111         {
112             contextAttribs.push_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT);
113             contextAttribs.push_back(resetStrategy);
114         }
115         else
116         {
117             ASSERT_EQ(EGL_NO_RESET_NOTIFICATION_EXT, resetStrategy);
118         }
119         contextAttribs.push_back(EGL_NONE);
120         mContext = eglCreateContext(mDisplay, mConfig, EGL_NO_CONTEXT, contextAttribs.data());
121         ASSERT_NE(EGL_NO_CONTEXT, mContext);
122 
123         eglMakeCurrent(mDisplay, mWindow, mWindow, mContext);
124         ASSERT_EGL_SUCCESS();
125 
126         const char *extensionString = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS));
127         ASSERT_NE(nullptr, strstr(extensionString, "GL_ANGLE_instanced_arrays"));
128     }
129 
createClientVersion3NonRobustContext(EGLint resetStrategy)130     void createClientVersion3NonRobustContext(EGLint resetStrategy)
131     {
132         std::vector<EGLint> contextAttribs = {
133             EGL_CONTEXT_CLIENT_VERSION,           3,         EGL_CONTEXT_MINOR_VERSION_KHR, 0,
134             EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT, EGL_FALSE,
135         };
136         if (IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"))
137         {
138             contextAttribs.push_back(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT);
139             contextAttribs.push_back(resetStrategy);
140         }
141         else
142         {
143             ASSERT_EQ(EGL_NO_RESET_NOTIFICATION_EXT, resetStrategy);
144         }
145         contextAttribs.push_back(EGL_NONE);
146         mContext = eglCreateContext(mDisplay, mConfig, EGL_NO_CONTEXT, contextAttribs.data());
147         ASSERT_NE(EGL_NO_CONTEXT, mContext);
148 
149         eglMakeCurrent(mDisplay, mWindow, mWindow, mContext);
150         ASSERT_EGL_SUCCESS();
151 
152         const char *extensionString = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS));
153         ASSERT_NE(nullptr, strstr(extensionString, "GL_ANGLE_instanced_arrays"));
154     }
155 
createRobustContext(EGLint resetStrategy,EGLContext shareContext)156     void createRobustContext(EGLint resetStrategy, EGLContext shareContext)
157     {
158         const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION,
159                                          3,
160                                          EGL_CONTEXT_MINOR_VERSION_KHR,
161                                          0,
162                                          EGL_CONTEXT_OPENGL_ROBUST_ACCESS,
163                                          EGL_TRUE,
164                                          EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT,
165                                          resetStrategy,
166                                          EGL_NONE};
167 
168         mContext = eglCreateContext(mDisplay, mConfig, shareContext, contextAttribs);
169         ASSERT_NE(EGL_NO_CONTEXT, mContext);
170 
171         eglMakeCurrent(mDisplay, mWindow, mWindow, mContext);
172         ASSERT_EGL_SUCCESS();
173     }
174 
destroyContext()175     void destroyContext()
176     {
177         if (mContext != EGL_NO_CONTEXT)
178         {
179             eglDestroyContext(mDisplay, mContext);
180             mContext = EGL_NO_CONTEXT;
181         }
182     }
183 
submitLongRunningTask()184     void submitLongRunningTask()
185     {
186         // Cause a GPU reset by drawing 100,000,000 fullscreen quads
187         GLuint program = CompileProgram(
188             "attribute vec4 pos;\n"
189             "varying vec2 texcoord;\n"
190             "void main() {gl_Position = pos; texcoord = (pos.xy * 0.5) + 0.5;}\n",
191             "precision mediump float;\n"
192             "uniform sampler2D tex;\n"
193             "varying vec2 texcoord;\n"
194             "void main() {gl_FragColor = gl_FragColor = texture2D(tex, texcoord);}\n");
195         ASSERT_NE(0u, program);
196         glUseProgram(program);
197 
198         GLfloat vertices[] = {
199             -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
200             1.0f,  -1.0f, 0.0f, 1.0f, 1.0f,  1.0f, 0.0f, 1.0f,
201         };
202 
203         const int kNumQuads = 10000;
204         std::vector<GLushort> indices(6 * kNumQuads);
205 
206         for (size_t i = 0; i < kNumQuads; i++)
207         {
208             indices[i * 6 + 0] = 0;
209             indices[i * 6 + 1] = 1;
210             indices[i * 6 + 2] = 2;
211             indices[i * 6 + 3] = 1;
212             indices[i * 6 + 4] = 2;
213             indices[i * 6 + 5] = 3;
214         }
215 
216         glBindAttribLocation(program, 0, "pos");
217         glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, vertices);
218         glEnableVertexAttribArray(0);
219 
220         GLTexture texture;
221         glActiveTexture(GL_TEXTURE0);
222         glBindTexture(GL_TEXTURE_2D, texture);
223         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
224         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
225         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
226 
227         GLint textureUniformLocation = glGetUniformLocation(program, "tex");
228         glUniform1i(textureUniformLocation, 0);
229 
230         glViewport(0, 0, mOSWindow->getWidth(), mOSWindow->getHeight());
231         glClearColor(1.0, 0.0, 0.0, 1.0);
232         glClear(GL_COLOR_BUFFER_BIT);
233         glDrawElementsInstancedANGLE(GL_TRIANGLES, kNumQuads * 6, GL_UNSIGNED_SHORT, indices.data(),
234                                      10000);
235     }
236 
getInvalidShaderLocalVariableAccessFS()237     const char *getInvalidShaderLocalVariableAccessFS()
238     {
239         static constexpr char kFS[] = R"(#version 300 es
240             layout(location = 0) out highp vec4 fragColor;
241             uniform highp int u_index;
242             uniform mediump float u_color;
243 
244             void main (void)
245             {
246                 highp vec4 color = vec4(0.0f);
247                 color[u_index] = u_color;
248                 fragColor = color;
249             })";
250 
251         return kFS;
252     }
253 
testInvalidShaderLocalVariableAccess(GLuint program,const eglContextOpenglRobustAccess robustAccessAttrib)254     void testInvalidShaderLocalVariableAccess(GLuint program,
255                                               const eglContextOpenglRobustAccess robustAccessAttrib)
256     {
257         glUseProgram(program);
258         EXPECT_GL_NO_ERROR();
259 
260         GLint indexLocation = glGetUniformLocation(program, "u_index");
261         ASSERT_NE(-1, indexLocation);
262         GLint colorLocation = glGetUniformLocation(program, "u_color");
263         ASSERT_NE(-1, colorLocation);
264 
265         // delibrately pass in -1 to u_index to test robustness extension protects write out of
266         // bound
267         constexpr GLint kInvalidIndex = -1;
268         glUniform1i(indexLocation, kInvalidIndex);
269         EXPECT_GL_NO_ERROR();
270 
271         glUniform1f(colorLocation, 1.0f);
272 
273         drawQuad(program, essl31_shaders::PositionAttrib(), 0);
274         EXPECT_GL_NO_ERROR();
275 
276         // When command buffers are submitted to GPU, if robustness is working properly, the
277         // fragment shader will not suffer from write out-of-bounds issue, which resulted in context
278         // reset and context loss.
279         glFinish();
280 
281         GLint errorCode = glGetError();
282 
283         if (robustAccessAttrib == eglContextOpenglRobustAccess::enable)
284         {
285             ASSERT(errorCode == GL_NO_ERROR);
286         }
287         else
288         {
289             ASSERT(errorCode == GL_NO_ERROR || errorCode == GL_CONTEXT_LOST);
290         }
291     }
292 
293   protected:
294     EGLDisplay mDisplay = EGL_NO_DISPLAY;
295     EGLSurface mWindow  = EGL_NO_SURFACE;
296     EGLContext mContext = EGL_NO_CONTEXT;
297     bool mInitialized   = false;
298 
299   private:
300     EGLConfig mConfig   = 0;
301     OSWindow *mOSWindow = nullptr;
302 };
303 
304 class EGLRobustnessTestES3 : public EGLRobustnessTest
305 {};
306 
307 class EGLRobustnessTestES31 : public EGLRobustnessTest
308 {};
309 
310 // Check glGetGraphicsResetStatusEXT returns GL_NO_ERROR if we did nothing
TEST_P(EGLRobustnessTest,NoErrorByDefault)311 TEST_P(EGLRobustnessTest, NoErrorByDefault)
312 {
313     ANGLE_SKIP_TEST_IF(!mInitialized);
314     ASSERT_TRUE(glGetGraphicsResetStatusEXT() == GL_NO_ERROR);
315 }
316 
317 // Checks that the application gets no loss with NO_RESET_NOTIFICATION
TEST_P(EGLRobustnessTest,DISABLED_NoResetNotification)318 TEST_P(EGLRobustnessTest, DISABLED_NoResetNotification)
319 {
320     ANGLE_SKIP_TEST_IF(
321         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"));
322     ANGLE_SKIP_TEST_IF(!mInitialized);
323     createContext(EGL_NO_RESET_NOTIFICATION_EXT);
324 
325     if (!IsWindows())
326     {
327         std::cout << "Test disabled on non Windows platforms because drivers can't recover. "
328                   << "See " << __FILE__ << ":" << __LINE__ << std::endl;
329         return;
330     }
331     std::cout << "Causing a GPU reset, brace for impact." << std::endl;
332 
333     submitLongRunningTask();
334     glFinish();
335     ASSERT_TRUE(glGetGraphicsResetStatusEXT() == GL_NO_ERROR);
336 }
337 
338 // Checks that resetting the ANGLE display allows to get rid of the context loss.
339 // Also checks that the application gets notified of the loss of the display.
340 // We coalesce both tests to reduce the number of TDRs done on Windows: by default
341 // having more than 5 TDRs in a minute will cause Windows to disable the GPU until
342 // the computer is rebooted.
TEST_P(EGLRobustnessTest,DISABLED_ResettingDisplayWorks)343 TEST_P(EGLRobustnessTest, DISABLED_ResettingDisplayWorks)
344 {
345     ANGLE_SKIP_TEST_IF(
346         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"));
347 
348     // Note that on Windows the OpenGL driver fails hard (popup that closes the application)
349     // on a TDR caused by D3D. Don't run D3D tests at the same time as the OpenGL tests.
350     ANGLE_SKIP_TEST_IF(IsWindows() && isGLRenderer());
351     ANGLE_SKIP_TEST_IF(!mInitialized);
352 
353     createContext(EGL_LOSE_CONTEXT_ON_RESET_EXT);
354 
355     if (!IsWindows())
356     {
357         std::cout << "Test disabled on non Windows platforms because drivers can't recover. "
358                   << "See " << __FILE__ << ":" << __LINE__ << std::endl;
359         return;
360     }
361     std::cout << "Causing a GPU reset, brace for impact." << std::endl;
362 
363     submitLongRunningTask();
364     glFinish();
365     ASSERT_TRUE(glGetGraphicsResetStatusEXT() != GL_NO_ERROR);
366 
367     recreateTestFixture();
368     ASSERT_TRUE(glGetGraphicsResetStatusEXT() == GL_NO_ERROR);
369 }
370 
371 // Test to reproduce the crash when running
372 // dEQP-EGL.functional.robustness.reset_context.shaders.out_of_bounds.reset_status.writes.uniform_block.fragment
373 // on Pixel 6
TEST_P(EGLRobustnessTestES3,ContextResetOnInvalidLocalShaderVariableAccess)374 TEST_P(EGLRobustnessTestES3, ContextResetOnInvalidLocalShaderVariableAccess)
375 {
376     ANGLE_SKIP_TEST_IF(!mInitialized);
377 
378     ANGLE_SKIP_TEST_IF(
379         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_create_context") ||
380         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"));
381 
382     createRobustContext(EGL_LOSE_CONTEXT_ON_RESET, EGL_NO_CONTEXT);
383 
384     ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), getInvalidShaderLocalVariableAccessFS());
385     testInvalidShaderLocalVariableAccess(program, eglContextOpenglRobustAccess::enable);
386 }
387 
388 // Similar to ContextResetOnInvalidLocalShaderVariableAccess, but the program is created on a
389 // context that's not robust, but used on one that is.
TEST_P(EGLRobustnessTestES3,ContextResetOnInvalidLocalShaderVariableAccess_ShareGroupBeforeProgramCreation)390 TEST_P(EGLRobustnessTestES3,
391        ContextResetOnInvalidLocalShaderVariableAccess_ShareGroupBeforeProgramCreation)
392 {
393     ANGLE_SKIP_TEST_IF(!mInitialized);
394 
395     ANGLE_SKIP_TEST_IF(
396         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_create_context") ||
397         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"));
398 
399     createContext(EGL_LOSE_CONTEXT_ON_RESET);
400     EGLContext shareContext = mContext;
401     createRobustContext(EGL_LOSE_CONTEXT_ON_RESET, shareContext);
402 
403     eglMakeCurrent(mDisplay, mWindow, mWindow, shareContext);
404     ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), getInvalidShaderLocalVariableAccessFS());
405     eglMakeCurrent(mDisplay, mWindow, mWindow, mContext);
406 
407     testInvalidShaderLocalVariableAccess(program, eglContextOpenglRobustAccess::enable);
408 
409     eglDestroyContext(mDisplay, shareContext);
410 }
411 
412 // Similar to ContextResetOnInvalidLocalShaderVariableAccess, but the program is created on a
413 // context that's not robust, but used on one that is.
TEST_P(EGLRobustnessTestES3,ContextResetOnInvalidLocalShaderVariableAccess_ShareGroupAfterProgramCreation)414 TEST_P(EGLRobustnessTestES3,
415        ContextResetOnInvalidLocalShaderVariableAccess_ShareGroupAfterProgramCreation)
416 {
417     ANGLE_SKIP_TEST_IF(!mInitialized);
418 
419     ANGLE_SKIP_TEST_IF(
420         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_create_context") ||
421         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"));
422 
423     createContext(EGL_LOSE_CONTEXT_ON_RESET);
424     ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), getInvalidShaderLocalVariableAccessFS());
425 
426     EGLContext shareContext = mContext;
427 
428     createRobustContext(EGL_LOSE_CONTEXT_ON_RESET, shareContext);
429     testInvalidShaderLocalVariableAccess(program, eglContextOpenglRobustAccess::enable);
430 
431     eglDestroyContext(mDisplay, shareContext);
432 }
433 
434 // Test to ensure shader local variable write out of bound won't crash
435 // when the context has robustness enabled, and EGL_NO_RESET_NOTIFICATION_EXT
436 // is set as the value for attribute EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEFY_EXT
TEST_P(EGLRobustnessTestES3,ContextNoResetOnInvalidLocalShaderVariableAccess)437 TEST_P(EGLRobustnessTestES3, ContextNoResetOnInvalidLocalShaderVariableAccess)
438 {
439     ANGLE_SKIP_TEST_IF(!mInitialized);
440     ANGLE_SKIP_TEST_IF(
441         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_create_context") ||
442         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"));
443 
444     createRobustContext(EGL_NO_RESET_NOTIFICATION_EXT, EGL_NO_CONTEXT);
445 
446     ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), getInvalidShaderLocalVariableAccessFS());
447     testInvalidShaderLocalVariableAccess(program, eglContextOpenglRobustAccess::enable);
448 }
449 
450 // Similar to ContextNoResetOnInvalidLocalShaderVariableAccess, but the program is created on a
451 // context that's not robust, but used on one that is.
TEST_P(EGLRobustnessTestES3,ContextNoResetOnInvalidLocalShaderVariableAccess_ShareGroupBeforeProgramCreation)452 TEST_P(EGLRobustnessTestES3,
453        ContextNoResetOnInvalidLocalShaderVariableAccess_ShareGroupBeforeProgramCreation)
454 {
455     ANGLE_SKIP_TEST_IF(!mInitialized);
456     ANGLE_SKIP_TEST_IF(
457         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_create_context") ||
458         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"));
459 
460     createContext(EGL_NO_RESET_NOTIFICATION_EXT);
461     EGLContext shareContext = mContext;
462     createRobustContext(EGL_NO_RESET_NOTIFICATION_EXT, shareContext);
463 
464     eglMakeCurrent(mDisplay, mWindow, mWindow, shareContext);
465     ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), getInvalidShaderLocalVariableAccessFS());
466     eglMakeCurrent(mDisplay, mWindow, mWindow, mContext);
467 
468     testInvalidShaderLocalVariableAccess(program, eglContextOpenglRobustAccess::enable);
469 
470     eglDestroyContext(mDisplay, shareContext);
471 }
472 
473 // Similar to ContextNoResetOnInvalidLocalShaderVariableAccess, but the program is created on a
474 // context that's not robust, but used on one that is.
TEST_P(EGLRobustnessTestES3,ContextNoResetOnInvalidLocalShaderVariableAccess_ShareGroupAfterProgramCreation)475 TEST_P(EGLRobustnessTestES3,
476        ContextNoResetOnInvalidLocalShaderVariableAccess_ShareGroupAfterProgramCreation)
477 {
478     ANGLE_SKIP_TEST_IF(!mInitialized);
479     ANGLE_SKIP_TEST_IF(
480         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_create_context") ||
481         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"));
482 
483     createContext(EGL_NO_RESET_NOTIFICATION_EXT);
484     ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), getInvalidShaderLocalVariableAccessFS());
485 
486     EGLContext shareContext = mContext;
487 
488     createRobustContext(EGL_NO_RESET_NOTIFICATION_EXT, shareContext);
489     testInvalidShaderLocalVariableAccess(program, eglContextOpenglRobustAccess::enable);
490 
491     eglDestroyContext(mDisplay, shareContext);
492 }
493 
494 // Replicate test
495 // dEQP-EGL.functional.robustness.reset_context.shaders.out_of_bounds_non_robust.reset_status.writes
496 // .local_array.fragment
497 // Test that when writing out-of-bounds in fragment shader:
498 // 1) After draw command, test receives GL_CONTEXT_LOST error or GL_NO_ERROR.
499 // 2) eglMakeCurrent(EGL_NO_CONTEXT) on lost context should return EGL_SUCCESS.
TEST_P(EGLRobustnessTestES3,NonRobustContextOnInvalidLocalShaderVariableAccessShouldNotCrash)500 TEST_P(EGLRobustnessTestES3, NonRobustContextOnInvalidLocalShaderVariableAccessShouldNotCrash)
501 {
502 
503     ANGLE_SKIP_TEST_IF(!mInitialized);
504     ANGLE_SKIP_TEST_IF(
505         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_create_context") ||
506         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"));
507 
508     createClientVersion3NonRobustContext(EGL_LOSE_CONTEXT_ON_RESET_KHR);
509 
510     ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), getInvalidShaderLocalVariableAccessFS());
511     testInvalidShaderLocalVariableAccess(program, eglContextOpenglRobustAccess::disable);
512 
513     eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
514     GLint errorCode = eglGetError();
515     ASSERT(errorCode == EGL_SUCCESS);
516 }
517 
518 // Test that using a program in a non-robust context, then sharing it with a robust context and
519 // using it with the same state (but with an OOB access) works.
TEST_P(EGLRobustnessTestES31,NonRobustContextThenOOBInSharedRobustContext)520 TEST_P(EGLRobustnessTestES31, NonRobustContextThenOOBInSharedRobustContext)
521 {
522     ANGLE_SKIP_TEST_IF(!mInitialized);
523     ANGLE_SKIP_TEST_IF(
524         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_create_context") ||
525         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"));
526 
527     createContext(EGL_NO_RESET_NOTIFICATION_EXT);
528 
529     GLint maxFragmentShaderStorageBlocks = 0;
530     glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &maxFragmentShaderStorageBlocks);
531     ANGLE_SKIP_TEST_IF(maxFragmentShaderStorageBlocks == 0);
532 
533     constexpr char kFS[] = R"(#version 310 es
534 layout(location = 0) out highp vec4 fragColor;
535 layout(std140, binding = 0) buffer Block
536 {
537     mediump vec4 data[];
538 };
539 uniform mediump uint index;
540 
541 void main (void)
542 {
543     fragColor = data[index];
544 })";
545 
546     ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Simple(), kFS);
547     glUseProgram(program);
548 
549     GLint indexLocation = glGetUniformLocation(program, "index");
550     ASSERT_NE(-1, indexLocation);
551 
552     constexpr std::array<float, 4> kBufferData = {1, 0, 0, 1};
553 
554     GLBuffer buffer;
555     glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
556     glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(kBufferData), kBufferData.data(), GL_STATIC_DRAW);
557     glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffer);
558 
559     // Use the program once before the robust context is created.
560     glUniform1ui(indexLocation, 0);
561     drawQuad(program, essl31_shaders::PositionAttrib(), 0);
562     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
563     ASSERT_GL_NO_ERROR();
564 
565     // Create the share context as robust, and draw identically except accessing OOB.
566     EGLContext shareContext = mContext;
567 
568     createRobustContext(EGL_NO_RESET_NOTIFICATION_EXT, shareContext);
569 
570     glClearColor(0, 1, 0, 1);
571     glClear(GL_COLOR_BUFFER_BIT);
572 
573     glUseProgram(program);
574     glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
575     glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffer);
576     glUniform1ui(indexLocation, 1'000'000'000u);
577     drawQuad(program, essl31_shaders::PositionAttrib(), 0);
578     // Expect 0, 0, 0, 0/1 returned from buffer
579     GLColor actualColor = angle::ReadColor(0, 0);
580     EXPECT_TRUE(actualColor.A == 0 || actualColor.A == 255);
581     actualColor.A = 0;
582     EXPECT_EQ(actualColor, GLColor::transparentBlack);
583     ASSERT_GL_NO_ERROR();
584 
585     eglDestroyContext(mDisplay, shareContext);
586 }
587 
588 // Similar to NonRobustContextThenOOBInSharedRobustContext, but access is in vertex shader.
TEST_P(EGLRobustnessTestES31,NonRobustContextThenOOBInSharedRobustContext_VertexShader)589 TEST_P(EGLRobustnessTestES31, NonRobustContextThenOOBInSharedRobustContext_VertexShader)
590 {
591     ANGLE_SKIP_TEST_IF(!mInitialized);
592     ANGLE_SKIP_TEST_IF(
593         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_create_context") ||
594         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"));
595 
596     createContext(EGL_NO_RESET_NOTIFICATION_EXT);
597 
598     GLint maxVertexShaderStorageBlocks = 0;
599     glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &maxVertexShaderStorageBlocks);
600     ANGLE_SKIP_TEST_IF(maxVertexShaderStorageBlocks == 0);
601 
602     constexpr char kVS[] = R"(#version 310 es
603 layout(std140, binding = 0) buffer Block
604 {
605     mediump vec4 data[];
606 };
607 uniform mediump uint index;
608 in vec4 position;
609 out mediump vec4 color;
610 
611 void main (void)
612 {
613     color = data[index];
614     gl_Position = position;
615 })";
616 
617     constexpr char kFS[] = R"(#version 310 es
618 layout(location = 0) out highp vec4 fragColor;
619 in mediump vec4 color;
620 
621 void main (void)
622 {
623     fragColor = color;
624 })";
625 
626     ANGLE_GL_PROGRAM(program, kVS, kFS);
627     glUseProgram(program);
628 
629     GLint indexLocation = glGetUniformLocation(program, "index");
630     ASSERT_NE(-1, indexLocation);
631 
632     constexpr std::array<float, 4> kBufferData = {1, 0, 0, 1};
633 
634     GLBuffer buffer;
635     glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
636     glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(kBufferData), kBufferData.data(), GL_STATIC_DRAW);
637     glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffer);
638 
639     // Use the program once before the robust context is created.
640     glUniform1ui(indexLocation, 0);
641     drawQuad(program, "position", 0);
642     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
643     ASSERT_GL_NO_ERROR();
644 
645     // Create the share context as robust, and draw identically except accessing OOB.
646     EGLContext shareContext = mContext;
647 
648     createRobustContext(EGL_NO_RESET_NOTIFICATION_EXT, shareContext);
649 
650     glClearColor(0, 1, 0, 1);
651     glClear(GL_COLOR_BUFFER_BIT);
652 
653     glUseProgram(program);
654     glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
655     glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffer);
656     glUniform1ui(indexLocation, 1'000'000'000u);
657     drawQuad(program, "position", 0);
658     // Expect 0, 0, 0, 0/1 returned from buffer
659     GLColor actualColor = angle::ReadColor(0, 0);
660     EXPECT_TRUE(actualColor.A == 0 || actualColor.A == 255);
661     actualColor.A = 0;
662     EXPECT_EQ(actualColor, GLColor::transparentBlack);
663     ASSERT_GL_NO_ERROR();
664 
665     eglDestroyContext(mDisplay, shareContext);
666 }
667 
668 // Similar to NonRobustContextThenOOBInSharedRobustContext, but access is in compute shader.
TEST_P(EGLRobustnessTestES31,NonRobustContextThenOOBInSharedRobustContext_ComputeShader)669 TEST_P(EGLRobustnessTestES31, NonRobustContextThenOOBInSharedRobustContext_ComputeShader)
670 {
671     ANGLE_SKIP_TEST_IF(!mInitialized);
672     ANGLE_SKIP_TEST_IF(
673         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_create_context") ||
674         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"));
675 
676     createContext(EGL_NO_RESET_NOTIFICATION_EXT);
677 
678     constexpr char kCS[] = R"(#version 310 es
679 layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
680 
681 layout(std140, binding = 0) buffer BlockIn
682 {
683     mediump vec4 dataIn[];
684 };
685 layout(std140, binding = 1) buffer BlockOut
686 {
687     mediump vec4 dataOut;
688 };
689 uniform mediump uint index;
690 
691 void main (void)
692 {
693     dataOut = dataIn[index];
694 })";
695 
696     ANGLE_GL_COMPUTE_PROGRAM(program, kCS);
697     glUseProgram(program);
698 
699     GLint indexLocation = glGetUniformLocation(program, "index");
700     ASSERT_NE(-1, indexLocation);
701 
702     constexpr std::array<float, 4> kBufferData        = {1, 0, 0, 1};
703     constexpr std::array<float, 4> kInvalidBufferData = {0, 0, 1, 1};
704 
705     GLBuffer bufferIn;
706     glBindBuffer(GL_SHADER_STORAGE_BUFFER, bufferIn);
707     glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(kBufferData), kBufferData.data(), GL_STATIC_DRAW);
708     glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, bufferIn);
709 
710     GLBuffer bufferOut;
711     glBindBuffer(GL_SHADER_STORAGE_BUFFER, bufferOut);
712     glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(kInvalidBufferData), kInvalidBufferData.data(),
713                  GL_STATIC_DRAW);
714     glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, bufferOut);
715 
716     // Use the program once before the robust context is created.
717     glUniform1ui(indexLocation, 0);
718     glDispatchCompute(1, 1, 1);
719     ASSERT_GL_NO_ERROR();
720 
721     std::array<float, 4> readbackData = {};
722 
723     glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
724     void *mappedBuffer =
725         glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(readbackData), GL_MAP_READ_BIT);
726     memcpy(readbackData.data(), mappedBuffer, sizeof(readbackData));
727     glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
728 
729     EXPECT_EQ(readbackData, kBufferData);
730 
731     // Create the share context as robust, and draw identically except accessing OOB.
732     EGLContext shareContext = mContext;
733 
734     createRobustContext(EGL_NO_RESET_NOTIFICATION_EXT, shareContext);
735 
736     glUseProgram(program);
737     glBindBuffer(GL_SHADER_STORAGE_BUFFER, bufferIn);
738     glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, bufferIn);
739     glBindBuffer(GL_SHADER_STORAGE_BUFFER, bufferOut);
740     glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, bufferOut);
741     glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(kInvalidBufferData), kInvalidBufferData.data(),
742                  GL_STATIC_DRAW);
743 
744     glUniform1ui(indexLocation, 1'000'000'000u);
745     glDispatchCompute(1, 1, 1);
746 
747     // Expect 0, 0, 0, 0/1 returned from bufferIn
748     glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
749     mappedBuffer =
750         glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(readbackData), GL_MAP_READ_BIT);
751     memcpy(readbackData.data(), mappedBuffer, sizeof(readbackData));
752     glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
753     ASSERT_GL_NO_ERROR();
754 
755     EXPECT_EQ(readbackData[0], 0);
756     EXPECT_EQ(readbackData[1], 0);
757     EXPECT_EQ(readbackData[2], 0);
758     EXPECT_TRUE(readbackData[3] == 0 || readbackData[3] == 1);
759 
760     eglDestroyContext(mDisplay, shareContext);
761 }
762 
763 // Test that indirect indices on unsized storage buffer arrays work.  Regression test for the
764 // ClampIndirectIndices AST transformation.
TEST_P(EGLRobustnessTestES31,IndirectIndexOnUnsizedStorageBufferArray)765 TEST_P(EGLRobustnessTestES31, IndirectIndexOnUnsizedStorageBufferArray)
766 {
767     ANGLE_SKIP_TEST_IF(!mInitialized);
768     ANGLE_SKIP_TEST_IF(
769         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_create_context") ||
770         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"));
771 
772     createRobustContext(EGL_NO_RESET_NOTIFICATION_EXT, EGL_NO_CONTEXT);
773 
774     const char kCS[] = R"(#version 310 es
775 
776 layout(binding = 0, std430) buffer B {
777   uint data[];
778 } b;
779 
780 layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
781 void main() {
782   b.data[gl_GlobalInvocationID.x] = gl_GlobalInvocationID.x;
783 })";
784 
785     ANGLE_GL_COMPUTE_PROGRAM(program, kCS);
786     EXPECT_GL_NO_ERROR();
787 
788     constexpr uint32_t kBufferSize              = 2;
789     constexpr uint32_t kBufferData[kBufferSize] = {10, 20};
790 
791     GLBuffer buffer;
792     glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
793     glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(kBufferData), kBufferData, GL_STATIC_DRAW);
794     glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffer);
795 
796     // Run the compute shader with a large workload.  Only the first two invocations should write to
797     // the buffer, the rest should be dropped out due to robust access.
798     glUseProgram(program);
799     glDispatchCompute(8192, 1, 1);
800 
801     glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
802     uint32_t bufferDataOut[kBufferSize] = {};
803     const uint32_t *ptr                 = reinterpret_cast<uint32_t *>(
804         glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(kBufferData), GL_MAP_READ_BIT));
805     memcpy(bufferDataOut, ptr, sizeof(kBufferData));
806     glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
807 
808     for (uint32_t index = 0; index < kBufferSize; ++index)
809     {
810         EXPECT_EQ(bufferDataOut[index], index) << " index " << index;
811     }
812 }
813 
814 // Similar to IndirectIndexOnUnsizedStorageBufferArray, but without a block instance name.
TEST_P(EGLRobustnessTestES31,IndirectIndexOnUnsizedStorageBufferArray_NoBlockInstanceName)815 TEST_P(EGLRobustnessTestES31, IndirectIndexOnUnsizedStorageBufferArray_NoBlockInstanceName)
816 {
817     ANGLE_SKIP_TEST_IF(!mInitialized);
818     ANGLE_SKIP_TEST_IF(
819         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_KHR_create_context") ||
820         !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_create_context_robustness"));
821 
822     createRobustContext(EGL_NO_RESET_NOTIFICATION_EXT, EGL_NO_CONTEXT);
823 
824     const char kCS[] = R"(#version 310 es
825 
826 layout(binding = 0, std430) buffer B {
827   uint data[];
828 };
829 
830 layout(local_size_x = 64, local_size_y = 1, local_size_z = 1) in;
831 void main() {
832   data[gl_GlobalInvocationID.x] = gl_GlobalInvocationID.x;
833 })";
834 
835     ANGLE_GL_COMPUTE_PROGRAM(program, kCS);
836     EXPECT_GL_NO_ERROR();
837 
838     constexpr uint32_t kBufferSize              = 2;
839     constexpr uint32_t kBufferData[kBufferSize] = {10, 20};
840 
841     GLBuffer buffer;
842     glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
843     glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(kBufferData), kBufferData, GL_STATIC_DRAW);
844     glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffer);
845 
846     // Run the compute shader with a large workload.  Only the first two invocations should write to
847     // the buffer, the rest should be dropped out due to robust access.
848     glUseProgram(program);
849     glDispatchCompute(8192, 1, 1);
850 
851     glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
852     uint32_t bufferDataOut[kBufferSize] = {};
853     const uint32_t *ptr                 = reinterpret_cast<uint32_t *>(
854         glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(kBufferData), GL_MAP_READ_BIT));
855     memcpy(bufferDataOut, ptr, sizeof(kBufferData));
856     glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
857 
858     for (uint32_t index = 0; index < kBufferSize; ++index)
859     {
860         EXPECT_EQ(bufferDataOut[index], index) << " index " << index;
861     }
862 }
863 
864 // Test context destruction after recovering from a long running task.
TEST_P(EGLRobustnessTest,DISABLED_LongRunningTaskVulkanShutdown)865 TEST_P(EGLRobustnessTest, DISABLED_LongRunningTaskVulkanShutdown)
866 {
867     ANGLE_SKIP_TEST_IF(!mInitialized);
868 
869     createContext(EGL_NO_RESET_NOTIFICATION_EXT);
870     submitLongRunningTask();
871     destroyContext();
872 }
873 
874 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLRobustnessTest);
875 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLRobustnessTestES3);
876 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLRobustnessTestES31);
877 ANGLE_INSTANTIATE_TEST(EGLRobustnessTest,
878                        WithNoFixture(ES2_VULKAN()),
879                        WithNoFixture(ES2_D3D9()),
880                        WithNoFixture(ES2_D3D11()),
881                        WithNoFixture(ES2_OPENGL()),
882                        WithNoFixture(ES2_OPENGLES()),
883                        WithNoFixture(ES2_VULKAN_SWIFTSHADER()));
884 ANGLE_INSTANTIATE_TEST(EGLRobustnessTestES3,
885                        WithNoFixture(ES3_VULKAN()),
886                        WithNoFixture(ES3_D3D11()),
887                        WithNoFixture(ES3_OPENGL()),
888                        WithNoFixture(ES3_OPENGLES()),
889                        WithNoFixture(ES3_VULKAN_SWIFTSHADER()));
890 ANGLE_INSTANTIATE_TEST(EGLRobustnessTestES31,
891                        WithNoFixture(ES31_VULKAN()),
892                        WithNoFixture(ES31_VULKAN_SWIFTSHADER()));
893