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