cancel
Showing results for 
Search instead for 
Did you mean: 

Archives Discussions

Shrinker
Journeyman III

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

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

0 Likes
19 Replies
frali
Staff

Hi Shrinker,

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

0 Likes

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.

 

0 Likes

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.

0 Likes

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

0 Likes

 

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.

 

0 Likes

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

0 Likes

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

0 Likes

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

0 Likes

server side data == vertex buffer object.

client side data == no vertex buffer object.

(this is different from indirect rendering...)

0 Likes

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.

0 Likes

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);

0 Likes

Have the issues been fixed yet?

I am especially interested in the desired behavior of gl_VertexID now when glDrawArrays(mode, first, count) is called with first = n. The 3.3 Core specification says "gl_VertexID holds the integer index i implicitly passed by DrawArrays or one of the other drawing commands defined in section 2.8.3." So I would assume that gl_VertexID is = n for the first drawn vertex too.

 

0 Likes

it has been fixed, but it did not reach a public release yet.

Pierre B.

0 Likes

It's been almost a month, so just so that I can confirm- is this still not resolved in the last public release?

Also, may I ask how long has this error been around? I'm trying to decide whether it's now required to name all variables in GLSL with names lexically before "gl_" in all shaders which are to be released, in order to avoid problems with AMD/ATI GPU users (who might not have the most recent drivers installed).

0 Likes

I had to do two things because of compatibility considerations so far:

1. let all property names begin with "_"

2. use an integer property instead of gl_VertexID because of the issues that nothing was drawn when no property was bound, and because of the starting vertex id in a draw call

0 Likes

Update: I confirm that adding `layout(location=0)` to any actual attribute in the vertex shader works the bug around and allows to name the attributes in any way.

 

0 Likes

Hi! I am really happy to finally find a post related to this annoying bug!

I've been searching for answers for months (years?) and I couldn't find a word about it. Note that in my case it concerns gl_InstanceID.

I can confirm that adding a `layout(location=0)` to any attribute works the bug around for gl_InstanceID as well.

Now I have to figure out why I randomly get a SIGSEGV (/usr/lib64/dri/fglrx_dri.so) during vertex shader linking (prior to geometry and fragment shaders re-linking; I use a 3-step compile/link for each shader) , OR my memory consumption suddenly increases of a given fixed amount, OR my memory consumption suddenly increases until saturating the system (that's really bad...), OR I get absolutely no error and my program runs just fine 7 times out of 10!

0 Likes

So what's the status? Has the main naming issue be fixed? How are vertex IDs treated now? Do they now properly start with n if I enter n as the first vertex for the draw call, as it says in the OpenGL specification?

 

0 Likes

I've updated drivers today and can confirm that the issue is still existing. When I switch from my "manual" gl_VertexID to the real gl_VertexID, nothing is displayed at all. Great job, guys 😕

 

0 Likes