glDrawElements GL_INVALID_OPERATION on AMD GPU

Hello! I wrote a small engine, and decided to test it on different graphics cards. And here is what turned out to be unexpected: on Nvidia and Intel everything works well on Linux and Windows, but on AMD - it gives error 1282 (GL_INVALID_OPERATION) when trying to call glDrawElements. Below are code snippets and links to the full one.

Rendering code
void drawModelDM(const Model &model, ShaderProgram *program, const glm::mat4 &mat = glm::mat4(1.0f)) {
    glBindVertexArray(model.shape->vaos[0]);

    [...]

    program->set(AlgineNames::ShadowShader::TransformationMatrix, mat * model.m_transform);
    
    for (size_t i = 0; i < model.shape->meshes.size(); i++) {
        int err = glGetError();
        if (err != 0)
            std::cout << "Before glDrawElements DM: " << err << "\n";
        glDrawElements(GL_TRIANGLES, model.shape->meshes[i].count, GL_UNSIGNED_INT, reinterpret_cast<void*>(model.shape->meshes[i].start * sizeof(uint))); // OK
        err = glGetError();
        if (err != 0)
            std::cout << "After glDrawElements DM: " << err << "\n";
    }
}

/**
 * Draws model
 */
void drawModel(const Model &model) {
    glBindVertexArray(model.shape->vaos[1]);

    [...]

    modelMatrix = &model.m_transform;
    updateMatrices();
    for (size_t i = 0; i < model.shape->meshes.size(); i++) {
        texture2DAB(0, model.shape->meshes[i].mat.ambientTexture);
        texture2DAB(1, model.shape->meshes[i].mat.diffuseTexture);
        texture2DAB(2, model.shape->meshes[i].mat.specularTexture);
        texture2DAB(3, model.shape->meshes[i].mat.normalTexture);
        texture2DAB(4, model.shape->meshes[i].mat.reflectionTexture);
        texture2DAB(5, model.shape->meshes[i].mat.jitterTexture);

        colorShader->set(AlgineNames::ColorShader::Material::AmbientStrength, model.shape->meshes[i].mat.ambientStrength);
        colorShader->set(AlgineNames::ColorShader::Material::DiffuseStrength, model.shape->meshes[i].mat.diffuseStrength);
        colorShader->set(AlgineNames::ColorShader::Material::SpecularStrength, model.shape->meshes[i].mat.specularStrength);
        colorShader->set(AlgineNames::ColorShader::Material::Shininess, model.shape->meshes[i].mat.shininess);

        int err = glGetError();
        if (err != 0)
            std::cout << "Before glDrawElements: " << err << "\n";
        glDrawElements(GL_TRIANGLES, model.shape->meshes[i].count, GL_UNSIGNED_INT, reinterpret_cast<void*>(model.shape->meshes[i].start * sizeof(uint))); // ERROR: 1282
        err = glGetError();
        if (err != 0)
            std::cout << "After glDrawElements: " << err
                << "\nCount: " << model.shape->meshes[i].count
                << "\nStart: " << model.shape->meshes[i].start
                << "\nVAO: " << model.shape->vaos[1] << "\n";
    }
}

/**
 * Renders to depth cubemap
 */
void renderToDepthCubemap(const uint index) {
   pointLamps[index].begin();
    pointLamps[index].updateMatrix();
    lightDataSetter.setShadowShaderPos(pointLamps[index]);
   lightDataSetter.setShadowShaderMatrices(pointLamps[index]);
   glClear(GL_DEPTH_BUFFER_BIT);

   // drawing models
    for (size_t i = 0; i < MODELS_COUNT; i++)
        drawModelDM(models[i], pointShadowShader);

   // drawing lamps
   for (GLuint i = 0; i < pointLampsCount; i++) {
      if (i == index) continue;
        drawModelDM(*pointLamps[i].mptr, pointShadowShader);
   }

   pointLamps[index].end();
}

/**
 * Renders to depth map
 */
void renderToDepthMap(uint index) {
   dirLamps[index].begin();
   glClear(GL_DEPTH_BUFFER_BIT);

   // drawing models
    for (size_t i = 0; i < MODELS_COUNT; i++)
        drawModelDM(models[i], dirShadowShader, dirLamps[index].m_lightSpace);

   // drawing lamps
   for (GLuint i = 0; i < dirLampsCount; i++) {
      if (i == index) continue;
        drawModelDM(*dirLamps[i].mptr, dirShadowShader, dirLamps[index].m_lightSpace);
   }

   dirLamps[index].end();
}

/**
 * Color rendering
 */
uint colorAttachment02[3] = { GL_COLOR_ATTACHMENT0, GL_NONE, GL_COLOR_ATTACHMENT2 };
uint colorAttachment0123[4] = {
    GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3
};
void render() {
    renderer.mainPass(displayFb->getId());
    
   // view port to window size
   glViewport(0, 0, winWidth, winHeight);

    glDrawBuffers(4, colorAttachment0123);
    colorShader->use();

    // sending lamps parameters to fragment shader
   sendLampsData();

    // drawing
    for (size_t i = 0; i < MODELS_COUNT; i++)
        drawModel(models[i]);
   for (size_t i = 0; i < pointLampsCount + dirLampsCount; i++)
       drawModel(lamps[i]);

    [...]
}

void display() {
    // animate
    for (usize i = 0; i < MODELS_COUNT; i++)
        if (models[i].shape->bonesPerVertex != 0)
            models[i].animator->animate(glfwGetTime());

    // shadow rendering
    // point lights
    pointShadowShader->use();
   for (uint i = 0; i < pointLampsCount; i++) {
       lightDataSetter.setShadowShaderFarPlane(pointLamps[i]);
        renderToDepthCubemap(i);
    }

    // dir lights
    dirShadowShader->use();
    for (uint i = 0; i < dirLampsCount; i++)
        renderToDepthMap(i);
   
    ssrShader->use();
    ssrShader->set(AlgineNames::SSRShader::ProjectionMatrix, camera.getProjectionMatrix());
    ssrShader->set(AlgineNames::SSRShader::ViewMatrix, camera.getViewMatrix());

   /* --- color rendering --- */
    glClear(GL_DEPTH_BUFFER_BIT); // color will cleared by quad rendering
   render();
   glUseProgram(0);
}

Full code here
Color shaders: vertex, fragment
Repository

Everything is perfectly drawn in the depth map, but not in the “color map”… So everything except geometry is displayed. And such a problem is ONLY on AMD.
Here is AMD debug output:

Message: glDrawElements has generated an error (GL_INVALID_OPERATION)
Source: API
Type: Error
ID: 1000
Severity: High

Message: glDrawElements failed because the currently active shader combination is invalid (GL_INVALID_OPERATION)
Source: API
Type: Error
ID: 2003
Severity: High

Nvidia Nsight didn’t show anything that could cause this problem

Precompiled binaries for Windows: with debug context enabled / debug output and without debug output

Please help… I’ve already spent about 5 hours trying to figure out what I’m doing wrong

Solved! The problem was that AMD required array of samplers to be filled. I had implemented workaround earlier, but because of the typo that I made when renaming macros, everything broke. Code for workaround:

#define pointLampsCount 1
#define dirLampsCount 1
#define pointLightsLimit 8
#define dirLightsLimit 8
// point light texture start id
#define POINT_LIGHT_TSID 6
// dir light texture start id
#define DIR_LIGHT_TSID POINT_LIGHT_TSID + pointLightsLimit // HERE was typo: instead of pointLightsLimit was pointLampsCount!

void fillArraysOfSamplers() {
  shader->use();

  for (int i = 0; i < pointLightsLimit; i++) {
    ShaderProgram::set(lightDataSetter.getLocation(LightDataSetter::ShadowMap, Light::TypePointLight, i), POINT_LIGHT_TSID + i);
	glActiveTexture(GL_TEXTURE0 + POINT_LIGHT_TSID + i);
    glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
  }
  
  for (int i = 0; i < dirLightsLimit; i++) {
    ShaderProgram::set(lightDataSetter.getLocation(LightDataSetter::ShadowMap, Light::TypeDirLight, i), DIR_LIGHT_TSID + i);
	glActiveTexture(GL_TEXTURE0 + DIR_LIGHT_TSID + i);
	glBindTexture(GL_TEXTURE_2D, 0);
  }
  
  glUseProgram(0);
}

Looking at the fragment shader, I notice that you have a bunch of sampler2D variables and also an array of samplerCubes. It’s invalid(*) for a shader to access a single texture unit via sampler variables of differing types. If you aren’t initialising the sampler uniforms, it’s quite likely that one or more of the sampler2D variables will refer to the same texture unit as an element of the samplerCube array.

(*) It’s not necessarily an error (in the sense of something that glGetError will report), but results in undefined behaviour. This might manifest as an actual error (as appears to be the case on AMD), or just unexpected shader output. Or it might even work; it just isn’t required to.

FWIW, this is from memory; I can’t actually find the relevant language in either the OpenGL or GLSL specifications right now.

Look at glValidateProgram. This case must generate INVALID_OPERATION, and the validation log should say why.

Thanks.

For future reference, it’s §11.1.3.11 (in the 4.6 spec):