Browse Source

Extend functionality of the shader_program class and improve its error handling

master
C. J. Howard 3 years ago
parent
commit
5df7608fa8
5 changed files with 240 additions and 54 deletions
  1. +5
    -5
      src/gl/shader-object.cpp
  2. +3
    -4
      src/gl/shader-object.hpp
  3. +127
    -29
      src/gl/shader-program.cpp
  4. +88
    -10
      src/gl/shader-program.hpp
  5. +17
    -6
      src/resources/shader-program-loader.cpp

+ 5
- 5
src/gl/shader-object.cpp View File

@ -50,7 +50,7 @@ shader_object::shader_object(shader_stage stage):
shader_object::~shader_object() shader_object::~shader_object()
{ {
// Flags the OpenGL shader object for deletion
// Flag the OpenGL shader object for deletion
glDeleteShader(gl_shader_id); glDeleteShader(gl_shader_id);
} }
@ -64,11 +64,11 @@ void shader_object::source(const char* buffer, std::size_t size)
switch (glGetError()) switch (glGetError())
{ {
case GL_INVALID_VALUE: case GL_INVALID_VALUE:
throw std::runtime_error("Shader object handle is not a value generated by OpenGL.");
throw std::runtime_error("OpenGL shader object handle is not a value generated by OpenGL.");
break; break;
case GL_INVALID_OPERATION: case GL_INVALID_OPERATION:
throw std::runtime_error("Shader object handle is not a shader object.");
throw std::runtime_error("OpenGL shader object handle is not a shader object.");
break; break;
} }
} }
@ -82,11 +82,11 @@ bool shader_object::compile()
switch (glGetError()) switch (glGetError())
{ {
case GL_INVALID_VALUE: case GL_INVALID_VALUE:
throw std::runtime_error("Shader object handle is not a value generated by OpenGL.");
throw std::runtime_error("OpenGL shader object handle is not a value generated by OpenGL.");
break; break;
case GL_INVALID_OPERATION: case GL_INVALID_OPERATION:
throw std::runtime_error("Shader object handle is not a shader object.");
throw std::runtime_error("OpenGL shader object handle is not a shader object.");
break; break;
} }

+ 3
- 4
src/gl/shader-object.hpp View File

@ -77,7 +77,7 @@ public:
/// Returns the shader stage of this shader object. /// Returns the shader stage of this shader object.
shader_stage get_stage() const; shader_stage get_stage() const;
/// Returns the shader object info log, which is updated when the shader is compiled.
/// Returns the shader object info log, which is updated when the shader object is compiled.
const std::string& get_info_log() const; const std::string& get_info_log() const;
/// Returns `true` if the shader object has been successfully compiled, `false` otherwise. /// Returns `true` if the shader object has been successfully compiled, `false` otherwise.
@ -88,12 +88,11 @@ public:
private: private:
friend class shader_program; friend class shader_program;
unsigned int gl_shader_id; unsigned int gl_shader_id;
shader_stage stage; shader_stage stage;
bool compiled;
std::string info_log; std::string info_log;
bool compiled;
}; };
inline shader_stage shader_object::get_stage() const inline shader_stage shader_object::get_stage() const

+ 127
- 29
src/gl/shader-program.cpp View File

@ -26,42 +26,145 @@
namespace gl { namespace gl {
shader_program::shader_program(const std::list<shader_object*>& shaders):
gl_program_id(0)
shader_program::shader_program():
gl_program_id(0),
linked(false)
{ {
// Create an OpenGL shader program
gl_program_id = glCreateProgram(); gl_program_id = glCreateProgram();
for (shader_object* shader: shaders)
// Handle OpenGL errors
if (!gl_program_id)
{ {
glAttachShader(gl_program_id, shader->gl_shader_id);
throw std::runtime_error("An error occurred while creating an OpenGL shader program.");
} }
}
glLinkProgram(gl_program_id);
shader_program::~shader_program()
{
// Delete shader inputs
free_inputs();
GLint status;
glGetProgramiv(gl_program_id, GL_LINK_STATUS, &status);
if (status == GL_FALSE)
// Detach all shader objects
detach_all();
// Delete the OpenGL shader program
glDeleteProgram(gl_program_id);
}
void shader_program::attach(const shader_object* object)
{
if (attached_objects.find(object) != attached_objects.end())
{ {
throw std::runtime_error(get_info_log().c_str());
throw std::runtime_error("Shader object is already attached to the shader program.");
} }
for (shader_object* shader: shaders)
else
{ {
glDetachShader(gl_program_id, shader->gl_shader_id);
// Check that both the OpenGL shader program and OpenGL shader object are valid
if (glIsProgram(gl_program_id) != GL_TRUE)
throw std::runtime_error("OpenGL shader program is not a valid program object.");
if (glIsShader(object->gl_shader_id) != GL_TRUE)
throw std::runtime_error("OpenGL shader object is not a valid shader object.");
// Attach the OpenGL shader object to the OpenGL shader program
glAttachShader(gl_program_id, object->gl_shader_id);
// Handle OpenGL errors
switch (glGetError())
{
case GL_INVALID_OPERATION:
throw std::runtime_error("OpenGL shader object is already attached to the shader program.");
break;
}
// Add shader object to set of attached objects
attached_objects.insert(object);
} }
}
// Find shader inputs
find_inputs();
void shader_program::detach(const shader_object* object)
{
if (attached_objects.find(object) == attached_objects.end())
{
throw std::runtime_error("Shader object is not attached to the shader program.");
}
else
{
// Check that both the OpenGL shader program and OpenGL shader object are valid
if (glIsProgram(gl_program_id) != GL_TRUE)
throw std::runtime_error("OpenGL shader program is not a valid program object.");
if (glIsShader(object->gl_shader_id) != GL_TRUE)
throw std::runtime_error("OpenGL shader object is not a valid shader object.");
// Detach the OpenGL shader object from the OpenGL shader program
glDetachShader(gl_program_id, object->gl_shader_id);
// Handle OpenGL errors
switch (glGetError())
{
case GL_INVALID_OPERATION:
throw std::runtime_error("OpenGL shader object is not attached to the shader program.");
break;
}
// Remove shader object from set of attached objects
attached_objects.erase(object);
}
} }
shader_program::~shader_program()
void shader_program::detach_all()
{ {
glDeleteProgram(gl_program_id);
while (!attached_objects.empty())
detach(*attached_objects.begin());
}
for (shader_input* input: inputs)
bool shader_program::link()
{
// Check that the OpenGL shader program is valid
if (glIsProgram(gl_program_id) != GL_TRUE)
throw std::runtime_error("OpenGL shader program is not a valid program object.");
// Link OpenGL shader program
glLinkProgram(gl_program_id);
// Handle OpenGL errors
switch (glGetError())
{ {
delete input;
case GL_INVALID_OPERATION:
throw std::runtime_error("OpenGL shader program is the currently active program object and transform feedback mode is active.");
break;
} }
// Get OpenGL shader program linking status
GLint gl_link_status;
glGetProgramiv(gl_program_id, GL_LINK_STATUS, &gl_link_status);
linked = (gl_link_status == GL_TRUE);
// Get OpenGL shader program info log length
GLint gl_info_log_length;
glGetProgramiv(gl_program_id, GL_INFO_LOG_LENGTH, &gl_info_log_length);
if (gl_info_log_length > 0)
{
// Resize string to accommodate OpenGL shader program info log
info_log.resize(gl_info_log_length);
// Read OpenGL shader program info log into string
glGetProgramInfoLog(gl_program_id, gl_info_log_length, &gl_info_log_length, info_log.data());
// Remove redundant null terminator from string
info_log.pop_back();
}
else
{
// Empty info log
info_log.clear();
}
// Find shader inputs
find_inputs();
return linked;
} }
void shader_program::find_inputs() void shader_program::find_inputs()
@ -210,19 +313,14 @@ void shader_program::find_inputs()
delete[] uniform_name; delete[] uniform_name;
} }
std::string shader_program::get_info_log() const
void shader_program::free_inputs()
{ {
GLint length;
glGetProgramiv(gl_program_id, GL_INFO_LOG_LENGTH, &length);
if (length > 0)
for (shader_input* input: inputs)
{ {
std::string log(length, '\0');
glGetProgramInfoLog(gl_program_id, length, &length, &log[0]);
return log;
delete input;
} }
return std::string();
inputs.clear();
} }
} // namespace gl } // namespace gl

+ 88
- 10
src/gl/shader-program.hpp View File

@ -20,10 +20,10 @@
#ifndef ANTKEEPER_GL_SHADER_PROGRAM_HPP #ifndef ANTKEEPER_GL_SHADER_PROGRAM_HPP
#define ANTKEEPER_GL_SHADER_PROGRAM_HPP #define ANTKEEPER_GL_SHADER_PROGRAM_HPP
#include <cstdlib>
#include <list> #include <list>
#include <map>
#include <string> #include <string>
#include <unordered_map>
#include <unordered_set>
namespace gl { namespace gl {
@ -31,16 +31,80 @@ class shader_object;
class rasterizer; class rasterizer;
class shader_input; class shader_input;
/**
* Shader program which can be linked to shader objects and executed.
*
* @see gl::shader_object
*/
class shader_program class shader_program
{ {
public: public:
/** /**
* Creates a shader program.
* Creates an empty shader program.
* *
* @param shaders List of shaders to be linked to the program. Note that after the shader program has been created, these shaders can be deleted if desired with no effect on the shader program.
* @exception std::runtime_error An error occurred while creating an OpenGL shader program.
*/
shader_program();
/**
* Destroys a shader program.
*/ */
explicit shader_program(const std::list<shader_object*>& shaders);
~shader_program(); ~shader_program();
/**
* Attaches a shader object to the shader program. Attaching a shader object has no effect on a shader program until shader_program::link() is called.
*
* @param object Shader object to attach.
*
* @exception std::runtime_error Shader object is already attached to the shader program.
* @exception std::runtime_error OpenGL shader program is not a valid program object.
* @exception std::runtime_error OpenGL shader object is not a valid shader object.
* @exception std::runtime_error OpenGL shader object is already attached to the shader program.
*
* @see shader_program::link()
*/
void attach(const shader_object* object);
/**
* Detaches a shader object from the shader program. Detaching a shader object has no effect on a shader program until shader_program::link() is called.
*
* @param object Shader object to detach.
*
* @exception std::runtime_error Shader object is not attached to the shader program.
* @exception std::runtime_error OpenGL shader program is not a valid program object.
* @exception std::runtime_error OpenGL shader object is not a valid shader object.
* @exception std::runtime_error OpenGL shader object is not attached to the shader program.
*
* @see shader_program::link()
*/
void detach(const shader_object* object);
/**
* Detaches all shader objects from the shader program.
*
* @exception std::runtime_error Shader object is not attached to the shader program.
* @exception std::runtime_error OpenGL shader program is not a valid program object.
* @exception std::runtime_error OpenGL shader object is not a valid shader object.
* @exception std::runtime_error OpenGL shader object is not attached to the shader program.
*
* @see shader_program::detach(const shader_object*)
*/
void detach_all();
/**
* Links all attached shader objects to create an executable shader program.
*
* @warning All existing pointers to a shader program's shader inputs will be invalidated if the program is re-linked.
*
* @return `true` if the attached shader objects were successfully linked into the shader program, `false` otherwise. If linking fails, check the info log via shader_program::get_info_log() for more information.
*/
bool link();
/// Returns the shader program info log, which is updated when the shader program is linked.
const std::string& get_info_log() const;
/// Returns `true` if the shader program has been successfully linked, `false` otherwise.
bool was_linked() const;
shader_program(const shader_program&) = delete; shader_program(const shader_program&) = delete;
shader_program& operator=(const shader_program&) = delete; shader_program& operator=(const shader_program&) = delete;
@ -50,15 +114,30 @@ public:
private: private:
friend class rasterizer; friend class rasterizer;
unsigned int gl_program_id;
std::string info_log;
bool linked;
std::unordered_set<const shader_object*> attached_objects;
void find_inputs(); void find_inputs();
std::string get_info_log() const;
unsigned int gl_program_id;
void free_inputs();
std::list<shader_input*> inputs; std::list<shader_input*> inputs;
std::map<std::string, shader_input*> input_map;
std::unordered_map<std::string, shader_input*> input_map;
}; };
inline const std::string& shader_program::get_info_log() const
{
return info_log;
}
inline bool shader_program::was_linked() const
{
return linked;
}
inline const std::list<shader_input*>* shader_program::get_inputs() const inline const std::list<shader_input*>* shader_program::get_inputs() const
{ {
return &inputs; return &inputs;
@ -78,4 +157,3 @@ inline const shader_input* shader_program::get_input(const std::string& name) co
} // namespace gl } // namespace gl
#endif // ANTKEEPER_GL_SHADER_PROGRAM_HPP #endif // ANTKEEPER_GL_SHADER_PROGRAM_HPP

+ 17
- 6
src/resources/shader-program-loader.cpp View File

@ -188,7 +188,7 @@ gl::shader_program* resource_loader::load(resource_manager*
std::unordered_set<gl::shader_stage> shade_stages = detect_shader_stages(source); std::unordered_set<gl::shader_stage> shade_stages = detect_shader_stages(source);
// Load detected shaders // Load detected shaders
std::list<gl::shader_object*> shaders;
std::list<gl::shader_object*> objects;
for (gl::shader_stage stage: shade_stages) for (gl::shader_stage stage: shade_stages)
{ {
text_file stage_source = *source; text_file stage_source = *source;
@ -199,16 +199,27 @@ gl::shader_program* resource_loader::load(resource_manager*
object->source(source_buffer.c_str(), source_buffer.length()); object->source(source_buffer.c_str(), source_buffer.length());
object->compile(); object->compile();
shaders.push_back(object);
objects.push_back(object);
} }
// Create shader program // Create shader program
gl::shader_program* program = new gl::shader_program(shaders);
gl::shader_program* program = new gl::shader_program();
// Attach shader objects
for (gl::shader_object* object: objects)
program->attach(object);
// Link shader program
if (!program->link())
{
throw std::runtime_error("Shader program linking failed: " + program->get_info_log());
}
// Delete shaders objects
for (gl::shader_object* shader: shaders)
// Detach and delete shader objects
for (gl::shader_object* object: objects)
{ {
delete shader;
program->detach(object);
delete object;
} }
return program; return program;

Loading…
Cancel
Save