opengl 4 tessellation linker error

Started by
11 comments, last by Yours3!f 11 years, 11 months ago
Hi,

I'm trying to do some basic tessellation, but I've ran into a strange problem. My glsl code compiles fine, but when I try to render some geometry with it, I get the invalid operation error. I tried to debug this issue with gdebugger, and interestingly it gives me a AP_PROGRAM_LINK_FAILED_ERROR. This is strange because I do check if the linking went right (GL_LINK_STATUS), and I get no errors even if I directly check the linking with glGetError. The geometry that I tried to render works fine with other shaders.
I followed this tutorial: http://prideout.net/blog/?p=48

Here's the glsl code:

[vertex shader]
#version 410

in vec4 in_vertex; //incoming vertex attribute in object space

out cross_shader_data
{
vec3 position;
} o;

void main()
{
o.position = in_vertex.xyz; //pass the data to the TC shader
}

[tessellation control shader]
#version 410

layout(vertices = 3) out; //a patch should consist of 3 vertices, thus essentially defining a vertex

in cross_shader_data
{
vec3 position;
} i[];

out cross_shader_data
{
vec3 position;
} o[];

uniform float tess_level_inner;
uniform float tess_level_outer;

void main()
{
o[gl_InvocationID].position = i[gl_InvocationID].position; //pass the vertex data to the TE shader

if(gl_InvocationID == 0) //define tessellation levels
{
gl_TessLevelInner[0] = tess_level_inner;

gl_TessLevelOuter[0] = tess_level_outer;
gl_TessLevelOuter[1] = tess_level_outer;
gl_TessLevelOuter[2] = tess_level_outer;
}
}

[tessellation evaluation shader]
#version 410

layout(triangles, equal_spacing, ccw) in; //process triangles, using equal spacing, and they'll be in counter-clockwise order

in cross_shader_data
{
vec3 position;
} i[];

out cross_shader_data
{
vec3 position;
vec3 patch_distance;
} o;

uniform mat4 modelviewproj;

void main()
{
vec3 p0 = gl_TessCoord.x * i[0].position;
vec3 p1 = gl_TessCoord.y * i[1].position;
vec3 p2 = gl_TessCoord.z * i[2].position;

o.patch_distance = gl_TessCoord;
o.position = normalize(p0 + p1 + p2); //create the new vertex
gl_Position = modelviewproj * vec4(o.position, 1); //do the transformation before passing to the geometry shader
}

[geometry shader]
#version 410

layout(triangles) in; //process triangles
layout(triangle_strip, max_vertices = 3) out; //spit out triangle strips that consist of max 3 vertices

in cross_shader_data
{
vec3 position;
vec3 patch_distance;
} i[3];

out cross_shader_data
{
vec3 face_normal;
vec3 patch_distance;
vec3 tri_distance;
} o;

uniform mat3 normal_mat;

void main()
{
vec3 a = i[2].position - i[0].position;
vec3 b = i[1].position - i[0].position;
o.face_normal = normal_mat * normalize(cross(a, b)); //transform face normal into view space

o.patch_distance = i[0].patch_distance;
o.tri_distance = vec3(1, 0, 0);
gl_Position = gl_in[0].gl_Position;
EmitVertex();

o.patch_distance = i[1].patch_distance;
o.tri_distance = vec3(0, 1, 0);
gl_Position = gl_in[1].gl_Position;
EmitVertex();

o.patch_distance = i[2].patch_distance;
o.tri_distance = vec3(0, 0, 1);
gl_Position = gl_in[2].gl_Position;
EmitVertex();
}

[pixel shader]
#version 410

in cross_shader_data
{
vec3 face_normal;
vec3 tri_distance;
vec3 patch_distance;
} i;

out vec4 color;

float amplify(float d, float scale, float offset)
{
d = scale * d + offset;
d = clamp(d, 0, 1);
d = 1 - exp2(-2 * d * d);
return d;
}

void main()
{
vec3 n = normalize(i.face_normal);
vec3 l = vec3(0, 1, 0);

float ndotl = max(dot(n, l), 0);

vec3 result = ndotl * vec3(1, 1, 1); //do some lighting

float d1 = min(min(i.tri_distance.x, i.tri_distance.y), i.tri_distance.z);
float d2 = min(min(i.patch_distance.x, i.patch_distance.y), i.patch_distance.z);

result *= amplify(d1, 40, -0.5) * amplify(d2, 60, -0.5); //just some edge outlining

color = vec4(result, 1);
}


Best regards,
Yours3!f
Advertisement
Hi!

For some reason passing structs between shader stages doesn’t work anymore (at least the way you/I did it…). At some point it did. I remember it fairly well, since I was very happy to see that GLSL finally allowed it, too (like HLSL). But with current drivers it doesn’t work anymore. I had to rewrite some of my old shaders, too. Maybe it just wasn’t specification compliant and only nVidia drivers allowed it.

If you follow the tutorial you linked above, you should be fine. So, something like:
in vec3 i_position[];
out vec3 o_position[];

And so on.

And yes, I was also confused that the GLSL compiler doesn't complain at all.

Cheers!

Hi!

For some reason passing structs between shader stages doesn’t work anymore (at least the way you/I did it…). At some point it did. I remember it fairly well, since I was very happy to see that GLSL finally allowed it, too (like HLSL). But with current drivers it doesn’t work anymore. I had to rewrite some of my old shaders, too. Maybe it just wasn’t specification compliant and only nVidia drivers allowed it.

If you follow the tutorial you linked above, you should be fine. So, something like:
in vec3 i_position[];
out vec3 o_position[];

And so on.

And yes, I was also confused that the GLSL compiler doesn't complain at all.

Cheers!


Thank you Tsus for the reply smile.png

I tried to convert the interface blocks to traditional in-outs but somethings still wrong... What am I missing?

drivers... I even made a segfault to the compiler while converting the shaders... (I mean it segfaulted when linking, and the last function in the stack was somewhere in the catalyst drivers... 12.4 64bit linux)

this is how the shaders look like now:

[vertex shader]
#version 410
in vec4 in_vertex;
out vec3 v_position;
void main()
{
v_position = in_vertex.xyz;
}
[tessellation control shader]
#version 410
layout(vertices = 3) out;
in vec3 v_position[];
out vec3 tc_position[];
uniform float tess_level_inner;
uniform float tess_level_outer;
void main()
{
tc_position[gl_InvocationID] = v_position[gl_InvocationID];
if(gl_InvocationID == 0)
{
gl_TessLevelInner[0] = tess_level_inner;
gl_TessLevelOuter[0] = tess_level_outer;
gl_TessLevelOuter[1] = tess_level_outer;
gl_TessLevelOuter[2] = tess_level_outer;
}
}
[tessellation evaluation shader]
#version 410
layout(triangles, equal_spacing, ccw) in;
in vec3 tc_position[];
out vec3 te_position;
out vec3 te_patch_distance;
uniform mat4 modelviewproj;
void main()
{
vec3 p0 = gl_TessCoord.x * tc_position[0];
vec3 p1 = gl_TessCoord.y * tc_position[1];
vec3 p2 = gl_TessCoord.z * tc_position[2];

te_patch_distance = gl_TessCoord;
te_position = normalize(p0 + p1 + p2);
gl_Position = modelviewproj * vec4(te_position, 1);
}
[geometry shader]
#version 410
layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;
in vec3 te_position[3];
in vec3 te_patch_distance[3];
out vec3 g_face_normal;
out vec3 g_patch_distance;
out vec3 g_tri_distance;
uniform mat3 normal_mat;
void main()
{
vec3 a = te_position[2] - te_position[0];
vec3 b = te_position[1] - te_position[0];
g_face_normal = normal_mat * normalize(cross(a, b));

g_patch_distance = te_patch_distance[0];
g_tri_distance = vec3(1, 0, 0);
gl_Position = gl_in[0].gl_Position;
EmitVertex();

g_patch_distance = te_patch_distance[1];
g_tri_distance = vec3(0, 1, 0);
gl_Position = gl_in[1].gl_Position;
EmitVertex();

g_patch_distance = te_patch_distance[2];
g_tri_distance = vec3(0, 0, 1);
gl_Position = gl_in[2].gl_Position;
EmitVertex();
}
[pixel shader]
#version 410
in vec3 g_face_normal;
in vec3 g_tri_distance;
in vec3 g_patch_distance;
out vec4 color;
float amplify(float d, float scale, float offset)
{
d = scale * d + offset;
d = clamp(d, 0, 1);
d = 1 - exp2(-2 * d * d);
return d;
}
void main()
{
vec3 n = normalize(g_face_normal);
vec3 l = vec3(0, 1, 0);

float ndotl = max(dot(n, l), 0);

vec3 result = ndotl * vec3(1, 1, 1);

float d1 = min(min(g_tri_distance.x, g_tri_distance.y), g_tri_distance.z);
float d2 = min(min(g_patch_distance.x, g_patch_distance.y), g_patch_distance.z);

result *= amplify(d1, 40, -0.5) * amplify(d2, 60, -0.5);

color = vec4(result, 1);
}


Best regards,
Yours3!f
Hi!

The code you posted worked fairly well. I just changed some minor things to make it fit to my demo app, here.
In particular, I used explicit vertex attribute binding in the vertex shader:
layout(location=0) in vec3 in_vertex;

For all uniform parameters I utilized a uniform buffer object, e.g.:
layout(std140) uniform TessFactor
{
float tess_level_inner;
float tess_level_outer;
};


Eventually, I removed the normalization of te_position in your tessellation evaluation shader (barycentric interpolation doesn't require it).
//te_position = normalize(p0 + p1 + p2);
te_position = p0 + p1 + p2;


It works on my end. I tested on a GeForce 460 GTX (Win 7, 64 Bit).

Good luck! Hopefully you find a way to get it running, too.
Best regards
just a couple of questions in connection with your modifications:


The code you posted worked fairly well. I just changed some minor things to make it fit to my demo app, here.
In particular, I used explicit vertex attribute binding in the vertex shader:
layout(location=0) in vec3 in_vertex;


I use special "shader controller" files for this :) so I don't have to worry about which attribute belong to which location.

For all uniform parameters I utilized a uniform buffer object, e.g.:
layout(std140) uniform TessFactor
{
float tess_level_inner;
float tess_level_outer;
};

[/quote]

is that a performance win, or you just like to use uniforms this way?


Eventually, I removed the normalization of te_position in your tessellation evaluation shader (barycentric interpolation doesn't require it).
//te_position = normalize(p0 + p1 + p2);
te_position = p0 + p1 + p2;

[/quote]

yeah I still need to dig deeper into the barycentric coordinates...

It works on my end. I tested on a GeForce 460 GTX (Win 7, 64 Bit).[/quote]

I guess this is the relevant bit. I have an AMD Radeon HD 5670 (Linux, 64 bit, same problem on Win 7 64 bit with same drivers) with newest drivers. So can you please verify the code on AMD as well? Because if thats the problem, then I guess I should contact the AMD dev guys to fix this...
Hi,

I did the modifications only to get the code running in a small tessellation exercise I wrote a while back. I didn’t want to change much on the CPU side, so I just used the uniform buffer objects that were passed in already.
On the CPU side I didn’t specify the attribute binding, so I had to do it in GLSL. Doing this in GLSL is just a personal favor.


[quote name='Tsus' timestamp='1336296166' post='4937734']
For all uniform parameters I utilized a uniform buffer object, e.g.:
layout(std140) uniform TessFactor
{
float tess_level_inner;
float tess_level_outer;
};


is that a performance win, or you just like to use uniforms this way?
[/quote]
I think the performance win isn’t that big. It’s more that I like that sort of grouping as it reminds me of constant buffers in Dx.


[quote name='Tsus' timestamp='1336296166' post='4937734']
It works on my end. I tested on a GeForce 460 GTX (Win 7, 64 Bit).

I guess this is the relevant bit. I have an AMD Radeon HD 5670 (Linux, 64 bit, same problem on Win 7 64 bit with same drivers) with newest drivers. So can you please verify the code on AMD as well? Because if thats the problem, then I guess I should contact the AMD dev guys to fix this...
[/quote]
Sorry, I neither have an AMD at home nor in the lab.
I attached the code so you can test it yourself.

Yes, you should probably report this to AMD if it turns out to be some sort of bug.

Cheers!

Hi,

I did the modifications only to get the code running in a small tessellation exercise I wrote a while back. I didn’t want to change much on the CPU side, so I just used the uniform buffer objects that were passed in already.
On the CPU side I didn’t specify the attribute binding, so I had to do it in GLSL. Doing this in GLSL is just a personal favor.

[quote name='Yours3!f' timestamp='1336304020' post='4937748']
[quote name='Tsus' timestamp='1336296166' post='4937734']
For all uniform parameters I utilized a uniform buffer object, e.g.:
layout(std140) uniform TessFactor
{
float tess_level_inner;
float tess_level_outer;
};


is that a performance win, or you just like to use uniforms this way?
[/quote]
I think the performance win isn’t that big. It’s more that I like that sort of grouping as it reminds me of constant buffers in Dx.


[quote name='Tsus' timestamp='1336296166' post='4937734']
It works on my end. I tested on a GeForce 460 GTX (Win 7, 64 Bit).

I guess this is the relevant bit. I have an AMD Radeon HD 5670 (Linux, 64 bit, same problem on Win 7 64 bit with same drivers) with newest drivers. So can you please verify the code on AMD as well? Because if thats the problem, then I guess I should contact the AMD dev guys to fix this...
[/quote]
Sorry, I neither have an AMD at home nor in the lab.
I attached the code so you can test it yourself.

Yes, you should probably report this to AMD if it turns out to be some sort of bug.

Cheers!
[/quote]

Thank you smile.png this saved me writing an example.

Well I had to do some modifications to get it running, but eventually it was running, so I guess I'm doing something wrong.
I've attached the modified code. (tessellation.dom, and Tessellation_end.cpp, plus file renamings and I added headers, libs and dlls)
HEUREKA XD

SOLVED smile.png

I examined your example, and found out one thing that's entirely different: how you draw your mesh.

so I just had to include the glPatchParameter function and change the glDrawElements from GL_TRIANGLES to GL_PATCHES... this is quite stupid... so this is what a working source does to me smile.png

Thank your for the help!!!
Hi,

just out of curiosity I tried the technique with interface blocks, and it worked, so that wasn't the problem smile.png seems like the newest drivers are capable of this.

another thing is that the normalization in the tessellation evaluation shader IS needed, without it the geometry won't "emerge" out of the surface. (see pictures)
Hi,


just out of curiosity I tried the technique with interface blocks, and it worked, so that wasn't the problem smile.png seems like the newest drivers are capable of this.

Nice! Thanks for the info! I’ll give it a try again, later.


another thing is that the normalization in the tessellation evaluation shader IS needed, without it the geometry won't "emerge" out of the surface.

Wouldn't this only work for unit-spheres?
Most often, I just pick an easy tessellation scheme like phong tessellation.

This topic is closed to new replies.

Advertisement