1 //
2 // Copyright 2017 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 // ProgramPipelineTest:
7 // Various tests related to Program Pipeline.
8 //
9
10 #include "test_utils/ANGLETest.h"
11 #include "test_utils/gl_raii.h"
12
13 using namespace angle;
14
15 namespace
16 {
17
18 class ProgramPipelineTest : public ANGLETest
19 {
20 protected:
ProgramPipelineTest()21 ProgramPipelineTest()
22 {
23 setWindowWidth(64);
24 setWindowHeight(64);
25 setConfigRedBits(8);
26 setConfigGreenBits(8);
27 setConfigBlueBits(8);
28 setConfigAlphaBits(8);
29 }
30 };
31
32 // Verify that program pipeline is not supported in version lower than ES31.
TEST_P(ProgramPipelineTest,GenerateProgramPipelineObject)33 TEST_P(ProgramPipelineTest, GenerateProgramPipelineObject)
34 {
35 ANGLE_SKIP_TEST_IF(!IsVulkan());
36
37 GLuint pipeline;
38 glGenProgramPipelines(1, &pipeline);
39 if (getClientMajorVersion() < 3 || getClientMinorVersion() < 1)
40 {
41 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
42 }
43 else
44 {
45 EXPECT_GL_NO_ERROR();
46
47 glDeleteProgramPipelines(1, &pipeline);
48 EXPECT_GL_NO_ERROR();
49 }
50 }
51
52 class ProgramPipelineTest31 : public ProgramPipelineTest
53 {
54 protected:
~ProgramPipelineTest31()55 ~ProgramPipelineTest31()
56 {
57 glDeleteProgram(mVertProg);
58 glDeleteProgram(mFragProg);
59 glDeleteProgramPipelines(1, &mPipeline);
60 }
61
62 void bindProgramPipeline(const GLchar *vertString, const GLchar *fragString);
63 void drawQuad(const std::string &positionAttribName,
64 const GLfloat positionAttribZ,
65 const GLfloat positionAttribXYScale);
66
67 GLuint mVertProg;
68 GLuint mFragProg;
69 GLuint mPipeline;
70 };
71
bindProgramPipeline(const GLchar * vertString,const GLchar * fragString)72 void ProgramPipelineTest31::bindProgramPipeline(const GLchar *vertString, const GLchar *fragString)
73 {
74 mVertProg = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &vertString);
75 ASSERT_NE(mVertProg, 0u);
76 mFragProg = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragString);
77 ASSERT_NE(mFragProg, 0u);
78
79 // Generate a program pipeline and attach the programs to their respective stages
80 glGenProgramPipelines(1, &mPipeline);
81 EXPECT_GL_NO_ERROR();
82 glUseProgramStages(mPipeline, GL_VERTEX_SHADER_BIT, mVertProg);
83 EXPECT_GL_NO_ERROR();
84 glUseProgramStages(mPipeline, GL_FRAGMENT_SHADER_BIT, mFragProg);
85 EXPECT_GL_NO_ERROR();
86 glBindProgramPipeline(mPipeline);
87 EXPECT_GL_NO_ERROR();
88 }
89
90 // Test generate or delete program pipeline.
TEST_P(ProgramPipelineTest31,GenOrDeleteProgramPipelineTest)91 TEST_P(ProgramPipelineTest31, GenOrDeleteProgramPipelineTest)
92 {
93 ANGLE_SKIP_TEST_IF(!IsVulkan());
94
95 GLuint pipeline;
96 glGenProgramPipelines(-1, &pipeline);
97 EXPECT_GL_ERROR(GL_INVALID_VALUE);
98 glGenProgramPipelines(0, &pipeline);
99 EXPECT_GL_NO_ERROR();
100
101 glDeleteProgramPipelines(-1, &pipeline);
102 EXPECT_GL_ERROR(GL_INVALID_VALUE);
103 glDeleteProgramPipelines(0, &pipeline);
104 EXPECT_GL_NO_ERROR();
105 }
106
107 // Test BindProgramPipeline.
TEST_P(ProgramPipelineTest31,BindProgramPipelineTest)108 TEST_P(ProgramPipelineTest31, BindProgramPipelineTest)
109 {
110 ANGLE_SKIP_TEST_IF(!IsVulkan());
111
112 glBindProgramPipeline(0);
113 EXPECT_GL_NO_ERROR();
114
115 glBindProgramPipeline(2);
116 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
117
118 GLuint pipeline;
119 glGenProgramPipelines(1, &pipeline);
120 glBindProgramPipeline(pipeline);
121 EXPECT_GL_NO_ERROR();
122
123 glDeleteProgramPipelines(1, &pipeline);
124 glBindProgramPipeline(pipeline);
125 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
126 }
127
128 // Test IsProgramPipeline
TEST_P(ProgramPipelineTest31,IsProgramPipelineTest)129 TEST_P(ProgramPipelineTest31, IsProgramPipelineTest)
130 {
131 ANGLE_SKIP_TEST_IF(!IsVulkan());
132
133 EXPECT_GL_FALSE(glIsProgramPipeline(0));
134 EXPECT_GL_NO_ERROR();
135
136 EXPECT_GL_FALSE(glIsProgramPipeline(2));
137 EXPECT_GL_NO_ERROR();
138
139 GLuint pipeline;
140 glGenProgramPipelines(1, &pipeline);
141 glBindProgramPipeline(pipeline);
142 EXPECT_GL_TRUE(glIsProgramPipeline(pipeline));
143 EXPECT_GL_NO_ERROR();
144
145 glBindProgramPipeline(0);
146 glDeleteProgramPipelines(1, &pipeline);
147 EXPECT_GL_FALSE(glIsProgramPipeline(pipeline));
148 EXPECT_GL_NO_ERROR();
149 }
150
151 // Simulates a call to glCreateShaderProgramv()
createShaderProgram(GLenum type,const GLchar * shaderString)152 GLuint createShaderProgram(GLenum type, const GLchar *shaderString)
153 {
154 GLShader shader(type);
155 if (!shader.get())
156 {
157 return 0;
158 }
159
160 glShaderSource(shader, 1, &shaderString, nullptr);
161 glCompileShader(shader);
162
163 GLuint program = glCreateProgram();
164
165 if (program)
166 {
167 GLint compiled;
168 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
169 glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);
170 if (compiled)
171 {
172 glAttachShader(program, shader);
173 glLinkProgram(program);
174 glDetachShader(program, shader);
175 }
176 }
177
178 EXPECT_GL_NO_ERROR();
179
180 return program;
181 }
182
drawQuad(const std::string & positionAttribName,const GLfloat positionAttribZ,const GLfloat positionAttribXYScale)183 void ProgramPipelineTest31::drawQuad(const std::string &positionAttribName,
184 const GLfloat positionAttribZ,
185 const GLfloat positionAttribXYScale)
186 {
187 glUseProgram(0);
188
189 std::array<Vector3, 6> quadVertices = ANGLETestBase::GetQuadVertices();
190
191 for (Vector3 &vertex : quadVertices)
192 {
193 vertex.x() *= positionAttribXYScale;
194 vertex.y() *= positionAttribXYScale;
195 vertex.z() = positionAttribZ;
196 }
197
198 GLint positionLocation = glGetAttribLocation(mVertProg, positionAttribName.c_str());
199
200 glBindBuffer(GL_ARRAY_BUFFER, 0);
201 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data());
202 glEnableVertexAttribArray(positionLocation);
203
204 glDrawArrays(GL_TRIANGLES, 0, 6);
205
206 glDisableVertexAttribArray(positionLocation);
207 glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
208 }
209
210 // Test glUseProgramStages
TEST_P(ProgramPipelineTest31,UseProgramStages)211 TEST_P(ProgramPipelineTest31, UseProgramStages)
212 {
213 ANGLE_SKIP_TEST_IF(!IsVulkan());
214
215 // Create two separable program objects from a
216 // single source string respectively (vertSrc and fragSrc)
217 const GLchar *vertString = essl31_shaders::vs::Simple();
218 const GLchar *fragString = essl31_shaders::fs::Red();
219
220 mVertProg = createShaderProgram(GL_VERTEX_SHADER, vertString);
221 ASSERT_NE(mVertProg, 0u);
222 mFragProg = createShaderProgram(GL_FRAGMENT_SHADER, fragString);
223 ASSERT_NE(mFragProg, 0u);
224
225 // Generate a program pipeline and attach the programs to their respective stages
226 GLuint pipeline;
227 glGenProgramPipelines(1, &pipeline);
228 EXPECT_GL_NO_ERROR();
229 glUseProgramStages(pipeline, GL_VERTEX_SHADER_BIT, mVertProg);
230 EXPECT_GL_NO_ERROR();
231 glUseProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, mFragProg);
232 EXPECT_GL_NO_ERROR();
233 glBindProgramPipeline(pipeline);
234 EXPECT_GL_NO_ERROR();
235
236 ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
237 ASSERT_GL_NO_ERROR();
238 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
239 }
240
241 // Test glUseProgramStages
TEST_P(ProgramPipelineTest31,UseCreateShaderProgramv)242 TEST_P(ProgramPipelineTest31, UseCreateShaderProgramv)
243 {
244 ANGLE_SKIP_TEST_IF(!IsVulkan());
245
246 // Create two separable program objects from a
247 // single source string respectively (vertSrc and fragSrc)
248 const GLchar *vertString = essl31_shaders::vs::Simple();
249 const GLchar *fragString = essl31_shaders::fs::Red();
250
251 bindProgramPipeline(vertString, fragString);
252
253 ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
254 ASSERT_GL_NO_ERROR();
255 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
256 }
257
258 // Test glUniform
TEST_P(ProgramPipelineTest31,FragmentStageUniformTest)259 TEST_P(ProgramPipelineTest31, FragmentStageUniformTest)
260 {
261 ANGLE_SKIP_TEST_IF(!IsVulkan());
262
263 // Create two separable program objects from a
264 // single source string respectively (vertSrc and fragSrc)
265 const GLchar *vertString = essl31_shaders::vs::Simple();
266 const GLchar *fragString = R"(#version 310 es
267 precision highp float;
268 uniform float redColorIn;
269 uniform float greenColorIn;
270 out vec4 my_FragColor;
271 void main()
272 {
273 my_FragColor = vec4(redColorIn, greenColorIn, 0.0, 1.0);
274 })";
275
276 bindProgramPipeline(vertString, fragString);
277
278 // Set the output color to yellow
279 GLint location = glGetUniformLocation(mFragProg, "redColorIn");
280 glActiveShaderProgram(mPipeline, mFragProg);
281 glUniform1f(location, 1.0);
282 location = glGetUniformLocation(mFragProg, "greenColorIn");
283 glActiveShaderProgram(mPipeline, mFragProg);
284 glUniform1f(location, 1.0);
285
286 ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
287 ASSERT_GL_NO_ERROR();
288 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
289
290 glClearColor(0.0, 0.0, 0.0, 0.0);
291 glClear(GL_COLOR_BUFFER_BIT);
292
293 ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
294 ASSERT_GL_NO_ERROR();
295 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
296
297 glClearColor(0.0, 0.0, 0.0, 0.0);
298 glClear(GL_COLOR_BUFFER_BIT);
299
300 // Set the output color to red
301 location = glGetUniformLocation(mFragProg, "redColorIn");
302 glActiveShaderProgram(mPipeline, mFragProg);
303 glUniform1f(location, 1.0);
304 location = glGetUniformLocation(mFragProg, "greenColorIn");
305 glActiveShaderProgram(mPipeline, mFragProg);
306 glUniform1f(location, 0.0);
307
308 ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
309 ASSERT_GL_NO_ERROR();
310 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
311
312 glDeleteProgram(mVertProg);
313 glDeleteProgram(mFragProg);
314 }
315
316 // Test varyings
TEST_P(ProgramPipelineTest31,ProgramPipelineVaryings)317 TEST_P(ProgramPipelineTest31, ProgramPipelineVaryings)
318 {
319 ANGLE_SKIP_TEST_IF(!IsVulkan());
320
321 // Create two separable program objects from a
322 // single source string respectively (vertSrc and fragSrc)
323 const GLchar *vertString = essl31_shaders::vs::Passthrough();
324 const GLchar *fragString = R"(#version 310 es
325 precision highp float;
326 in vec4 v_position;
327 out vec4 my_FragColor;
328 void main()
329 {
330 my_FragColor = round(v_position);
331 })";
332
333 bindProgramPipeline(vertString, fragString);
334
335 ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
336 ASSERT_GL_NO_ERROR();
337
338 int w = getWindowWidth() - 2;
339 int h = getWindowHeight() - 2;
340
341 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::black);
342 EXPECT_PIXEL_COLOR_EQ(w, 0, GLColor::red);
343 EXPECT_PIXEL_COLOR_EQ(0, h, GLColor::green);
344 EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::yellow);
345 }
346
347 // Creates a program pipeline with a 2D texture and renders with it.
TEST_P(ProgramPipelineTest31,DrawWith2DTexture)348 TEST_P(ProgramPipelineTest31, DrawWith2DTexture)
349 {
350 ANGLE_SKIP_TEST_IF(!IsVulkan());
351
352 const GLchar *vertString = R"(#version 310 es
353 precision highp float;
354 in vec4 a_position;
355 out vec2 texCoord;
356 void main()
357 {
358 gl_Position = a_position;
359 texCoord = vec2(a_position.x, a_position.y) * 0.5 + vec2(0.5);
360 })";
361
362 const GLchar *fragString = R"(#version 310 es
363 precision highp float;
364 in vec2 texCoord;
365 uniform sampler2D tex;
366 out vec4 my_FragColor;
367 void main()
368 {
369 my_FragColor = texture(tex, texCoord);
370 })";
371
372 std::array<GLColor, 4> colors = {
373 {GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow}};
374
375 GLTexture tex;
376 glBindTexture(GL_TEXTURE_2D, tex);
377 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors.data());
378 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
379 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
380
381 bindProgramPipeline(vertString, fragString);
382
383 ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
384 ASSERT_GL_NO_ERROR();
385
386 int w = getWindowWidth() - 2;
387 int h = getWindowHeight() - 2;
388
389 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
390 EXPECT_PIXEL_COLOR_EQ(w, 0, GLColor::green);
391 EXPECT_PIXEL_COLOR_EQ(0, h, GLColor::blue);
392 EXPECT_PIXEL_COLOR_EQ(w, h, GLColor::yellow);
393 }
394
395 // Test modifying a shader after it has been detached from a pipeline
TEST_P(ProgramPipelineTest31,DetachAndModifyShader)396 TEST_P(ProgramPipelineTest31, DetachAndModifyShader)
397 {
398 ANGLE_SKIP_TEST_IF(!IsVulkan());
399
400 // TODO (timvp): Fix this test for Vulkan with PPO
401 // http://anglebug.com/3570
402 ANGLE_SKIP_TEST_IF(IsVulkan());
403
404 const GLchar *vertString = essl31_shaders::vs::Simple();
405 const GLchar *fragString = essl31_shaders::fs::Green();
406
407 GLShader vertShader(GL_VERTEX_SHADER);
408 GLShader fragShader(GL_FRAGMENT_SHADER);
409 mVertProg = glCreateProgram();
410 mFragProg = glCreateProgram();
411
412 // Compile and link a separable vertex shader
413 glShaderSource(vertShader, 1, &vertString, nullptr);
414 glCompileShader(vertShader);
415 glProgramParameteri(mVertProg, GL_PROGRAM_SEPARABLE, GL_TRUE);
416 glAttachShader(mVertProg, vertShader);
417 glLinkProgram(mVertProg);
418 EXPECT_GL_NO_ERROR();
419
420 // Compile and link a separable fragment shader
421 glShaderSource(fragShader, 1, &fragString, nullptr);
422 glCompileShader(fragShader);
423 glProgramParameteri(mFragProg, GL_PROGRAM_SEPARABLE, GL_TRUE);
424 glAttachShader(mFragProg, fragShader);
425 glLinkProgram(mFragProg);
426 EXPECT_GL_NO_ERROR();
427
428 // Generate a program pipeline and attach the programs
429 glGenProgramPipelines(1, &mPipeline);
430 glUseProgramStages(mPipeline, GL_VERTEX_SHADER_BIT, mVertProg);
431 glUseProgramStages(mPipeline, GL_FRAGMENT_SHADER_BIT, mFragProg);
432 glBindProgramPipeline(mPipeline);
433 EXPECT_GL_NO_ERROR();
434
435 // Draw once to ensure this worked fine
436 ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
437 ASSERT_GL_NO_ERROR();
438 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
439
440 // Detach the fragment shader and modify it such that it no longer fits with this pipeline
441 glDetachShader(mFragProg, fragShader);
442
443 // Add an input to the fragment shader, which will make it incompatible
444 const GLchar *fragString2 = R"(#version 310 es
445 precision highp float;
446 in vec4 color;
447 out vec4 my_FragColor;
448 void main()
449 {
450 my_FragColor = color;
451 })";
452 glShaderSource(fragShader, 1, &fragString2, nullptr);
453 glCompileShader(fragShader);
454
455 // Link and draw with the program again, which should be fine since the shader was detached
456 glLinkProgram(mFragProg);
457
458 ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
459 ASSERT_GL_NO_ERROR();
460 }
461
462 // Test binding two programs that use a texture as different types
TEST_P(ProgramPipelineTest31,DifferentTextureTypes)463 TEST_P(ProgramPipelineTest31, DifferentTextureTypes)
464 {
465 // Only the Vulkan backend supports PPO
466 ANGLE_SKIP_TEST_IF(!IsVulkan());
467
468 // TODO (timvp): Fix this test for Vulkan with PPO
469 // http://anglebug.com/3570
470 ANGLE_SKIP_TEST_IF(IsVulkan());
471
472 // Per the OpenGL ES 3.1 spec:
473 //
474 // It is not allowed to have variables of different sampler types pointing to the same texture
475 // image unit within a program object. This situation can only be detected at the next rendering
476 // command issued which triggers shader invocations, and an INVALID_OPERATION error will then
477 // be generated
478 //
479
480 // Create a vertex shader that uses the texture as 2D
481 const GLchar *vertString = R"(#version 310 es
482 precision highp float;
483 in vec4 a_position;
484 uniform sampler2D tex2D;
485 layout(location = 0) out vec4 texColorOut;
486 layout(location = 1) out vec2 texCoordOut;
487 void main()
488 {
489 gl_Position = a_position;
490 vec2 texCoord = vec2(a_position.x, a_position.y) * 0.5 + vec2(0.5);
491 texColorOut = textureLod(tex2D, texCoord, 0.0);
492 texCoordOut = texCoord;
493 })";
494
495 // Create a fragment shader that uses the texture as Cube
496 const GLchar *fragString = R"(#version 310 es
497 precision highp float;
498 layout(location = 0) in vec4 texColor;
499 layout(location = 1) in vec2 texCoord;
500 uniform samplerCube texCube;
501 out vec4 my_FragColor;
502 void main()
503 {
504 my_FragColor = texture(texCube, vec3(texCoord.x, texCoord.y, 0.0));
505 })";
506
507 // Create and populate the 2D texture
508 std::array<GLColor, 4> colors = {
509 {GLColor::red, GLColor::green, GLColor::blue, GLColor::yellow}};
510 GLTexture tex;
511 glBindTexture(GL_TEXTURE_2D, tex);
512 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colors.data());
513 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
514 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
515
516 // Create a pipeline that uses the bad combination. This should fail to link the pipeline.
517 bindProgramPipeline(vertString, fragString);
518 ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
519 ASSERT_GL_ERROR(GL_INVALID_OPERATION);
520
521 // Update the fragment shader to correctly use 2D texture
522 const GLchar *fragString2 = R"(#version 310 es
523 precision highp float;
524 layout(location = 0) in vec4 texColor;
525 layout(location = 1) in vec2 texCoord;
526 uniform sampler2D tex2D;
527 out vec4 my_FragColor;
528 void main()
529 {
530 my_FragColor = texture(tex2D, texCoord);
531 })";
532
533 // Bind the pipeline again, which should succeed.
534 bindProgramPipeline(vertString, fragString2);
535 ProgramPipelineTest31::drawQuad("a_position", 0.5f, 1.0f);
536 ASSERT_GL_NO_ERROR();
537 }
538
539 ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(ProgramPipelineTest);
540 ANGLE_INSTANTIATE_TEST_ES31(ProgramPipelineTest31);
541
542 } // namespace
543