Шейдерная программа 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 шт):
Ладно, после стольких попыток ТС все таки дополнил код, что бы можно было предположить что там не так, однако, хочу сказать сразу, что проблема не воспроизвелась, быть может снова, ТС что-то скрывает, и не договаривает.
Написал рабочий код:
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
Спасибо @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.