<div dir="ltr">On 1 October 2013 18:22, Ian Romanick <span dir="ltr"><<a href="mailto:idr@freedesktop.org" target="_blank">idr@freedesktop.org</a>></span> wrote:<br><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
From: Gregory Hainaut <<a href="mailto:gregory.hainaut@gmail.com">gregory.hainaut@gmail.com</a>><br>
<br>
the goal is to test the state mix of glUseProgram /<br>
glBindProgramPipeline / glActiveProgram.<br>
<br>
Ian R. quote:<br>
    "In this case, either the UseProgram state or the<br>
    BindProgramPipeline state.  If UseProgram sets a non-zero program,<br>
    that state is used.  Otherwise the BindProgramPipeline state is<br>
    used.....In this case, I think AMD's behavior is incorrect."<br></blockquote><div><br></div><div>Last time I reviewed this patch, I asked for additional context on this quote.  Can you put the additional context into the commit message so that in the future people don't have to go to the mailing list archives to understand the quote?  Thanks.<br>
</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
Note: Nvidia seems to be fine.<br>
<br>
V4:<br>
* split into a separate commit<br>
* Merge the 2 vertex shaders with the help of the __VERSION__ macro<br>
* Properly set shader version with asprintf<br>
* Use standard bool<br>
* Split test into subtest<br>
* Fix expected of GL_PROGRAM_PIPELINE_BINDING (based on nvidia behavior)<br>
<br>
V5 (idr):<br>
<br>
* Expect GL_PROGRAM_PIPELINE_BINDING to still be pipe when a<br>
  non-separable program is also bound (via glUseProgram).  After<br>
  discussions in Khronos, that was determined to be the correct<br>
  behavior.  Fix NVIDIA drivers should be available soon.<br>
* Don't test glUseProgramStages with a non-separable program.  There is<br>
  a separate test for that now.<br>
* Use 'pass = foo() && pass;' idiom instead of 'pass &= foo();'.  C's<br>
  short-circuit evaulation rules cause these to be different.<br>
* Use piglit_build_simple_program for non-separable shader program.<br>
* #extension is only necessary to use layout qualifiers, so remove it.<br>
* s/GLboolean/bool/g<br>
* Produce no output with -auto.<br>
* Use descriptive names for subtests.<br>
* Add test to CMakeLists.gl.txt.<br>
* Trivial reformatting.<br>
<br>
V6 (idr): Major refactor / rewrite based on feedback from Paul.<br>
* Each subtest is moved to its own function with its own "pass" tracking.<br>
* Each subtest restored program, pipeline, and uniform state before<br>
  ending.  Tests also do not depend on state left over from previous<br>
  tests.<br>
* The bind_program and bind_pipeline functions are eliminated.<br>
* s/BindPipeline/BindProgramPipeline/g<br>
* Paul also suggested moving piglit_init earlier in the file.  This<br>
  has not been done yet because I wasn't sure where to put it.  First?<br>
  After the utility functions?  After the subtest functions?<br>
<br>
Note: vertex shader doesn't work with FGLRX. It would require GLSL150<br>
but that mean you can't use fixed pipeline anymore...<br>
<br>
Reviewed-by: Ian Romanick <<a href="mailto:ian.d.romanick@intel.com">ian.d.romanick@intel.com</a>><br>
---<br>
 tests/all.tests                                    |   1 +<br>
 .../arb_separate_shader_objects/CMakeLists.gl.txt  |   1 +<br>
 .../mix_pipeline_useprogram.c                      | 415 +++++++++++++++++++++<br>
 3 files changed, 417 insertions(+)<br>
 create mode 100644 tests/spec/arb_separate_shader_objects/mix_pipeline_useprogram.c<br>
<br>
diff --git a/tests/all.tests b/tests/all.tests<br>
index ee6219c..e0cdc8b 100644<br>
--- a/tests/all.tests<br>
+++ b/tests/all.tests<br>
@@ -1275,6 +1275,7 @@ spec['ARB_separate_shader_objects'] = arb_separate_shader_objects<br>
 arb_separate_shader_objects['GetProgramPipelineiv'] = concurrent_test('arb_separate_shader_object-GetProgramPipelineiv')<br>
 arb_separate_shader_objects['IsProgramPipeline'] = concurrent_test('arb_separate_shader_object-IsProgramPipeline')<br>
 arb_separate_shader_objects['UseProgramStages - non-separable program'] = concurrent_test('arb_separate_shader_object-UseProgramStages-non-separable')<br>
+arb_separate_shader_objects['Mix BindProgramPipeline and UseProgram'] = concurrent_test('arb_separate_shader_object-mix_pipeline_useprogram')<br>
 arb_separate_shader_objects['ProgramUniform coverage'] = concurrent_test('arb_separate_shader_object-ProgramUniform-coverage')<br>
 arb_separate_shader_objects['ValidateProgramPipeline'] = concurrent_test('arb_separate_shader_object-ValidateProgramPipeline')<br>
<br>
diff --git a/tests/spec/arb_separate_shader_objects/CMakeLists.gl.txt b/tests/spec/arb_separate_shader_objects/CMakeLists.gl.txt<br>
index d78f270..0ed3e2f 100644<br>
--- a/tests/spec/arb_separate_shader_objects/CMakeLists.gl.txt<br>
+++ b/tests/spec/arb_separate_shader_objects/CMakeLists.gl.txt<br>
@@ -11,6 +11,7 @@ link_libraries (<br>
<br>
 piglit_add_executable (arb_separate_shader_object-GetProgramPipelineiv GetProgramPipelineiv.c)<br>
 piglit_add_executable (arb_separate_shader_object-IsProgramPipeline IsProgramPipeline.c)<br>
+piglit_add_executable (arb_separate_shader_object-mix_pipeline_useprogram mix_pipeline_useprogram.c)<br>
 piglit_add_executable (arb_separate_shader_object-ProgramUniform-coverage ProgramUniform-coverage.c)<br>
 piglit_add_executable (arb_separate_shader_object-UseProgramStages-non-separable UseProgramStages-non-separable.c)<br>
 piglit_add_executable (arb_separate_shader_object-ValidateProgramPipeline ValidateProgramPipeline.c)<br>
diff --git a/tests/spec/arb_separate_shader_objects/mix_pipeline_useprogram.c b/tests/spec/arb_separate_shader_objects/mix_pipeline_useprogram.c<br>
new file mode 100644<br>
index 0000000..7c7fb63<br>
--- /dev/null<br>
+++ b/tests/spec/arb_separate_shader_objects/mix_pipeline_useprogram.c<br>
@@ -0,0 +1,415 @@<br>
+/*<br>
+ * Copyright © 2013 Gregory Hainaut <<a href="mailto:gregory.hainaut@gmail.com">gregory.hainaut@gmail.com</a>><br>
+ *<br>
+ * Permission is hereby granted, free of charge, to any person obtaining a<br>
+ * copy of this software and associated documentation files (the "Software"),<br>
+ * to deal in the Software without restriction, including without limitation<br>
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,<br>
+ * and/or sell copies of the Software, and to permit persons to whom the<br>
+ * Software is furnished to do so, subject to the following conditions:<br>
+ *<br>
+ * The above copyright notice and this permission notice (including the next<br>
+ * paragraph) shall be included in all copies or substantial portions of the<br>
+ * Software.<br>
+ *<br>
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR<br>
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,<br>
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL<br>
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER<br>
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING<br>
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS<br>
+ * IN THE SOFTWARE.<br>
+ */<br>
+<br>
+/**<br>
+ * \file mix_pipeline_useprogram.<br>
+ * Test mixing separable and non-separable programs in various ways.<br>
+ *<br>
+ * Section 2.11.3 (Program Objects) of the OpenGL 4.1 spec says:<br>
+ *<br>
+ *     "The executable code for an individual shader stage is taken from the<br>
+ *     current program for that stage. If there is a current program object<br>
+ *     established by UseProgram, that program is considered current for all<br>
+ *     stages. Otherwise, if there is a bound program pipeline object (see<br>
+ *     section 2.11.4), the program bound to the appropriate stage of the<br>
+ *     pipeline object is considered current. If there is no current program<br>
+ *     object or bound program pipeline object, no program is current for any<br>
+ *     stage."<br>
+<br>
+ * Section 2.11.4 (Program Pipeline Objects) of the OpenGL 4.1 spec says:<br>
+ *<br>
+ *     "If no current program object has been established by UseProgram, the<br>
+ *     program objects used for each shader stage and for uniform updates are<br>
+ *     taken from the bound program pipeline object, if any. If there is a<br>
+ *     current program object established by UseProgram, the bound program<br>
+ *     pipeline object has no effect on rendering or uniform updates. When a<br>
+ *     bound program pipeline object is used for rendering, individual shader<br>
+ *     executables are taken from its program objects as described in the<br>
+ *     discussion of UseProgram in section 2.11.3)."<br>
+ *<br>
+ * Section 2.11.7 (Uniform Variables) of the OpenGL 4.1 spec says:<br>
+ *<br>
+ *     "If a non-zero program object is bound by UseProgram, it is the active<br>
+ *     program object whose uniforms are updated by these commands. If no<br>
+ *     program object is bound using UseProgram, the active program object of<br>
+ *     the current program pipeline object set by ActiveShaderProgram is the<br>
+ *     active program object. If the current program pipeline object has no<br>
+ *     active program or there is no current program pipeline object, then<br>
+ *     there is no active program."<br>
+ *<br>
+ * Note that with these rules, it's not possible to mix program objects bound<br>
+ * to the context with program objects bound to a program pipeline object; if<br>
+ * any program is bound to the context, the current pipeline object is<br>
+ * ignored.<br>
+ */<br>
+#include "piglit-util-gl-common.h"<br>
+<br>
+PIGLIT_GL_TEST_CONFIG_BEGIN<br>
+<br>
+       /* Require OpenGL 2.0+, but do *not* allow a core profile.  The test<br>
+        * uses GLSL 1.10, and GLSL less than 1.30 is deprecated.  In<br>
+        * addition, the test tries to use fixed-function drawing.<br>
+        */<br>
+       config.supports_gl_compat_version = 20;<br>
+<br>
+       config.window_width = 32;<br>
+       config.window_height = 32;<br>
+<br>
+PIGLIT_GL_TEST_CONFIG_END<br>
+<br>
+static GLuint prog;<br>
+static GLuint pipe;<br>
+<br>
+static const float red[4] = {1.0, 0.0, 0.0, 1.0};<br>
+static const float violet[4] = {1.0, 0.0, 1.0, 1.0};<br>
+static const float green[4] = {0.0, 1.0, 0.0, 1.0};<br>
+static const float blue[4] = {0.0, 0.0, 1.0, 1.0};<br>
+static const float gb[4] = {0.0, 1.0, 1.0, 1.0};<br>
+<br>
+static bool<br>
+check_GetInteger_value(GLenum pname, GLint expected)<br>
+{<br>
+       bool pass = true;<br>
+       GLint value = 0;<br>
+       glGetIntegerv(pname, &value);<br>
+<br>
+       pass = piglit_check_gl_error(GL_NO_ERROR) && pass;<br>
+       if (value != expected) {<br>
+               fprintf(stderr, "Failed to get %s expected %d got %d\n",<br>
+                       piglit_get_gl_enum_name(pname), expected, value);<br>
+               return false;<br>
+       } else {<br>
+               return true;<br>
+       }<br>
+}<br>
+<br>
+static bool<br>
+restore_default_program_state(void)<br>
+{<br>
+       GLint active_shader_pipe;<br>
+<br>
+       glUseProgram(0);<br>
+       glBindProgramPipeline(0);<br>
+<br>
+       /* Reset uniform values to known state.<br>
+        */<br>
+       glProgramUniform1f(prog, 0, 0.0);<br>
+       glGetProgramPipelineiv(pipe, GL_ACTIVE_PROGRAM, &active_shader_pipe);<br>
+<br>
+       glProgramUniform1f(active_shader_pipe, 0, 0.0);<br></blockquote><div><br></div><div>Is ACTIVE_PROGRAM known to be nonzero at this point?  If not, the above call to glProgramUniform1f() will cause an INVALID_VALUE error.<br>
<br>It would be nice to have either a comment here explaining why ACTIVE_PROGRAM is known not to be zero, or a test:<br><br>if (active_shader_pipe != 0)<br></div><div>   glProgramUniform1f(...);<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">

+<br>
+       return piglit_check_gl_error(GL_NO_ERROR);<br>
+}<br>
+<br>
+static bool<br>
+draw(const float *expected)<br>
+{<br>
+       piglit_draw_rect(-1, -1, 2, 2);<br>
+       return piglit_probe_rect_rgba(0, 0, piglit_width, piglit_height,<br>
+                                     expected);<br>
+}<br>
+<br>
+static bool<br>
+set_and_check_uniform(GLint program, float expected)<br>
+{<br>
+       float value;<br>
+<br>
+       /* glUniform* uses either the currently bound program or the active<br>
+        * program of the currently bound pipeline.  glGetUniform* uses the<br>
+        * program expclitly passed to it.  THESE MAY BE DIFFERENT!<br>
+        */<br>
+       glUniform1f(0, expected);<br>
+       glGetUniformfv(program, 0, &value);<br>
+       if (value != expected) {<br>
+               fprintf(stderr,<br>
+                       "Failed to get uniform value of %d, expected %f, "<br>
+                       "got %f\n",<br>
+                       program, expected, value);<br>
+               return false;<br>
+       }<br>
+       return true;<br>
+}<br>
+<br>
+static bool<br>
+test_UseProgram_then_BindProgramPipeline(void)<br>
+{<br>
+       static const char subtest_name[] =<br>
+               "glUseProgram, then glBindProgramPipeline";<br>
+       bool subtest = true;<br>
+<br>
+       if (!piglit_automatic)<br>
+               printf("%s...\n", subtest_name);<br>
+<br>
+       glUseProgram(prog);<br>
+       subtest = set_and_check_uniform(prog, 1.0) && subtest;<br>
+<br>
+       /* Program rendering */<br>
+       subtest = draw(violet) && subtest;<br>
+<br>
+       glBindProgramPipeline(pipe);<br>
+<br>
+       /* It must ignore the pipeline */<br>
+       subtest = set_and_check_uniform(prog, 0.0) && subtest;<br>
+<br>
+       /* Still program rendering */<br>
+       subtest = draw(red) && subtest;<br>
+<br>
+       subtest = restore_default_program_state() && subtest;<br>
+       piglit_report_subtest_result(subtest ? PIGLIT_PASS : PIGLIT_FAIL,<br>
+                                    subtest_name);<br>
+       return subtest;<br>
+}<br>
+<br>
+static bool<br>
+test_BindProgramPipeline_without_UseProgram(void)<br>
+{<br>
+       static const char subtest_name[] =<br>
+               "glBindProgramPipeline without glUseProgram";<br>
+       bool subtest = true;<br>
+       GLint active_shader_pipe;<br>
+<br>
+       if (!piglit_automatic)<br>
+               printf("%s...\n", subtest_name);<br>
+<br>
+       glBindProgramPipeline(pipe);<br>
+       glGetProgramPipelineiv(pipe, GL_ACTIVE_PROGRAM, &active_shader_pipe);<br>
+       subtest = set_and_check_uniform(active_shader_pipe, 1.0) && subtest;<br>
+<br>
+       /* Pipeline rendering  */<br>
+       subtest = draw(green) && subtest;<br>
+<br>
+       subtest = restore_default_program_state() && subtest;<br>
+       piglit_report_subtest_result(subtest ? PIGLIT_PASS : PIGLIT_FAIL,<br>
+                                    subtest_name);<br>
+       return subtest;<br>
+}<br>
+<br>
+static bool<br>
+test_BindProgramPipeline_then_UseProgram(void)<br>
+{<br>
+       static const char subtest_name[] =<br>
+               "glBindProgramPipeline, then glUseProgram";<br>
+       bool subtest = true;<br>
+<br>
+       if (!piglit_automatic)<br>
+               printf("%s...\n", subtest_name);<br>
+<br>
+       glBindProgramPipeline(pipe);<br>
+       glUseProgram(prog);<br>
+<br>
+       /* UseProgram rendering */<br>
+       subtest = draw(red) && subtest;<br>
+<br>
+       subtest = restore_default_program_state() && subtest;<br>
+       piglit_report_subtest_result(subtest ? PIGLIT_PASS : PIGLIT_FAIL,<br>
+                                    subtest_name);<br>
+       return subtest;<br>
+}<br>
+<br>
+static bool<br>
+test_BindProgramPipeline_then_UseProgram_0(void)<br>
+{<br>
+       static const char subtest_name[] =<br>
+               "glBindProgramPipeline, then glUseProgram(0)";<br>
+       bool subtest = true;<br>
+<br>
+       if (!piglit_automatic)<br>
+               printf("%s...\n", subtest_name);<br>
+<br>
+       glBindProgramPipeline(pipe);<br>
+<br>
+       /* Pipeline rendering  */<br>
+       subtest = draw(gb) && subtest;<br>
+<br>
+       glUseProgram(prog);<br>
+       subtest = set_and_check_uniform(prog, 1.0) && subtest;<br>
+       glUseProgram(0);<br>
+<br>
+       /* Still pipeline rendering with the same value for the uniform.<br>
+        */<br>
+       subtest = draw(gb) && subtest;<br>
+<br>
+       subtest = restore_default_program_state() && subtest;<br>
+       piglit_report_subtest_result(subtest ? PIGLIT_PASS : PIGLIT_FAIL,<br>
+                                    subtest_name);<br>
+       return subtest;<br>
+}<br>
+<br>
+static bool<br>
+test_UseProgram_then_BindProgramPipeline_then_UseProgram_0(void)<br>
+{<br>
+       static const char subtest_name[] =<br>
+               "glUseProgram, then glBindProgramPipeline, then "<br>
+               "glUseProgram(0)";<br>
+       bool subtest = true;<br>
+       GLint active_shader_pipe;<br>
+<br>
+       if (!piglit_automatic)<br>
+               printf("%s...\n", subtest_name);<br>
+<br>
+       glGetProgramPipelineiv(pipe, GL_ACTIVE_PROGRAM, &active_shader_pipe);<br>
+<br>
+       glUseProgram(prog);<br>
+<br>
+       /* Program rendering  */<br>
+       subtest = draw(red) && subtest;<br>
+<br>
+       glBindProgramPipeline(pipe);<br>
+       subtest = set_and_check_uniform(prog, 1.0) && subtest;<br>
+<br>
+       /* Still program rendering  */<br>
+       subtest = draw(violet) && subtest;<br>
+<br>
+       glUseProgram(0);<br>
+       subtest = set_and_check_uniform(active_shader_pipe, 0.0) && subtest;<br>
+<br>
+       /* Pipeline rendering  */<br>
+       subtest = draw(gb) && subtest;<br>
+<br>
+       subtest = restore_default_program_state() && subtest;<br>
+       piglit_report_subtest_result(subtest ? PIGLIT_PASS : PIGLIT_FAIL,<br>
+                                    subtest_name);<br>
+       return subtest;<br>
+}<br>
+<br>
+static bool<br>
+test_final_sanity(void)<br>
+{<br>
+       static const char subtest_name[] = "Final sanity test";<br>
+       bool subtest = true;<br>
+<br>
+       if (!piglit_automatic)<br>
+               printf("%s...\n", subtest_name);<br>
+<br>
+       /* Fixed function rendering */<br>
+       subtest = draw(blue) && subtest;<br>
+<br>
+       piglit_report_subtest_result(subtest ? PIGLIT_PASS : PIGLIT_FAIL,<br>
+                                    subtest_name);<br>
+       return subtest;<br>
+}<br>
+<br>
+enum piglit_result<br>
+piglit_display(void)<br>
+{<br>
+       GLint active_shader_pipe;<br>
+       GLint uniform_loc_pipe;<br>
+       GLint uniform_loc_prog;<br>
+       bool pass = true;<br>
+<br>
+       glGetProgramPipelineiv(pipe, GL_ACTIVE_PROGRAM, &active_shader_pipe);<br>
+       uniform_loc_pipe = glGetUniformLocation(active_shader_pipe, "blue");<br>
+       uniform_loc_prog = glGetUniformLocation(prog, "blue");<br>
+       /* otherwise it is difficult which program is really updated */<br>
+       assert(uniform_loc_prog == 0);<br>
+       assert(uniform_loc_pipe == 0);<br></blockquote><div><br></div><div>It looks like this comment, from my previous review of this patch, never got addressed:<br><br>"This seems really dodgy to me.  My understanding is that OpenGL makes no
 guarantees as to where any particular uniform is located, even in the 
circumstance we have here where only one uniform exists.<br>

<br>IMHO we should write the <span class="">test</span> so that (a) if the "blue" uniform gets assigned the same nonzero location in both prog and active_shader_pipe, the <span class="">test</span>
 is still just as effective, and (b) if the "blue" uniform gets assigned
 different locations in prog and active_shader_pipe, the <span class="">test</span> still passes (but it's ok if it's a less mean <span class="">test</span>
 in this case).  I think all that would be required is to add a 
"location" argument to set_and_check_uniform, and pass either 
uniform_loc_prog or uniform_loc_pipe to it at each call site, as 
appropriate."<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+       /* Set up fixed function to draw blue if we lose our shader.  This<br>
+        * color is chosen because neither of the programs used elsewhere in<br>
+        * the test can generate blue as a color.<br>
+        */<br>
+       glColor4f(0.0, 0.0, 1.0, 1.0);<br>
+<br>
+       pass = test_UseProgram_then_BindProgramPipeline() && pass;<br>
+       pass = test_BindProgramPipeline_without_UseProgram() && pass;<br>
+       pass = test_BindProgramPipeline_then_UseProgram() && pass;<br>
+       pass = test_BindProgramPipeline_then_UseProgram_0() && pass;<br>
+       pass = test_UseProgram_then_BindProgramPipeline_then_UseProgram_0()<br>
+               && pass;<br>
+       pass = test_final_sanity() && pass;<br>
+<br>
+       piglit_present_results();<br>
+<br>
+       return pass ? PIGLIT_PASS : PIGLIT_FAIL;<br>
+}<br>
+<br>
+void<br>
+piglit_init(int argc, char **argv)<br>
+{<br></blockquote><div><br></div><div>Also, this comment, from my previous review, doesn't seem to have been addressed:<br><br>"Would you have any objection to moving piglit_init before piglit_display in this <span class="">test</span>? 
 A long time ago I started doing that in my piglit tests because I 
usually find it easier to follow the logic of piglit_dispaly once I've 
seen piglit_init.  That seems particularly true of this <span class="">test</span>, since the shader source code is contained in piglit_init."</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">

+       GLint vs_prog;<br>
+       GLint fs_prog;<br>
+       GLint vs, fs;<br>
+       const char vs_source[] =<br>
+               "#version 110\n"<br>
+               "void main()\n"<br>
+               "{\n"<br>
+               "       gl_Position = gl_Vertex;\n"<br>
+               "}\n";<br>
+       const char fs_source_r[] =<br>
+               "#version 110\n"<br>
+               "uniform float blue;\n"<br>
+               "void main()\n"<br>
+               "{\n"<br>
+               "       gl_FragColor = vec4(1.0, 0.0, blue, 1.0);\n"<br>
+               "}\n";<br>
+       const char fs_source_g[] =<br>
+               "#version 110\n"<br>
+               "uniform float blue;\n"<br>
+               "void main()\n"<br>
+               "{\n"<br>
+               "       gl_FragColor = vec4(0.0, 1.0, 1.0 - blue, 1.0);\n"<br>
+               "}\n";<br>
+       bool pass = true;<br>
+<br>
+       piglit_require_extension("GL_ARB_separate_shader_objects");<br>
+<br>
+       /* Standard program (ie not separate) */<br>
+       prog = piglit_build_simple_program(vs_source, fs_source_r);<br>
+<br>
+       /* Now create program for the pipeline program */<br>
+       vs = piglit_compile_shader_text(GL_VERTEX_SHADER, vs_source);<br>
+       fs = piglit_compile_shader_text(GL_FRAGMENT_SHADER, fs_source_g);<br>
+<br>
+       vs_prog = glCreateProgram();<br>
+       glProgramParameteri(vs_prog, GL_PROGRAM_SEPARABLE, GL_TRUE);<br>
+       glAttachShader(vs_prog, vs);<br>
+       glLinkProgram(vs_prog);<br>
+       pass = piglit_link_check_status(vs_prog) && pass;<br>
+       glDeleteShader(vs);<br>
+<br>
+       fs_prog = glCreateProgram();<br>
+       glProgramParameteri(fs_prog, GL_PROGRAM_SEPARABLE, GL_TRUE);<br>
+       glAttachShader(fs_prog, fs);<br>
+       glLinkProgram(fs_prog);<br>
+       pass = piglit_link_check_status(fs_prog) && pass;<br>
+       glDeleteShader(fs);<br>
+<br>
+       glGenProgramPipelines(1, &pipe);<br>
+       glUseProgramStages(pipe, GL_VERTEX_SHADER_BIT, vs_prog);<br>
+       glUseProgramStages(pipe, GL_FRAGMENT_SHADER_BIT, fs_prog);<br>
+       glActiveShaderProgram(pipe, fs_prog);<br>
+       pass = piglit_program_pipeline_check_status(pipe) && pass;<br>
+<br>
+       /* Sanity check initial state.<br>
+        */<br>
+       pass = check_GetInteger_value(GL_PROGRAM_PIPELINE_BINDING, 0) && pass;<br>
+       pass = check_GetInteger_value(GL_CURRENT_PROGRAM, 0) && pass;<br>
+<br>
+       pass = piglit_check_gl_error(GL_NO_ERROR) && pass;<br>
+<br>
+       /* If the set up went bad, none of the subtests will work.  Bail out<br>
+        * now.<br>
+        */<br>
+       if (!pass)<br>
+               piglit_report_result(PIGLIT_FAIL);<br>
+}<br></blockquote><div><br></div><div>Finally, this comment, from my previous review, doesn't seem to have been addressed:<br><br>"The only parts of this <span class="">test</span> that rely on compatibility behaviour are the VS's use of gl_Vertex, and this final sanity <span class="">test</span>.  It seems to me that the final sanity <span class="">test</span> is tangential to the primary purpose of the <span class="">test</span>;
 I'd rather see this subtest removed, and gl_Vertex changed to a 
user-defined vertex attribute, so that we can add 
supports_gl_core_version to the config block."<br><br></div><div>With those issues addressed, this patch is:<br><br>Reviewed-by: Paul Berry <<a href="mailto:stereotype441@gmail.com">stereotype441@gmail.com</a>><br>
</div></div></div></div>