• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2021 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 // EGLMultiContextTest.cpp:
7 //   Tests relating to multiple non-shared Contexts.
8 
9 #include <gtest/gtest.h>
10 
11 #include "EGLMultiThreadSteps.h"
12 #include "test_utils/ANGLETest.h"
13 #include "test_utils/angle_test_configs.h"
14 #include "test_utils/gl_raii.h"
15 #include "util/EGLWindow.h"
16 
17 using namespace angle;
18 
19 namespace
20 {
21 
SafeDestroyContext(EGLDisplay display,EGLContext & context)22 EGLBoolean SafeDestroyContext(EGLDisplay display, EGLContext &context)
23 {
24     EGLBoolean result = EGL_TRUE;
25     if (context != EGL_NO_CONTEXT)
26     {
27         result  = eglDestroyContext(display, context);
28         context = EGL_NO_CONTEXT;
29     }
30     return result;
31 }
32 
33 class EGLMultiContextTest : public ANGLETest
34 {
35   public:
EGLMultiContextTest()36     EGLMultiContextTest() : mContexts{EGL_NO_CONTEXT, EGL_NO_CONTEXT}, mTexture(0) {}
37 
testTearDown()38     void testTearDown() override
39     {
40         glDeleteTextures(1, &mTexture);
41 
42         EGLDisplay display = getEGLWindow()->getDisplay();
43 
44         if (display != EGL_NO_DISPLAY)
45         {
46             for (auto &context : mContexts)
47             {
48                 SafeDestroyContext(display, context);
49             }
50         }
51 
52         // Set default test state to not give an error on shutdown.
53         getEGLWindow()->makeCurrent();
54     }
55 
56     EGLContext mContexts[2];
57     GLuint mTexture;
58 };
59 
60 // Test that calling eglDeleteContext on a context that is not current succeeds.
TEST_P(EGLMultiContextTest,TestContextDestroySimple)61 TEST_P(EGLMultiContextTest, TestContextDestroySimple)
62 {
63     EGLWindow *window = getEGLWindow();
64     EGLDisplay dpy    = window->getDisplay();
65 
66     EGLContext context1 = window->createContext(EGL_NO_CONTEXT);
67     EGLContext context2 = window->createContext(EGL_NO_CONTEXT);
68 
69     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, context1));
70     EXPECT_EGL_TRUE(eglDestroyContext(dpy, context2));
71     EXPECT_EGL_SUCCESS();
72 }
73 
74 // Test that a compute shader running in one thread will still work when rendering is happening in
75 // another thread (with non-shared contexts).  The non-shared context will still share a Vulkan
76 // command buffer.
TEST_P(EGLMultiContextTest,ComputeShaderOkayWithRendering)77 TEST_P(EGLMultiContextTest, ComputeShaderOkayWithRendering)
78 {
79     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
80     ANGLE_SKIP_TEST_IF(!isVulkanRenderer());
81     ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 || getClientMinorVersion() < 1);
82 
83     // Initialize contexts
84     EGLWindow *window = getEGLWindow();
85     EGLDisplay dpy    = window->getDisplay();
86     EGLConfig config  = window->getConfig();
87 
88     constexpr size_t kThreadCount    = 2;
89     EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
90     EGLContext ctx[kThreadCount]     = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
91 
92     EGLint pbufferAttributes[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE};
93 
94     for (size_t t = 0; t < kThreadCount; ++t)
95     {
96         surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
97         EXPECT_EGL_SUCCESS();
98 
99         ctx[t] = window->createContext(EGL_NO_CONTEXT);
100         EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
101     }
102 
103     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
104     std::mutex mutex;
105     std::condition_variable condVar;
106 
107     enum class Step
108     {
109         Thread0Start,
110         Thread0DispatchedCompute,
111         Thread1Drew,
112         Thread0DispatchedComputeAgain,
113         Finish,
114         Abort,
115     };
116     Step currentStep = Step::Thread0Start;
117 
118     // This first thread dispatches a compute shader.  It immediately starts.
119     std::thread deletingThread = std::thread([&]() {
120         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
121 
122         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
123         EXPECT_EGL_SUCCESS();
124 
125         // Potentially wait to be signalled to start.
126         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Start));
127 
128         // Wake up and do next step: Create, detach, and dispatch a compute shader program.
129         constexpr char kCS[]  = R"(#version 310 es
130 layout(local_size_x=1) in;
131 void main()
132 {
133 })";
134         GLuint computeProgram = glCreateProgram();
135         GLuint cs             = CompileShader(GL_COMPUTE_SHADER, kCS);
136         EXPECT_NE(0u, cs);
137 
138         glAttachShader(computeProgram, cs);
139         glDeleteShader(cs);
140         glLinkProgram(computeProgram);
141         GLint linkStatus;
142         glGetProgramiv(computeProgram, GL_LINK_STATUS, &linkStatus);
143         EXPECT_GL_TRUE(linkStatus);
144         glDetachShader(computeProgram, cs);
145         EXPECT_GL_NO_ERROR();
146         glUseProgram(computeProgram);
147 
148         glDispatchCompute(8, 4, 2);
149         EXPECT_GL_NO_ERROR();
150 
151         // Signal the second thread and wait for it to draw and flush.
152         threadSynchronization.nextStep(Step::Thread0DispatchedCompute);
153         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Drew));
154 
155         // Wake up and do next step: Dispatch the same compute shader again.
156         glDispatchCompute(8, 4, 2);
157 
158         // Signal the second thread and wait for it to draw and flush again.
159         threadSynchronization.nextStep(Step::Thread0DispatchedComputeAgain);
160         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
161 
162         // Wake up and do next step: Dispatch the same compute shader again, and force flush the
163         // underlying command buffer.
164         glDispatchCompute(8, 4, 2);
165         glFinish();
166 
167         // Clean-up and exit this thread.
168         EXPECT_GL_NO_ERROR();
169         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
170         EXPECT_EGL_SUCCESS();
171     });
172 
173     // This second thread renders.  It starts once the other thread does its first nextStep()
174     std::thread continuingThread = std::thread([&]() {
175         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
176 
177         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
178         EXPECT_EGL_SUCCESS();
179 
180         // Wait for first thread to create and dispatch a compute shader.
181         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0DispatchedCompute));
182 
183         // Wake up and do next step: Create graphics resources, draw, and force flush the
184         // underlying command buffer.
185         GLTexture texture;
186         glBindTexture(GL_TEXTURE_2D, texture);
187         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
188         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
189         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
190 
191         GLRenderbuffer renderbuffer;
192         GLFramebuffer fbo;
193         glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
194         constexpr int kRenderbufferSize = 4;
195         glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kRenderbufferSize, kRenderbufferSize);
196         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
197         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
198                                   renderbuffer);
199         glBindTexture(GL_TEXTURE_2D, texture);
200 
201         GLProgram graphicsProgram;
202         graphicsProgram.makeRaster(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
203         ASSERT_TRUE(graphicsProgram.valid());
204 
205         drawQuad(graphicsProgram.get(), essl1_shaders::PositionAttrib(), 0.5f);
206         glFinish();
207 
208         // Signal the first thread and wait for it to dispatch a compute shader again.
209         threadSynchronization.nextStep(Step::Thread1Drew);
210         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0DispatchedComputeAgain));
211 
212         // Wake up and do next step: Draw and force flush the underlying command buffer again.
213         drawQuad(graphicsProgram.get(), essl1_shaders::PositionAttrib(), 0.5f);
214         glFinish();
215 
216         // Signal the first thread and wait exit this thread.
217         threadSynchronization.nextStep(Step::Finish);
218 
219         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
220         EXPECT_EGL_SUCCESS();
221     });
222 
223     deletingThread.join();
224     continuingThread.join();
225 
226     ASSERT_NE(currentStep, Step::Abort);
227 
228     // Clean up
229     for (size_t t = 0; t < kThreadCount; ++t)
230     {
231         eglDestroySurface(dpy, surface[t]);
232         eglDestroyContext(dpy, ctx[t]);
233     }
234 }
235 }  // anonymous namespace
236 
237 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLMultiContextTest);
238 ANGLE_INSTANTIATE_TEST_ES31(EGLMultiContextTest);
239