Entalpi
Entalpi

Reputation: 2033

Compute shader uniform optimized away improperly?

Getting the location of the uniform 'uniform Sphere spheres[10]' (via glGetUniformLocation(program, name)). Return -1 even though there are other uniforms in the program (frustum_planes) that work.

The frustum_planes and the spheres uniforms are also used at the same places which makes it weird that only one of the two are present.

Any ideas what might be going wrong?

Shader

#version 460 core
// Required by compute shaders apparently
// Process one object per shader invocation (optimisation?)
layout (local_size_x = 1) in;

struct Sphere {
    vec3 center;
    float radius;
};

uniform Sphere spheres[10];

// Plane defined as: Ax + By + Cz = D
uniform vec4 frustum_planes[6];

uint test(Sphere obj, vec4 plane) {
    const float distance = plane.x * obj.center.x + plane.y * obj.center.y + plane.z * obj.center.z + plane.w;
    if (distance < -obj.radius) {
        return 0; // Negative halfspace
    }
    return 1; // Positive halfspace or on the plane
}

/// Same as the OpenGL provided struct: DrawElementsIndirectCommand
struct DrawCommand {
    uint count; // Num elements (vertices)
    uint instanceCount; // Number of instances to draw (a.k.a primcount)
    uint firstIndex; // Specifies a byte offset (cast to a pointer type) into the buffer bound to GL_ELEMENT_ARRAY_BUFFER to start reading indices from.
    uint baseVertex; // Specifies a constant that should be added to each element of indices​ when chosing elements from the enabled vertex arrays.
    uint baseInstance; // Specifies the base instance for use in fetching instanced vertex attributes.
    // 20 bytes
    uint padding0;
    uint padding1;
    uint padding2;
    // 32 bytes (multiple of 16)
};

// One draw command per object 
// Command buffer backed by Shader Storage Object Buffer (SSBO)
layout(std140, binding = 0) writeonly buffer DrawCommandsBlock {
    DrawCommand draw_commands[];
};

void main() {
    const uint idx = gl_GlobalInvocationID.x;

    uint inside = 0; 
    for (int i = 0; i < 6; i++) {
        inside += test(spheres[idx], frustum_planes[i]) << i;
    }

    const uint INSIDE_ALL_PLANES = 63; // = 0b111111;
    const bool visible = inside == INSIDE_ALL_PLANES;
    draw_commands[idx].count = 25350; // sphere.indices.size();
    draw_commands[idx].instanceCount = visible ? 1 : 0; // This is the trick right here
    draw_commands[idx].baseInstance = 0;
    draw_commands[idx].baseVertex = 0;
    draw_commands[idx].padding0 = 0; // Avoid optimisation 
    draw_commands[idx].padding1 = 0;
    draw_commands[idx].padding2 = 0;
}

Working glGetUniformLocation call

glGetUniformLocation(gl_state.cull_shader.gl_program, "spheres[0].center");

main.cpp

  glUseProgram(gl_state.cull_shader.gl_program);
  if (glGetUniformLocation(gl_state.cull_shader.gl_program, "frustum_planes") == -1) {
    std::cerr << "Work!" << std::endl;
  }
  if (glGetUniformLocation(gl_state.cull_shader.gl_program, "spheres") == -1) {
    std::cerr << "Does not work?!" << std::endl;
  }
  glUniform4fv(glGetUniformLocation(gl_state.cull_shader.gl_program, "spheres"), NUM_OBJECTS, &bounding_volumes[0].pos.x);
  glUniform4fv(glGetUniformLocation(gl_state.cull_shader.gl_program, "frustum_planes"), 6, glm::value_ptr(frustum[0]));
  glDispatchCompute(NUM_OBJECTS, 1, 1);
  glMemoryBarrier(GL_COMMAND_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT); // Buffer objects affected by this bit are derived from the GL_DRAW_INDIRECT_BUFFER binding. 

Upvotes: 2

Views: 839

Answers (1)

BDL
BDL

Reputation: 22167

It is not possible to query locations of uniforms that are not basic types or arrays of basic types. The OpenGL Wiki states:

The introspection APIs will often make a single "variable" definition in GLSL appear as though it were multiple variables. This is done for variables that are not basic types or arrays of basic types (note that in GLSL, "basic types" include vector and matrix types).

...

Instead, each sub-element of the struct that is a basic type is visible.

This means, you can't query spheres (or spheres[0]). What you have to do is to query each member of each struct separately. In order to get the location of the first Sphere's center, you'll have to query for spheres[0].center.

Note, that querying frustum_planes works as expected because it is a array of vec4 (which is a basic type).

Upvotes: 5

Related Questions