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