Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER: vertex attribute not found in shader (gl) #940

Open
pseregiet opened this issue Nov 16, 2023 · 7 comments

Comments

@pseregiet
Copy link

pseregiet commented Nov 16, 2023

I have this shader

@ctype vec2 HMM_Vec2
@ctype vec3 HMM_Vec3
@ctype vec4 HMM_Vec4
@ctype mat4 HMM_Mat4

@vs vs_particle
in vec2 apos;
in vec2 auv;

in vec4 amodel1;
in vec4 amodel2;
in vec4 amodel3_lmao; //changed the name for testing
in vec4 amodel4;
in vec2 auv_offset;
in float atransparency;

out INTERFACE {
    vec2 uv;
    vec2 uv_offset;
    float trans;
} inter;

uniform vs_particle_slow {
    mat4 uvp;
};

void main() {
    mat4 model = mat4(amodel1, amodel2, amodel3_lmao, amodel4);

    inter.uv = auv;
    inter.uv_offset = auv_offset;
    inter.trans = atransparency;
    gl_Position = uvp * model * vec4(apos, 0.0, 1.0);
}
@end
...

which sokol-shdc turns into this

/*
    #version 330
    
    uniform vec4 vs_particle_slow[4];
    layout(location = 2) in vec4 amodel1;
    layout(location = 3) in vec4 amodel2;
    layout(location = 4) in vec4 amodel3_lmao;
    layout(location = 5) in vec4 amodel4;
    out INTERFACE
    {
        vec2 uv;
        vec2 uv_offset;
        float trans;
    } inter;
    
    layout(location = 1) in vec2 auv;
    layout(location = 6) in vec2 auv_offset;
    layout(location = 7) in float atransparency;
    layout(location = 0) in vec2 apos;
    
    void main()
    {
        inter.uv = auv;
        inter.uv_offset = auv_offset;
        inter.trans = atransparency;
        gl_Position = (mat4(vs_particle_slow[0], vs_particle_slow[1], vs_particle_slow[2], vs_particle_slow[3]) * mat4(amodel1, amodel2, amodel3_lmao, amodel4)) * vec4(apos, 0.0, 1.0);
    }
    
*/

Clearly the attribute 'amodel3_lmao' is used. I create a pipeline for this shader like this:

void particle_pipeline(pipelines *pipes)
{
    pipes->particle_shd = sg_make_shader(shdparticle_shader_desc(SG_BACKEND_GLCORE33));

    sg_pipeline_desc desc = {0};
    desc.shader = pipes->particle_shd;
    desc.color_count = 1;
    desc.colors[0] = {
        .write_mask = SG_COLORMASK_RGBA,
        .blend = {
            .enabled = true,
            .src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA,
            .dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,
        }
    };
    desc.layout.attrs[ATTR_vs_particle_apos]    = {
        .buffer_index = 0,
        .format = SG_VERTEXFORMAT_USHORT2N,
    };
    desc.layout.attrs[ATTR_vs_particle_auv]    = {
        .buffer_index = 0,
        .format = SG_VERTEXFORMAT_USHORT2N,
    };
    desc.layout.attrs[ATTR_vs_particle_amodel1]    = {
        .buffer_index = 1,
        .format = SG_VERTEXFORMAT_FLOAT4,
    };
    desc.layout.attrs[ATTR_vs_particle_amodel2]    = {
        .buffer_index = 1,
        .format = SG_VERTEXFORMAT_FLOAT4,
    };
    desc.layout.attrs[ATTR_vs_particle_amodel3_lmao]    = {
        .buffer_index = 1,
        .format = SG_VERTEXFORMAT_FLOAT4,
    };
    desc.layout.attrs[ATTR_vs_particle_amodel4]    = {
        .buffer_index = 1,
        .format = SG_VERTEXFORMAT_FLOAT4,
    };
    desc.layout.attrs[ATTR_vs_particle_auv_offset] = {
        .buffer_index = 1,
        .format = SG_VERTEXFORMAT_USHORT2N,
    };
    desc.layout.attrs[ATTR_vs_particle_atransparency] = {
        .buffer_index = 1,
        .format = SG_VERTEXFORMAT_FLOAT,
    };
    desc.layout.buffers[0] = { .step_func = SG_VERTEXSTEP_PER_VERTEX };
    desc.layout.buffers[1] = { .step_func = SG_VERTEXSTEP_PER_INSTANCE };
    desc.depth = {
        .compare = SG_COMPAREFUNC_LESS_EQUAL,
        .write_enabled = false,
    };
    desc.cull_mode = SG_CULLMODE_NONE;
    desc.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP;

    pipes->particle = sg_make_pipeline(&desc);
}

Desprite that I get this error when this pipeline is created:

[sg][error][id:7] ./3rdparty/sokol/sokol_gfx.h:7509:0:
	GL_VERTEX_ATTRIBUTE_NOT_FOUND_IN_SHADER: vertex attribute not found in shader (gl)

[sg][info][id:7] ./3rdparty/sokol/sokol_gfx.h:7510:0:
	amodel3_lmao

EDIT: After some looking around I guess this is not sokol specific issue, but an OpenGL issue. Still if you have any tips for this that would be appreciated. amodel3 is clearly used for creating the mat4 so how can it be optimised ? If i use it for some other random calculation (for example uv *= amodel3.xz) then it stays.

@floooh
Copy link
Owner

floooh commented Nov 17, 2023

That's indeed weird. The info you posted looks good as far as I can see.

Also, what GPU and driver version do you have?

If i use it for some other random calculation (for example uv *= amodel3.xz) then it stays.

This is also crazy... I had some problems in the past with glslangValidator and or SPIRVCross to strip unused attributes, but I haven't seen this sort of behaviour from GL drivers so far.

If it turns out that GL drivers are allowed to remove "unused" attributes then I guess the sokol_gfx.h GL backend needs to be a bit more flexible (I'm not sure if it is already enough to just turn the error into a warning, theoretically the vertex attribute should then be disabled, and skipped in _sg_gl_apply_bindings(), might be worth a try though)

sokol/sokol_gfx.h

Lines 8876 to 8923 in 5b6b743

// vertex attributes
for (GLuint attr_index = 0; attr_index < (GLuint)_sg.limits.max_vertex_attrs; attr_index++) {
_sg_gl_attr_t* attr = &bnd->pip->gl.attrs[attr_index];
_sg_gl_cache_attr_t* cache_attr = &_sg.gl.cache.attrs[attr_index];
bool cache_attr_dirty = false;
int vb_offset = 0;
GLuint gl_vb = 0;
if (attr->vb_index >= 0) {
// attribute is enabled
SOKOL_ASSERT(attr->vb_index < bnd->num_vbs);
_sg_buffer_t* vb = bnd->vbs[attr->vb_index];
SOKOL_ASSERT(vb);
gl_vb = vb->gl.buf[vb->cmn.active_slot];
vb_offset = bnd->vb_offsets[attr->vb_index] + attr->offset;
if ((gl_vb != cache_attr->gl_vbuf) ||
(attr->size != cache_attr->gl_attr.size) ||
(attr->type != cache_attr->gl_attr.type) ||
(attr->normalized != cache_attr->gl_attr.normalized) ||
(attr->stride != cache_attr->gl_attr.stride) ||
(vb_offset != cache_attr->gl_attr.offset) ||
(cache_attr->gl_attr.divisor != attr->divisor))
{
_sg_gl_cache_bind_buffer(GL_ARRAY_BUFFER, gl_vb);
glVertexAttribPointer(attr_index, attr->size, attr->type, attr->normalized, attr->stride, (const GLvoid*)(GLintptr)vb_offset);
_sg_stats_add(gl.num_vertex_attrib_pointer, 1);
glVertexAttribDivisor(attr_index, (GLuint)attr->divisor);
_sg_stats_add(gl.num_vertex_attrib_divisor, 1);
cache_attr_dirty = true;
}
if (cache_attr->gl_attr.vb_index == -1) {
glEnableVertexAttribArray(attr_index);
_sg_stats_add(gl.num_enable_vertex_attrib_array, 1);
cache_attr_dirty = true;
}
} else {
// attribute is disabled
if (cache_attr->gl_attr.vb_index != -1) {
glDisableVertexAttribArray(attr_index);
_sg_stats_add(gl.num_disable_vertex_attrib_array, 1);
cache_attr_dirty = true;
}
}
if (cache_attr_dirty) {
cache_attr->gl_attr = *attr;
cache_attr->gl_attr.offset = vb_offset;
cache_attr->gl_vbuf = gl_vb;
}
}

@pseregiet
Copy link
Author

That's indeed weird. The info you posted looks good as far as I can see.

Also, what GPU and driver version do you have?

If i use it for some other random calculation (for example uv *= amodel3.xz) then it stays.

This is also crazy... I had some problems in the past with glslangValidator and or SPIRVCross to strip unused attributes, but I haven't seen this sort of behaviour from GL drivers so far.

If it turns out that GL drivers are allowed to remove "unused" attributes then I guess the sokol_gfx.h GL backend needs to be a bit more flexible (I'm not sure if it is already enough to just turn the error into a warning, theoretically the vertex attribute should then be disabled, and skipped in _sg_gl_apply_bindings(), might be worth a try though)

sokol/sokol_gfx.h

Lines 8876 to 8923 in 5b6b743

// vertex attributes
for (GLuint attr_index = 0; attr_index < (GLuint)_sg.limits.max_vertex_attrs; attr_index++) {
_sg_gl_attr_t* attr = &bnd->pip->gl.attrs[attr_index];
_sg_gl_cache_attr_t* cache_attr = &_sg.gl.cache.attrs[attr_index];
bool cache_attr_dirty = false;
int vb_offset = 0;
GLuint gl_vb = 0;
if (attr->vb_index >= 0) {
// attribute is enabled
SOKOL_ASSERT(attr->vb_index < bnd->num_vbs);
_sg_buffer_t* vb = bnd->vbs[attr->vb_index];
SOKOL_ASSERT(vb);
gl_vb = vb->gl.buf[vb->cmn.active_slot];
vb_offset = bnd->vb_offsets[attr->vb_index] + attr->offset;
if ((gl_vb != cache_attr->gl_vbuf) ||
(attr->size != cache_attr->gl_attr.size) ||
(attr->type != cache_attr->gl_attr.type) ||
(attr->normalized != cache_attr->gl_attr.normalized) ||
(attr->stride != cache_attr->gl_attr.stride) ||
(vb_offset != cache_attr->gl_attr.offset) ||
(cache_attr->gl_attr.divisor != attr->divisor))
{
_sg_gl_cache_bind_buffer(GL_ARRAY_BUFFER, gl_vb);
glVertexAttribPointer(attr_index, attr->size, attr->type, attr->normalized, attr->stride, (const GLvoid*)(GLintptr)vb_offset);
_sg_stats_add(gl.num_vertex_attrib_pointer, 1);
glVertexAttribDivisor(attr_index, (GLuint)attr->divisor);
_sg_stats_add(gl.num_vertex_attrib_divisor, 1);
cache_attr_dirty = true;
}
if (cache_attr->gl_attr.vb_index == -1) {
glEnableVertexAttribArray(attr_index);
_sg_stats_add(gl.num_enable_vertex_attrib_array, 1);
cache_attr_dirty = true;
}
} else {
// attribute is disabled
if (cache_attr->gl_attr.vb_index != -1) {
glDisableVertexAttribArray(attr_index);
_sg_stats_add(gl.num_disable_vertex_attrib_array, 1);
cache_attr_dirty = true;
}
}
if (cache_attr_dirty) {
cache_attr->gl_attr = *attr;
cache_attr->gl_attr.offset = vb_offset;
cache_attr->gl_vbuf = gl_vb;
}
}

I'll just add that despite this attribute being apparently optimised out my code still runs fine. If a whole line of a matrix was gone it would surely produce a bogus transformation matrix. RenderDoc's Mesh View shows the attribute is gone:
image

@pseregiet
Copy link
Author

Oh and my driver is

OpenGL vendor string: AMD
OpenGL renderer string: AMD Radeon RX 590 Series (polaris10, LLVM 16.0.6, DRM 3.54, 6.5.9-zen2-1-zen)
OpenGL core profile version string: 4.6 (Core Profile) Mesa 23.2.1-arch1.2
OpenGL core profile shading language version string: 4.60

@Interrupt
Copy link

I just ran into this when compiling a Zig app that loads a mesh but only has a basic shader that does not use the normals or tangents passed in. When I remove those vertex attributes from the layout, the mesh loads and renders fine.

@alichraghi
Copy link

it seems like the problem is shdc ignores unused variables (uniform/in/out) so the information never get added into shader descriptor

@floooh
Copy link
Owner

floooh commented Jul 30, 2024

Unfortunately this stripping of unused uniforms is something that's out of my control. It may happen in the first compile pass from GLSL to SPIRV, and I also had it happen as secondary effect of the SPIRV-Tools dead code elimination (although I disabled all optimizer passes I could find which involve DCE).

Even at runtime, the GLSL compilers in GL drivers are free to remove unused uniforms so that they don't show up in glGetUniformLocation.

@pseregiet
Copy link
Author

it seems like the problem is shdc ignores unused variables (uniform/in/out) so the information never get added into shader descriptor

The fact openGL strips unused uniforms or attributes is not strange. What is weird is that if you look at my original code the attributes that was "removed" is clearly used. And at the same time all rendering looks correct as if it was present. The attribute that was removed was part of a matrix, so if it was gone, undefined, filed with random data I would surely have corrupted transformations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants