Шейдерная программа OpenGL перестала прикреплять к себе шейдеры

Во время переноса библиотеки из заголовочной в простую (сборка через cmake, тип *.a) у меня возникла проблема. После переноса класса Program, появилась ошибка error: no shaders attached to the program. Вот старая реализация класса програм (покажу только изменения) и новая реализация, а также класс шейдер. Код:

#include <iostream>
#include <vector>
#include <glad/glad.h>
#include <GLFW/glfw3.h>

using namespace std;

const GLchar* shader1 = "#version 460 core\nout vec4 FragColor;\nvoid main() { FragColor = vec4(1.0); }";
const GLchar* shader2 = "#version 460 core\nvoid main() { gl_Position = vec4(1.0); }";

#define LogSize 1024

// class Shader
class Shader
{
  public:
    GLuint shader;
    Shader(GLenum type, const GLchar* source)
    {
        shader = glCreateShader(type);
        glShaderSource(shader, 1, &source, NULL);
        glCompileShader(shader);
        GLint error = 0; GLchar log[LogSize];
        glGetShaderiv(shader, GL_COMPILE_STATUS, &error);
        if (error == GL_FALSE)
        {
            glGetShaderInfoLog(shader, LogSize, NULL, log);
            printf("%s", log);
        }
    }
    void del() { glDeleteShader(shader); }
};

class Program
{
  public:
    GLuint ID;
    Program(vector<Shader> &shaders);
    Program(Shader* shaders, size_t len);
};

// new class Program
Program::Program(vector<Shader> &shaders)
{
    ID = glCreateProgram();
    for (size_t i = 0; i < shaders.size(); i++) glAttachShader(ID, shaders[i].shader);
    glLinkProgram(ID);

    GLint error = 0; GLchar log[LogSize]; // LogSize = 1024
    glGetProgramiv(ID, GL_LINK_STATUS, &error);
    if (error == GL_FALSE) 
    {
        glGetProgramInfoLog(ID, LogSize, NULL, log);
        printf("%s", log);
    }
}
// old class Program
Program::Program(Shader* shaders, size_t len)
{
    ID = glCreateProgram();
    for (size_t i = 0; i < len; i++) glAttachShader(ID, shaders[i].shader);
    glLinkProgram(ID);

    GLint error = 0; GLchar log[LogSize]; // LogSize = 1024
    glGetProgramiv(ID, GL_LINK_STATUS, &error);
    if (error == GL_FALSE) 
    {
        glGetProgramInfoLog(ID, LogSize, NULL, log);
        printf("%s", log);
    }
 }

void framebuffer_size_callback(GLFWwindow* win, int a, int b) {}
void mouse_callback(GLFWwindow*, double a, double b) {}
void processInput(GLFWwindow* win) {}

int main()
{
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    GLFWwindow* window = glfwCreateWindow(400, 400, "", NULL, NULL);
    if (window == NULL) 
    {
       std::cout << "Fail to create window!";
       glfwTerminate();
       return -1;
    }

    glfwMakeContextCurrent(window);
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    glfwSetCursorPosCallback(window, mouse_callback);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
         std::cout << "Failed to initialize GLAD" << std::endl;
            
 
    // main
    Shader shader1(GL_VERTEX_SHADER, "path to shader");
    Shader shader2(GL_FRAGMENT_SHADER, "path to shader");

    // new program
    vector<Shader> shaders = {shader1, shader2};
    Program program(shaders);
    // old program
    Shader shaders2[] = {shader1, shader2};
    Program program2(shaders2, 2);

    // и в старой и в новой
    shader1.del();
    shader2.del();
}

Как видно, код почти не поменялся, но теперь при возвращении к старой версии ничего не работает. Помогите пожалуйста, я не пойму почему оно перестало работать и небольшое уточнение, я использую glad, glfw3 и OpenGL 4.6. Вот команда для компиляции ```g++ (или любой другой компилятор) main.cpp -o main -I путь_к_заголовкам_glfw3_и_glad -L путь_к_библиотека_glfw3_и_glad -lglad -lglfw3


Ответы (2 шт):

Автор решения: evo

Ладно, после стольких попыток ТС все таки дополнил код, что бы можно было предположить что там не так, однако, хочу сказать сразу, что проблема не воспроизвелась, быть может снова, ТС что-то скрывает, и не договаривает.

Написал рабочий код:

main.cxx:

#include <vector>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <open/gl/shader.hxx>
#include <open/gl/program.hxx>
#include <open/gl/vertex_shader.hxx>
#include <open/gl/fragment_shader.hxx>

static std::string fragment_source
  = "#version 460 core\nout vec4 FragColor;\nvoid main() { FragColor = vec4(1.0); }";

static std::string vertex_source
  = "#version 460 core\nvoid main() { gl_Position = vec4(1.0); }";

int
main(int argc, char *argv[]) {
  glfwInit();

  glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
  glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
  glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

  GLFWwindow* window = glfwCreateWindow(640, 480, "Some Window", NULL, NULL);
  glfwMakeContextCurrent(window);

  gladLoadGLLoader((GLADloadproc) glfwGetProcAddress);

  std::vector<open::gl::shader *> shaders {
    new open::gl::vertex_shader(vertex_source),
    new open::gl::fragment_shader(fragment_source))
  };

  open::gl::program program(shaders);

  for (open::gl::shader *shader : shaders) {
    delete shader;
  }

  shaders.clear();
}

shader.hxx:

#pragma once

#include <string>
#include <filesystem>
#include <glad/glad.h>

namespace open::gl {
  struct shader {
    using handle = ::GLuint;

    handle id() const;

    ~shader();

    shader(shader &&other);
    shader(shader const &) = delete;
    shader &operator =(shader &&other);
    shader &operator =(shader const &) = delete;

  protected:
    enum class type {
      vertex = GL_VERTEX_SHADER,
      fragment = GL_FRAGMENT_SHADER
    };

    shader(shader::type type);
    shader(shader::type type, std::filesystem::path const &path);
    shader(shader::type type, std::string const &src);

    void set_source(std::string const &src) const;
    void compile();

  private:
    handle m_handle_;
  };
} // namespace open::gl

shader.cxx:

#include "shader.hxx"
#include "utils.hxx"
#include <utility>

namespace open::gl {
  shader::handle
  shader::id() const {
    return m_handle_;
  }

  shader::~shader() {
    glDeleteShader(m_handle_);
  }

  shader::shader(shader &&other)
    : m_handle_(std::exchange(other.m_handle_, 0))
  { }

  shader
  &shader::operator=(shader &&other) {
    if (&other != this) {
      m_handle_ = std::exchange(other.m_handle_, 0);
    }
    return *this;
  }

  shader::shader(shader::type type)
    : m_handle_{glCreateShader(static_cast<GLuint>(type))} {
    if (!glIsShader(m_handle_)) {
      throw std::runtime_error("Failed to create shader");
    }
  }

  shader::shader(shader::type type, std::filesystem::path const &path)
      : shader(type, utils::path_to_content(path))
  { }

  shader::shader(shader::type type, std::string const &src)
    : shader(type) {
    set_source(src);
    compile();
  }

  void
  shader::set_source(std::string const &src) const {
    GLchar const *data = src.data();
    GLint data_size = src.length();
    glShaderSource(m_handle_, 1, &data, &data_size);
  }

  void
  shader::compile() {
    glCompileShader(m_handle_);
    // need some more checks for error's call glGetError()

    GLint has_compiled {};
    glGetShaderiv(m_handle_, GL_COMPILE_STATUS, &has_compiled);

    if (GL_TRUE != has_compiled) {
      GLint error_size {};
      glGetShaderiv(m_handle_, GL_INFO_LOG_LENGTH, &error_size);
      std::string error_message(static_cast<size_t>(error_size + 1), '\0');
      glGetShaderInfoLog(m_handle_, error_size, nullptr, error_message.data());
      glDeleteShader(m_handle_);
      m_handle_ = 0;
      throw std::runtime_error("Shader failed to compile with reason: " + error_message);
    }
  }
} // namespace open::gl

program.hxx:

#pragma once

#include <vector>
#include <glad/glad.h>

namespace open::gl {
  struct shader;

  struct program {
    using handle = GLuint;

    program();
    program(shader const &shader);
    program(std::vector<open::gl::shader *> const &shaders);

    handle id() const;
    void attach(open::gl::shader const &shader);
    void link() const;

    program(program && other) noexcept;
    program(program const &other) = delete;

    program &operator =(program const &other) = delete;
    program &operator =(program &&other) noexcept;

  private:
    handle m_handle_;
  };
} // namespace open::gl

program.cxx:

#include "program.hxx"
#include "shader.hxx"
#include <utility>
#include <stdexcept>

namespace open::gl {
  program::program()
    : m_handle_(glCreateProgram()) {
    // error checking...
  }

  program::program(open::gl::shader const &shader)
    : program() {
    attach(shader);
    link();
  }

  program::program(std::vector<open::gl::shader *> const &shaders)
    : program() {
    for (open::gl::shader const *shader : shaders) {
      attach(*shader);
    }

    link();
  }

  program::handle
  program::id() const {
    return m_handle_;
  }

  void program::attach(open::gl::shader const &shader) {
    glAttachShader(m_handle_, shader.id());
    // error checking...
  }

  void program::link() const {
    glLinkProgram(m_handle_);
    GLint compile_status {};

    glGetProgramiv(m_handle_, GL_LINK_STATUS, &compile_status);

    if (!compile_status) {
      size_t log_length {};
      glGetProgramiv(m_handle_, GL_INFO_LOG_LENGTH,
                     reinterpret_cast<GLint *>(&log_length));
      if (log_length > 0) {
        std::string error_message(log_length, '\0');
        glGetProgramInfoLog(m_handle_, log_length, nullptr, error_message.data());
        throw std::runtime_error("Failed to link program with reason :" + error_message);
      }
      throw std::runtime_error("Failed to link program with unknown reason");
    }
  }

  program::program(program &&other) noexcept
    : m_handle_(std::exchange(other.m_handle_, 0))
  { }

  program
  &program::operator =(program &&other) noexcept {
    if (&other != this) {
      m_handle_ = std::exchange(other.m_handle_, 0);
    }

    return *this;
  }

} // namespace open::gl

fragment_shader.hxx:

#pragma once

#include "shader.hxx"

namespace open::gl {
  struct fragment_shader
    : public shader {
    fragment_shader(std::string const &src);
    fragment_shader(std::filesystem::path const &path);
  };
} // namespace open::gl

fragment_shader.cxx:

#include "fragment_shader.hxx"

namespace open::gl {
  fragment_shader::fragment_shader(std::string const &src)
    : shader(shader::type::fragment, src)
  { }

  fragment_shader::fragment_shader(std::filesystem::path const &path)
    : shader(shader::type::fragment, path)
  { }
} // namespace open::gl

vertex_shader.hxx:

#pragma once

#include "shader.hxx"

namespace open::gl {
  struct vertex_shader
    : public shader {
    vertex_shader(std::string const &src);
    vertex_shader(std::filesystem::path const &path);
  };
} // namespace open::gl

vertex_shader.cxx:

#include "vertex_shader.hxx"

namespace open::gl {
  vertex_shader::vertex_shader(std::string const &src)
    : shader(shader::type::vertex, src)
  { }

  vertex_shader::vertex_shader(std::filesystem::path const &path)
    : shader(shader::type::vertex, path)
  { }
} // namespace open::gl

utils.hxx:

#pragma once

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

namespace open::gl::utils {
  std::string
  path_to_content(std::filesystem::path const &path) {
    try {
      std::ifstream resource(path.string());
      std::streamsize file_length
        = resource
            .seekg(0, std::ios::end)
            .tellg();
      resource.seekg(0, std::ios::beg);
      std::string content(file_length, '\0');
      resource.read(content.data(), file_length);
      return content;
    } catch (std::iostream::failure const &ex) {
      std::cerr << "Failed to open input file: " << ex.what() << '\n';
      throw;
    }
  }
} // namespace open::gl::utils
→ Ссылка
Автор решения: jdoseIOO

Спасибо @evo, после его слов "Копирование ресурсов видеокарты, включая шейдеры, не возможна, нельзя копировать класс шейдера, он должен быть уникальным, как и класс программы, у вас быть может удаляется шейдер в деструкторе, и после этого он не пригоден для использования". После его слов я решил изменить свой код. Как было раньше(с ошибкой):

// класс Program
Program::Program(vector<Shader> shaders);

// в main
vector<Shader> shaders = {shader1, shader2, ...};
Program program(shaders);

И это был ошибочный вариант. Я передавал в программу не ссылку на шейдер, а копию шейдера, из-за этого вызывался деструктор который удалял шейдер из памяти видеокаты. В новом варианте я передаю ссылку на шейдер, поэтому деструктор не вызывается. Как сейчас(без ошибки):

// класс Program
Program::Program(vector<Shader*> &shaders);

// в main
vector<Shader*> shaders = {&shader1, &shader2, ...};
Program program(shaders);

Теперь ошибки нет, спасибо за помощь @evo.

→ Ссылка