• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2018 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 // ParallelShaderCompileTest.cpp : Tests of the GL_KHR_parallel_shader_compile extension.
8 
9 #include "test_utils/ANGLETest.h"
10 #include "test_utils/gl_raii.h"
11 
12 #include "util/random_utils.h"
13 #include "util/test_utils.h"
14 
15 using namespace angle;
16 
17 namespace
18 {
19 
20 namespace
21 {
22 
23 constexpr int kTaskCount             = 32;
24 constexpr unsigned int kPollInterval = 100;
25 
26 }  // anonymous namespace
27 
28 class ParallelShaderCompileTest : public ANGLETest<>
29 {
30   protected:
ParallelShaderCompileTest()31     ParallelShaderCompileTest()
32     {
33         setWindowWidth(128);
34         setWindowHeight(128);
35         setConfigRedBits(8);
36         setConfigGreenBits(8);
37         setConfigBlueBits(8);
38         setConfigAlphaBits(8);
39     }
40 
ensureParallelShaderCompileExtensionAvailable()41     bool ensureParallelShaderCompileExtensionAvailable()
42     {
43         if (IsGLExtensionRequestable("GL_KHR_parallel_shader_compile"))
44         {
45             glRequestExtensionANGLE("GL_KHR_parallel_shader_compile");
46         }
47 
48         if (!IsGLExtensionEnabled("GL_KHR_parallel_shader_compile"))
49         {
50             return false;
51         }
52         return true;
53     }
54 
55     class Task
56     {
57       public:
Task(int id)58         Task(int id) : mID(id) {}
~Task()59         virtual ~Task() {}
60 
61         virtual bool compile()            = 0;
62         virtual bool isCompileCompleted() = 0;
63         virtual bool link()               = 0;
postLink()64         virtual void postLink() {}
65         virtual void runAndVerify(ParallelShaderCompileTest *test) = 0;
66 
isLinkCompleted()67         bool isLinkCompleted()
68         {
69             GLint status;
70             glGetProgramiv(mProgram, GL_COMPLETION_STATUS_KHR, &status);
71             return (status == GL_TRUE);
72         }
73 
74       protected:
InsertRandomString(const std::string & source)75         static std::string InsertRandomString(const std::string &source)
76         {
77             RNG rng;
78             std::ostringstream ostream;
79             ostream << source << "\n// Random string to fool program cache: " << rng.randomInt()
80                     << "\n";
81             return ostream.str();
82         }
83 
CompileShader(GLenum type,const std::string & source)84         static GLuint CompileShader(GLenum type, const std::string &source)
85         {
86             GLuint shader = glCreateShader(type);
87 
88             const char *sourceArray[1] = {source.c_str()};
89             glShaderSource(shader, 1, sourceArray, nullptr);
90             glCompileShader(shader);
91             return shader;
92         }
93 
RecompileShader(GLuint shader,const std::string & source)94         static void RecompileShader(GLuint shader, const std::string &source)
95         {
96             const char *sourceArray[1] = {source.c_str()};
97             glShaderSource(shader, 1, sourceArray, nullptr);
98             glCompileShader(shader);
99         }
100 
CheckShader(GLuint shader)101         static bool CheckShader(GLuint shader)
102         {
103             GLint compileResult;
104             glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
105 
106             if (compileResult == 0)
107             {
108                 GLint infoLogLength;
109                 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
110 
111                 // Info log length includes the null terminator, so 1 means that the info log is an
112                 // empty string.
113                 if (infoLogLength > 1)
114                 {
115                     std::vector<GLchar> infoLog(infoLogLength);
116                     glGetShaderInfoLog(shader, static_cast<GLsizei>(infoLog.size()), nullptr,
117                                        &infoLog[0]);
118                     std::cerr << "shader compilation failed: " << &infoLog[0];
119                 }
120                 else
121                 {
122                     std::cerr << "shader compilation failed. <Empty log message>";
123                 }
124                 std::cerr << std::endl;
125             }
126             return (compileResult == GL_TRUE);
127         }
128 
129         GLuint mProgram;
130         int mID;
131     };
132 
133     template <typename T>
134     class TaskRunner
135     {
136       public:
TaskRunner()137         TaskRunner() {}
~TaskRunner()138         ~TaskRunner() {}
139 
run(ParallelShaderCompileTest * test,unsigned int pollInterval)140         void run(ParallelShaderCompileTest *test, unsigned int pollInterval)
141         {
142 
143             std::vector<std::unique_ptr<T>> compileTasks;
144             for (int i = 0; i < kTaskCount; ++i)
145             {
146                 std::unique_ptr<T> task(new T(i));
147                 bool isCompiling = task->compile();
148                 ASSERT_TRUE(isCompiling);
149                 compileTasks.push_back(std::move(task));
150             }
151 
152             std::vector<std::unique_ptr<T>> linkTasks;
153             while (!compileTasks.empty())
154             {
155                 for (unsigned int i = 0; i < compileTasks.size();)
156                 {
157                     auto &task = compileTasks[i];
158 
159                     if (task->isCompileCompleted())
160                     {
161                         bool isLinking = task->link();
162                         task->postLink();
163                         ASSERT_TRUE(isLinking);
164                         linkTasks.push_back(std::move(task));
165                         compileTasks.erase(compileTasks.begin() + i);
166                         continue;
167                     }
168                     ++i;
169                 }
170                 if (pollInterval != 0)
171                 {
172                     angle::Sleep(pollInterval);
173                 }
174             }
175 
176             while (!linkTasks.empty())
177             {
178                 for (unsigned int i = 0; i < linkTasks.size();)
179                 {
180                     auto &task = linkTasks[i];
181 
182                     if (task->isLinkCompleted())
183                     {
184                         task->runAndVerify(test);
185                         linkTasks.erase(linkTasks.begin() + i);
186                         continue;
187                     }
188                     else
189                     {
190                         task->postLink();
191                     }
192                     ++i;
193                 }
194                 if (pollInterval != 0)
195                 {
196                     angle::Sleep(pollInterval);
197                 }
198             }
199         }
200     };
201 
202     class ClearColorWithDraw : public Task
203     {
204       public:
ClearColorWithDraw(int taskID)205         ClearColorWithDraw(int taskID) : Task(taskID)
206         {
207             auto color = static_cast<GLubyte>(taskID * 255 / kTaskCount);
208             mColor     = {color, color, color, 255};
209         }
210 
compile()211         bool compile() override
212         {
213             mVertexShader =
214                 CompileShader(GL_VERTEX_SHADER, InsertRandomString(essl1_shaders::vs::Simple()));
215             mFragmentShader = CompileShader(GL_FRAGMENT_SHADER,
216                                             InsertRandomString(essl1_shaders::fs::UniformColor()));
217             return (mVertexShader != 0 && mFragmentShader != 0);
218         }
219 
isCompileCompleted()220         bool isCompileCompleted() override
221         {
222             GLint status;
223             glGetShaderiv(mVertexShader, GL_COMPLETION_STATUS_KHR, &status);
224             if (status == GL_TRUE)
225             {
226                 glGetShaderiv(mFragmentShader, GL_COMPLETION_STATUS_KHR, &status);
227                 return (status == GL_TRUE);
228             }
229             return false;
230         }
231 
link()232         bool link() override
233         {
234             mProgram = 0;
235             if (CheckShader(mVertexShader) && CheckShader(mFragmentShader))
236             {
237                 mProgram = glCreateProgram();
238                 glAttachShader(mProgram, mVertexShader);
239                 glAttachShader(mProgram, mFragmentShader);
240                 glLinkProgram(mProgram);
241             }
242             glDeleteShader(mVertexShader);
243             glDeleteShader(mFragmentShader);
244             return (mProgram != 0);
245         }
246 
runAndVerify(ParallelShaderCompileTest * test)247         void runAndVerify(ParallelShaderCompileTest *test) override
248         {
249             glClearColor(0, 0, 0, 0);
250             glClear(GL_COLOR_BUFFER_BIT);
251             glDisable(GL_DEPTH_TEST);
252             glUseProgram(mProgram);
253             ASSERT_GL_NO_ERROR();
254             GLint colorUniformLocation =
255                 glGetUniformLocation(mProgram, essl1_shaders::ColorUniform());
256             ASSERT_NE(colorUniformLocation, -1);
257             auto normalizeColor = mColor.toNormalizedVector();
258             glUniform4fv(colorUniformLocation, 1, normalizeColor.data());
259             test->drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
260             EXPECT_PIXEL_COLOR_EQ(test->getWindowWidth() / 2, test->getWindowHeight() / 2, mColor);
261             glUseProgram(0);
262             glDeleteProgram(mProgram);
263             ASSERT_GL_NO_ERROR();
264         }
265 
266       protected:
recompile()267         void recompile()
268         {
269             RecompileShader(mVertexShader, essl1_shaders::vs::Simple());
270             RecompileShader(mFragmentShader, essl1_shaders::fs::UniformColor());
271         }
272 
273       private:
274         GLuint mVertexShader;
275         GLuint mFragmentShader;
276         GLColor mColor;
277     };
278 
279     class ClearColorWithDrawRecompile : public ClearColorWithDraw
280     {
281       public:
ClearColorWithDrawRecompile(int taskID)282         ClearColorWithDrawRecompile(int taskID) : ClearColorWithDraw(taskID) {}
283 
postLink()284         void postLink() override { recompile(); }
285     };
286 
287     class ImageLoadStore : public Task
288     {
289       public:
ImageLoadStore(int taskID)290         ImageLoadStore(int taskID) : Task(taskID) {}
~ImageLoadStore()291         ~ImageLoadStore() {}
292 
compile()293         bool compile() override
294         {
295             const char kCSSource[] = R"(#version 310 es
296 layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
297 layout(r32ui, binding = 0) readonly uniform highp uimage2D uImage_1;
298 layout(r32ui, binding = 1) writeonly uniform highp uimage2D uImage_2;
299 void main()
300 {
301     uvec4 value = imageLoad(uImage_1, ivec2(gl_LocalInvocationID.xy));
302     imageStore(uImage_2, ivec2(gl_LocalInvocationID.xy), value);
303 })";
304 
305             mShader = CompileShader(GL_COMPUTE_SHADER, InsertRandomString(kCSSource));
306             return mShader != 0;
307         }
308 
isCompileCompleted()309         bool isCompileCompleted() override
310         {
311             GLint status;
312             glGetShaderiv(mShader, GL_COMPLETION_STATUS_KHR, &status);
313             return status == GL_TRUE;
314         }
315 
link()316         bool link() override
317         {
318             mProgram = 0;
319             if (CheckShader(mShader))
320             {
321                 mProgram = glCreateProgram();
322                 glAttachShader(mProgram, mShader);
323                 glLinkProgram(mProgram);
324             }
325             glDeleteShader(mShader);
326             return mProgram != 0;
327         }
328 
runAndVerify(ParallelShaderCompileTest * test)329         void runAndVerify(ParallelShaderCompileTest *test) override
330         {
331             // Taken from ComputeShaderTest.StoreImageThenLoad.
332             constexpr GLuint kInputValues[3][1] = {{300}, {200}, {100}};
333             GLTexture texture[3];
334             glBindTexture(GL_TEXTURE_2D, texture[0]);
335             glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, 1, 1);
336             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT,
337                             kInputValues[0]);
338             EXPECT_GL_NO_ERROR();
339 
340             glBindTexture(GL_TEXTURE_2D, texture[1]);
341             glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, 1, 1);
342             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT,
343                             kInputValues[1]);
344             EXPECT_GL_NO_ERROR();
345 
346             glBindTexture(GL_TEXTURE_2D, texture[2]);
347             glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, 1, 1);
348             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT,
349                             kInputValues[2]);
350             EXPECT_GL_NO_ERROR();
351 
352             glUseProgram(mProgram);
353 
354             glBindImageTexture(0, texture[0], 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI);
355             glBindImageTexture(1, texture[1], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32UI);
356 
357             glDispatchCompute(1, 1, 1);
358             glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
359             EXPECT_GL_NO_ERROR();
360 
361             glBindImageTexture(0, texture[1], 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI);
362             glBindImageTexture(1, texture[2], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32UI);
363 
364             glDispatchCompute(1, 1, 1);
365             glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT);
366             EXPECT_GL_NO_ERROR();
367 
368             GLuint outputValue;
369             GLFramebuffer framebuffer;
370             glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
371             glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
372                                    texture[2], 0);
373             glReadPixels(0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, &outputValue);
374             EXPECT_GL_NO_ERROR();
375 
376             EXPECT_EQ(300u, outputValue);
377 
378             glUseProgram(0);
379             glDeleteProgram(mProgram);
380             ASSERT_GL_NO_ERROR();
381         }
382 
383       private:
384         GLuint mShader;
385     };
386 };
387 
388 // Test basic functionality of GL_KHR_parallel_shader_compile
TEST_P(ParallelShaderCompileTest,Basic)389 TEST_P(ParallelShaderCompileTest, Basic)
390 {
391     ANGLE_SKIP_TEST_IF(!ensureParallelShaderCompileExtensionAvailable());
392 
393     GLint count = 0;
394     glMaxShaderCompilerThreadsKHR(8);
395     EXPECT_GL_NO_ERROR();
396     glGetIntegerv(GL_MAX_SHADER_COMPILER_THREADS_KHR, &count);
397     EXPECT_GL_NO_ERROR();
398     EXPECT_EQ(8, count);
399 }
400 
401 // Test to compile and link many programs in parallel.
TEST_P(ParallelShaderCompileTest,LinkAndDrawManyPrograms)402 TEST_P(ParallelShaderCompileTest, LinkAndDrawManyPrograms)
403 {
404     ANGLE_SKIP_TEST_IF(!ensureParallelShaderCompileExtensionAvailable());
405 
406     TaskRunner<ClearColorWithDraw> runner;
407     runner.run(this, kPollInterval);
408 }
409 
410 // Tests no crash in case that the Shader starts another compile while the Program being attached
411 // to is still linking.
412 // crbug.com/1317673
TEST_P(ParallelShaderCompileTest,LinkProgramAndRecompileShader)413 TEST_P(ParallelShaderCompileTest, LinkProgramAndRecompileShader)
414 {
415     ANGLE_SKIP_TEST_IF(!ensureParallelShaderCompileExtensionAvailable());
416 
417     TaskRunner<ClearColorWithDrawRecompile> runner;
418     runner.run(this, 0);
419 }
420 
421 class ParallelShaderCompileTestES31 : public ParallelShaderCompileTest
422 {};
423 
424 // Test to compile and link many computing programs in parallel.
TEST_P(ParallelShaderCompileTestES31,LinkAndDispatchManyPrograms)425 TEST_P(ParallelShaderCompileTestES31, LinkAndDispatchManyPrograms)
426 {
427     // Flaky on Win NVIDIA D3D11. http://anglebug.com/3359
428     // Suspectable to the flakyness of http://anglebug.com/3349.
429     ANGLE_SKIP_TEST_IF(IsWindows() && IsD3D11());
430 
431     // TODO(http://anglebug.com/5656): Fails on Linux+Intel+OpenGL
432     ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsOpenGL());
433 
434     ANGLE_SKIP_TEST_IF(!ensureParallelShaderCompileExtensionAvailable());
435 
436     TaskRunner<ImageLoadStore> runner;
437     runner.run(this, kPollInterval);
438 }
439 
440 ANGLE_INSTANTIATE_TEST_ES2(ParallelShaderCompileTest);
441 
442 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ParallelShaderCompileTestES31);
443 ANGLE_INSTANTIATE_TEST_ES31(ParallelShaderCompileTestES31);
444 
445 }  // namespace
446