19 Replies Latest reply on Feb 8, 2011 9:04 PM by Shrinker

    OpenGL 3.3/GLSL 330 Vertex Attribute and Built-In issues

    Shrinker

      Hello! I guess this is the right place to post about problems I have been experiencing lately.

      I am working with OpenGL (3.3) on my Nvidia desktop and my Ati laptop, both are patched to the newest versions of the respective graphics drivers. I have identified some problems that I could confirm with friends who also own properly maintained Ati systems.

      In GLSL nowadays, the proper way of inputting vertex coordinate data is by declaring a vertex attribute in the source code and using an "in" variable in the vertex shader. That vertex data shall then be transformed with the model view and the projection matrices, both input as uniforms. That works good so far.

      Now though, I have a specific case where there is no need to specify vertex coordinates, because they can easily be inferred from gl_VertexID. I have for days studied the OpenGL specifications and am quite sure that what I am doing is nothing out of the ordinary. So I have a vertex shader that has no inputs, but outputs data, and it infers what to output solely from gl_VertexID. This works fine on my Nvidia system, but my Ati system just doesn't display anything. It starts displaying something once I declare a dummy input and enter dummy data that is also use and overwritten in the shader itself (i.e. unnecessary assignments to make sure the input is not optimized away). It looks to me like the driver doesn't display anything when there are no inputs, which is behavior that is not backed by the OpenGL specification. Triangles or Quads can easily be produced with the sequence information coming from gl_VertexID alone.

      Another problem that I've had is that as soon as I used any of the still valid Built-Ins, like gl_VertexID or gl_InstanceID, I could only use vertex attributes IF, sorted alphanumerically, their names would be BEFORE gl_*. I have triple-checked this all in the source code counterpart too to make sure I am sending the right data, and to make sure that I retrieve valid input data locations.

      So, for instance, if I input vertex positions for a quad through the named input "foo" and color the quad based on gl_VertexID, it works fine. If I, however, name my input "eeh" (which comes after gl_*), nothing is displayed at all. My guess is that the built-ins somehow count against the input table when used, making it impossible to actually input any attribute data.

      As a workaround to my specific case, I'm now using an integer input named "_vertexID", which is just that, the vertex id. I have to use the leading underline because whenever I use any gl_-Built-In, nothing is displayed otherwise (because of the issue described above).

      Somewhere else I have seen an ongoing debate about what gl_VertexID should yield when the call to glDrawArrays does not start at the 0th, but at a greater-than-0th vertex. In my case, where all vertex coordinates are inferred directly from gl_Vertex, it just doesn't make sense to let gl_VertexID start at zero when it is nonzero in the draw call. The OpenGL specification states that the value is implied by the draw call. If the Ati driver really starts at zero, no matter the starting vertex specified in the draw call, then that is probably erroneous behavior which is not in accordance with the specification.

       

      Best regards,

      Shrinker

        • OpenGL 3.3/GLSL 330 Vertex Attribute and Built-In issues
          frali

          Hi Shrinker,

          Could you please try to paste the shaders? I think it could help to declare the problems clearly. Thanks.

            • OpenGL 3.3/GLSL 330 Vertex Attribute and Built-In issues
              Shrinker

              Okay, let me quickly reconstruct my test cases... *thinks*

              Works.vert and Works.frag:
              ---
              #version 330
              precision highp float;

              in vec2 f;
              out float X;

              void main()
              {
                  gl_Position = vec4(f, 0, 1);
                  if (gl_VertexID == 0)
                      X = 1.;
                  if (gl_VertexID == 0) gl_Position = vec4(0, 0, 0, 1); else
                  if (gl_VertexID == 1) gl_Position = vec4(1, 0, 0, 1); else
                  if (gl_VertexID == 2) gl_Position = vec4(1, 1, 0, 1); else
                  if (gl_VertexID == 3) gl_Position = vec4(0, 1, 0, 1);
              }
              ---
              #version 330
              precision highp float;

              in float X;
              out vec4 fragColor;

              void main()
              {
                  fragColor = vec4(X, 0, 0, 1);
              }
              ---


              DoesntWork.vert and Doestnwork.frag:
              ---
              #version 330
              precision highp float;

              in vec2 h;
              out float X;

              void main()
              {
                  gl_Position = vec4(h, 0, 1);
                  if (gl_VertexID == 0)
                      X = 1.;
                  if (gl_VertexID == 0) gl_Position = vec4(0, 0, 0, 1); else
                  if (gl_VertexID == 1) gl_Position = vec4(1, 0, 0, 1); else
                  if (gl_VertexID == 2) gl_Position = vec4(1, 1, 0, 1); else
                  if (gl_VertexID == 3) gl_Position = vec4(0, 1, 0, 1);
              }
              ---
              #version 330
              precision highp float;

              in float X;
              out vec4 fragColor;

              void main()
              {
                  fragColor = vec4(X, 0, 0, 1);
              }
              ---

              Notable functions used to fill gl_Position with the same data that is inferred from gl_VertexID there:

              glGetAttribLocation(programID /*of shader*/, name);

              void Mesh3X::loadData(const uint attribIndex, const float *const buf, const uint size, const uint8 dataDimension, const bool hintDynamic)
              {
                  if (loc[attribIndex] != -1)
                  {
                      clearData(attribIndex);
                      Engine::glBindVertexArray(vao); //vao is a vertex array object created with glGenVertexArrays(1, &vao);
                      Engine::glGenBuffers(1, vbo+attribIndex); //vbo is an array itself, this is all tested and confirmed to work here
                      Engine::glBindBuffer(GL_ARRAY_BUFFER, vbo[attribIndex]);
                      Engine::glBufferData(GL_ARRAY_BUFFER, size*sizeof(float), buf, hintDynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
                      Engine::glVertexAttribPointer(loc[attribIndex], dataDimension, GL_FLOAT, false, 0, nullptr);
                      Engine::glEnableVertexAttribArray(loc[attribIndex]);
                      Engine::glBindVertexArray(0);
                      ownsVBO[attribIndex] = true;
                  }
              }

              Notes about the shaders:
              - removing the vec2 input from the main function causes nothing to be displayed, even though the same data is inferred from gl_VertexID
              - first shader pair works, second shader pair does reliably not work when the input carries a name that follows gl_* (naming it H instead of h works too)

              The Mesh3X render function (called in scene graph traversal, firstVertex == 0, displayedVertexCount == 4)
              ---
              void Mesh3X::render()
              {
                  RenderStateStorage state;
                  state.begin(this, true);

                  Engine::glBindVertexArray(vao);
                  if (ibo) //== 0/false, so this branch is not entered
                  {
                      Engine::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
                      glDrawElements(drawMode, displayedIndexCount, indexDataType, (uint8*)nullptr+firstIndex);
                      Engine::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
                  }
                  else
                      glDrawArrays(drawMode, firstVertex, displayedVertexCount);
                  Engine::glBindVertexArray(0);

                  state.end(this, true);
              }
              ---

               

              If this all worked, and on top of that gl_VertexID would really represent the values implied by the glDrawArrays call (non-zero when the first vertex in the call is non-zero), then I'd have to make absolutely no modifications to my code, which worked nice on Nvidia, to work on Ati too. That would be most satisfying.

               

                • OpenGL 3.3/GLSL 330 Vertex Attribute and Built-In issues
                  pboudier

                  it seems that we still require to have an attribute bound to trigger a draw (and it needs to be attrib 0).

                  the OGL spec now allows indeed to have zero attribute (or no attribute 0 bound), so this is a bug still. we will look into it.

                    • OpenGL 3.3/GLSL 330 Vertex Attribute and Built-In issues
                      Shrinker

                      Nice!

                      What about the attrib 0 naming and the glDrawArrays call with first vertex being greater than zero though?

                      About the gl_VertexID thing: Normally, one would draw only a subset of the vertices if they wanted to draw a subset of a displayed model. It makes handling things a lot easier if the values of the vertex id variable remained consinstent there too, and consistent with the call arguments. One would otherwise have to supply gl_VertexID AND the starting vertex through vertex attribs and a uniform in their program if it should be compatible with both vendors, rendering gl_VertexID completely useless.

                       

                      Shrinker

                        • OpenGL 3.3/GLSL 330 Vertex Attribute and Built-In issues
                          pboudier

                           

                          What about the attrib 0 naming and the glDrawArrays call with first vertex being greater than zero though?



                          -> on the naming side, we will investigate (sounds like a nasty side effect)

                          -> on the attribute greater than zero: there are two different cases, if you use client side or server side array. in the first case a copy is involved, and in the second case, we only use pointers. I believe that AMD currently always ignores first, and nvidia ignores it for client side.

                           

                            • OpenGL 3.3/GLSL 330 Vertex Attribute and Built-In issues
                              Shrinker

                              That sounds like an implementation detail that should not have an impact on the output specified by the OpenGL specification

                                • OpenGL 3.3/GLSL 330 Vertex Attribute and Built-In issues
                                  pboudier

                                  it is not an implementation detail, but two different ways to input geometry data to OGL... anyway, we will review what we can do.

                                    • OpenGL 3.3/GLSL 330 Vertex Attribute and Built-In issues
                                      Shrinker

                                      Mh, I have so far only been using it with applications that run locally on my system. Guess I am a little blind for the server-side of things Sorry

                                       

                                      These emoticons are scary

                                        • OpenGL 3.3/GLSL 330 Vertex Attribute and Built-In issues
                                          pboudier

                                          server side data == vertex buffer object.

                                          client side data == no vertex buffer object.

                                          (this is different from indirect rendering...)

                                            • OpenGL 3.3/GLSL 330 Vertex Attribute and Built-In issues
                                              hduregger

                                              I am using OpenGL 3.2 with Catalyst 8.723 on Ubuntu Linux 10.04 and can confirm this alphabetical variable naming issue.

                                              Here it causes gl_instanceID to misbehave.

                                                • OpenGL 3.3/GLSL 330 Vertex Attribute and Built-In issues
                                                  hduregger

                                                  Btw. great discovery Shrinker, I was puzzled about what was going on. Especially that variable naming problem is nasty to discover.

                                                   

                                                  I can further confirm that the same behaviour can cause segmentation faults in the driver when a geometry shader is active.

                                                  E.g. I am using an attrib called 'vertex' here. If used like that then gl_instanceID will misbehave. And if a geometry shader is attached the application will crash with a segfault in the driver shared object.

                                                  On the other hand if I rename the variable to '_vertex'. Then gl_instanceID works and the application does not crash running the geometry shader.

                                                  Summary:

                                                  A. Vertex shader attrib variable name 'vertex'
                                                  - gl_instanceID misbehaves
                                                  - if geometry shader is attached and gl_instanceID is accessed application crashes in driver .so

                                                  B. Vertex shader attrib name '_vertex'
                                                  - gl_instanceID works
                                                  - if geometry shader is attached and gl_instanceID is accessed application runs correctly

                                                  edit:
                                                  using OpenGL 3.2 with Catalyst 8.723 on Ubuntu Linux 10.04

                                                  unfortunately I can't upgrade to the newest drivers, because of kernel changes I need to wait for the October release of catalyst.

                                                   

                                                  edit:
                                                  [vertex shader]

                                                  #version 150

                                                  struct Camera
                                                  {
                                                     mat4 projection;
                                                     vec3 position;
                                                  };

                                                  uniform samplerBuffer textureSampler;

                                                  uniform Camera camera;
                                                  uniform float radius;

                                                  in vec4 _vertex;

                                                  flat out mat4 projection;
                                                  flat out float radiusGeom;

                                                  void main()
                                                  {
                                                     radiusGeom = radius;

                                                     projection = camera.projection;

                                                     vec4 v = _vertex;

                                                     vec4 offset = texelFetch(textureSampler, gl_InstanceID);
                                                     v += offset;

                                                     gl_Position = (v - vec4(camera.position, 0.0f));
                                                  }

                                                  [geometry shader]

                                                  #version 150

                                                  layout(points) in;
                                                  layout(triangle_strip, max_vertices = 4) out;


                                                  flat in mat4 projection[];

                                                  flat in float radiusGeom[];

                                                  flat out vec4 center;
                                                  flat out float radiusFrag;
                                                  smooth out vec4 position;


                                                  void emitVertex(float dx, float dy, float radius)
                                                  {
                                                     vec4 p = gl_in[0].gl_Position + vec4(dx, dy, 0.0f, 0.0f);

                                                     gl_Position = projection[0] * p;

                                                     position = p;
                                                     radiusFrag = radius;
                                                     center = gl_in[0].gl_Position;

                                                     EmitVertex();
                                                  }


                                                  void main()
                                                  {
                                                     float radiusPos = radiusGeom[0];
                                                     float radiusNeg = -radiusPos;

                                                     emitVertex(radiusNeg, radiusNeg, radiusPos);
                                                     emitVertex(radiusNeg, radiusPos, radiusPos);
                                                     emitVertex(radiusPos, radiusNeg, radiusPos);
                                                     emitVertex(radiusPos, radiusPos, radiusPos);

                                                     EndPrimitive();
                                                  }


                                                  [fragment shader]

                                                  #version 150

                                                  flat in vec4 center;
                                                  flat in float radiusFrag;
                                                  smooth in vec4 position;


                                                  void main()
                                                  {
                                                     vec2 localPosition = position.xy - center.xy;

                                                     float distance = length(localPosition);

                                                     float temp = distance / radiusFrag;

                                                     temp = min(1.0f, temp);

                                                     float percent = 1.0f - temp;

                                                     percent = min(1.0f, max(0.0f, percent));

                                                     gl_FragColor = vec4(1.0f, 1.0f, 1.0f, 1.0f) * percent;
                                                  }


                                                  Rendering with
                                                  glDrawArraysInstanced(GL_POINTS, 0, 1, count);