• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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