0
votes

Hey I have a class abstracting everything shader related called Shader.cpp:

#include "Shader.h"
#include "Renderer.h"

#include <iostream>
#include <fstream>

Shader::Shader()
{
    GLCALL(m_RenderedID = glCreateProgram());
}


Shader::~Shader()
{
    GLCALL(glDeleteProgram(m_RenderedID));
}

void Shader::Bind() const
{
    GLCALL(glUseProgram(m_RenderedID));
}

void Shader::Unbind() const
{
    GLCALL(glUseProgram(0));
}

std::string ParseShader(const std::string& path)
{
    std::ifstream ifs(path);
    return std::string((std::istreambuf_iterator<char>(ifs)),
        (std::istreambuf_iterator<char>()));
}

static unsigned int CompileShader(unsigned int type, const std::string& source)
{
    GLCALL(unsigned int id = glCreateShader(type));
    const char* src = source.c_str();
    GLCALL(glShaderSource(id, 1, &src, nullptr));
    GLCALL(glCompileShader(id));

    int result;
    GLCALL(glGetShaderiv(id, GL_COMPILE_STATUS, &result));

    if (result == GL_FALSE)
    {
        int length;
        glGetShaderiv(id, GL_INFO_LOG_LENGTH, &length);
        char* message = (char*)alloca(length * sizeof(char));
        GLCALL(glGetShaderInfoLog(id, length, &length, message));
        std::cout << "Failed to compile shader!" << std::endl;
        std::cout << message << std::endl;
        glDeleteShader(id);
        return 0;
    }
    return id;
}

void Shader::Attach(const unsigned int type, const std::string& path)
{
    unsigned int id = CompileShader(type, ParseShader(path));
    if (m_ShaderFiles.find(id) == m_ShaderFiles.end())
        m_ShaderFiles[id] = ShaderFile({ type, path });
    GLCALL(glAttachShader(m_RenderedID, id));
}

void Shader::Link()
{
    int result;

    GLCALL(glLinkProgram(m_RenderedID));
    GLCALL(glGetProgramiv(m_RenderedID, GL_LINK_STATUS, &result));

    if (result == GL_FALSE)
    {
        std::cout << "Failed to link shader!" << std::endl;
        return;
    }

    GLCALL(glValidateProgram(m_RenderedID));
    GLCALL(glGetProgramiv(m_RenderedID, GL_VALIDATE_STATUS, &result));

    if (result == GL_FALSE)
    {
        std::cout << "Failed to validate shader!" << std::endl;
        return;
    }

    for (const auto& shaderFile : m_ShaderFiles) {
        GLCALL(glDeleteShader(shaderFile.first));
    }
}

// this part is taken from Shader.h because it's templated
template<typename T, unsigned int S>
void SetUniform(Uniform<T, S>& uniform)
{
    Bind();
    uniform.Set(GetUniformLocation(uniform.GetName()));
}

int Shader::GetUniformLocation(const std::string& name)
{
    if (m_UniformLocationCache.find(name) != m_UniformLocationCache.end())
        return m_UniformLocationCache[name];

    GLCALL(int location = glGetUniformLocation(m_RenderedID, name.c_str()));
    m_UniformLocationCache[name] = location;
    return location;
}

This is my app.cpp using the Shader class:

#include <GL/glew.h>
#include <GLFW/glfw3.h>

#include <iostream>
#include <fstream>
#include <string>

#include "core/renderer/Shader.h"
#include "core/renderer/uniform/Uniform4f.cpp"
#include "core/renderer/VertexArray.h"
#include "core/renderer/VertexBufferLayout.h"
#include "core/renderer/VertexBuffer.h"
#include "core/renderer/IndexBuffer.h"

int main(void)
{
    GLFWwindow* window;

    /* Initialize the library */
    if (!glfwInit())
        return -1;

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    /* Create a windowed mode window and its OpenGL context */
    window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        return -1;
    }

    /* Make the window's context current */
    glfwMakeContextCurrent(window);

    glfwSwapInterval(1);

    if (glewInit() != GLEW_OK)
        std::cout << "Error!" << std::endl;

    std::cout << glGetString(GL_VERSION) << std::endl;

    float vertices[] = {
        -0.5f, -0.5f,
        0.5f, -0.5f,
        0.5f, 0.5f,
        -0.5f, 0.5f
    };

    unsigned int indices[] = {
        0, 1, 2,
        2, 3, 0
    };

    VertexArray va;
    VertexBufferLayout layout;
    layout.Push({ GL_FLOAT, 2, sizeof(float) * 2, GL_FALSE });

    VertexBuffer vb(vertices, sizeof(vertices), layout);

    IndexBuffer ib(indices, 6);

    va.AddVertexBuffer(vb);

    Shader shader;
    shader.Attach(GL_VERTEX_SHADER, "res/shaders/basic_vs.shader");
    shader.Attach(GL_FRAGMENT_SHADER, "res/shaders/basic_fs.shader");
    shader.Link();
    shader.Bind();

    Uniform4f colorUniform("u_Color");

    float r = 0.00f;
    float increment = 0.05f;
    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window))
    {
        /* Render here */
        glClear(GL_COLOR_BUFFER_BIT);

        colorUniform.SetValues({ r, 0.5f, 0.9f, 1.0f });
        shader.SetUniform(colorUniform);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);

        if (r > 1.0f)
            increment = -0.05f;
        else if (r < 0.0f)
            increment = 0.05f;

        r += increment;

        /* Swap front and back buffers */
        glfwSwapBuffers(window);

        /* Poll for and process events */
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}
#include <iostream>

Here's the Uniform4f.cpp I'm trying to set:

#include "Uniform.h"
#include "../Renderer.h"

#include <iostream>

class Uniform4f : public Uniform<float, 4>
{
public:
    Uniform4f(const std::string& name, const std::array<float, 4>& values = {})
        : Uniform(name, values)
    {

    }

    virtual void Set(int location)
    {
        GLCALL(glUniform4fv(location, m_Values.size(), reinterpret_cast<const GLfloat*>(m_Values.data())));
    }
};

Now I'm getting GL_INVALID_OPERATION on the call to glUniform4fv(location, m_Values.size(), reinterpret_cast<const GLfloat*>(m_Values.data())) inside the call to shader.SetUniform(colorUniform); in the render loop in app.cpp.

The docs states it can either be one of these problems:

  • GL_INVALID_OPERATION is generated if there is no current program object.
  • GL_INVALID_OPERATION is generated if the size of the uniform variable declared in the shader does not match the size indicated by the glUniform command.
  • GL_INVALID_OPERATION is generated if one of the signed or unsigned integer variants of this function is used to load a uniform variable of type float, vec2, vec3, vec4, or an array of these, or if one of the floating-point variants of this function is used to load a uniform variable of type int, ivec2, ivec3, ivec4, unsigned int, uvec2, uvec3, uvec4, or an array of these.
  • GL_INVALID_OPERATION is generated if one of the signed integer variants of this function is used to load a uniform variable of type unsigned int, uvec2, uvec3, uvec4, or an array of these.
  • GL_INVALID_OPERATION is generated if one of the unsigned integer variants of this function is used to load a uniform variable of type int, ivec2, ivec3, ivec4, or an array of these.
  • GL_INVALID_OPERATION is generated if location is an invalid uniform location for the current program object and location is not equal to -1.
  • GL_INVALID_OPERATION is generated if count is greater than 1 and the indicated uniform variable is not an array variable.
  • GL_INVALID_OPERATION is generated if a sampler is loaded using a command other than glUniform1i and glUniform1iv.

I can't really figure which error could cause it, non of the above seems right. While debugging I see that the uniform values, size, and location that are passed to glUniform4fv are all correct, my shader programs in Binded after it was Linked in the app.cpp, so I'm not sure what's causing this.

I would be thankful to get any help on this.

1
Tip for next time: I saw all that code at the beginning of your question and got bored. I see that there is text after the code, but I have no motivation to read it. (I guess the advice in How to Ask would have been useful this time.)JaMiT

1 Answers

1
votes

Ok I figured it out. The docs say:

count
For the vector (glUniform*v) commands, specifies the number of elements that are to be modified. This should be 1 if the targeted uniform variable is not an array, and 1 or more if it is an array.

It's kind confusing but my mistake is that the target uniform variable is only 1 vec4 and not an array of vec4 and that's what count represent.