Add proper sharing and deletion of shader files

This commit is contained in:
Benau 2018-01-19 14:41:33 +08:00
parent 105935c645
commit 0f95d36dbc
7 changed files with 120 additions and 50 deletions

View File

@ -54,6 +54,7 @@ protected:
/** OpenGL's program id. */ /** OpenGL's program id. */
GLuint m_program; GLuint m_program;
std::vector<std::shared_ptr<GLuint> > m_shaders;
// ======================================================================== // ========================================================================
/** Ends recursion. */ /** Ends recursion. */
@ -67,13 +68,13 @@ protected:
void loadAndAttachShader(GLint shader_type, const std::string &name, void loadAndAttachShader(GLint shader_type, const std::string &name,
Types ... args) Types ... args)
{ {
GLint shader_id = ShaderFilesManager::getInstance() auto shader_id = ShaderFilesManager::getInstance()
->getShaderFile(name, shader_type); ->getShaderFile(name, shader_type);
glAttachShader(m_program, shader_id); if (shader_id)
GLint is_deleted = GL_TRUE; {
glGetShaderiv(shader_id, GL_DELETE_STATUS, &is_deleted); m_shaders.push_back(shader_id);
if (is_deleted == GL_FALSE) glAttachShader(m_program, *shader_id);
glDeleteShader(shader_id); }
loadAndAttachShader(args...); loadAndAttachShader(args...);
} // loadAndAttachShader } // loadAndAttachShader
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -87,6 +88,10 @@ protected:
public: public:
ShaderBase(); ShaderBase();
~ShaderBase()
{
glDeleteProgram(m_program);
}
int loadTFBProgram(const std::string &vertex_file_path, int loadTFBProgram(const std::string &vertex_file_path,
const char **varyings, const char **varyings,
unsigned varyingscount); unsigned varyingscount);
@ -346,7 +351,8 @@ public:
GLint Result = GL_FALSE; GLint Result = GL_FALSE;
glGetProgramiv(m_program, GL_LINK_STATUS, &Result); glGetProgramiv(m_program, GL_LINK_STATUS, &Result);
if (Result == GL_FALSE) { if (Result == GL_FALSE)
{
int info_length; int info_length;
Log::error("Shader", "Error when linking these shaders :"); Log::error("Shader", "Error when linking these shaders :");
printFileList(args...); printFileList(args...);
@ -356,6 +362,11 @@ public:
Log::error("Shader", error_message); Log::error("Shader", error_message);
delete[] error_message; delete[] error_message;
} }
// After linking all shaders can be detached
for (auto shader : m_shaders)
{
glDetachShader(m_program, *shader);
}
} // loadProgram } // loadProgram
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------

View File

@ -639,9 +639,9 @@ ShaderBasedRenderer::~ShaderBasedRenderer()
delete m_spherical_harmonics; delete m_spherical_harmonics;
delete m_skybox; delete m_skybox;
delete m_rtts; delete m_rtts;
ShaderFilesManager::kill();
ShaderBase::killShaders(); ShaderBase::killShaders();
SP::destroy(); SP::destroy();
ShaderFilesManager::kill();
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -54,9 +54,11 @@ const std::string& ShaderFilesManager::getHeader()
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
void ShaderFilesManager::readFile(const std::string& file, void ShaderFilesManager::readFile(const std::string& file,
std::ostringstream& code) std::ostringstream& code, bool not_header)
{ {
std::ifstream stream(file_manager->getShader(file), std::ios::in); std::ifstream stream(((file.find('/') != std::string::npos ||
file.find('\\') != std::string::npos) && not_header) ?
file : file_manager->getShader(file), std::ios::in);
if (!stream.is_open()) if (!stream.is_open())
{ {
@ -72,6 +74,7 @@ void ShaderFilesManager::readFile(const std::string& file,
const std::size_t pos = line.find(stk_include); const std::size_t pos = line.find(stk_include);
// load the custom file pointed by the #stk_include directive // load the custom file pointed by the #stk_include directive
// we only look for #stk_include in official shader directory
if (pos != std::string::npos) if (pos != std::string::npos)
{ {
// find the start " // find the start "
@ -97,7 +100,7 @@ void ShaderFilesManager::readFile(const std::string& file,
filename = filename.substr(0, pos); filename = filename.substr(0, pos);
// read the whole include file // read the whole include file
readFile(filename, code); readFile(filename, code, false/*not_header*/);
} }
else else
{ {
@ -113,9 +116,16 @@ void ShaderFilesManager::readFile(const std::string& file,
* \param file Filename of the shader to load. * \param file Filename of the shader to load.
* \param type Type of the shader. * \param type Type of the shader.
*/ */
GLuint ShaderFilesManager::loadShader(const std::string &file, unsigned type) SharedShader ShaderFilesManager::loadShader(const std::string& full_path,
unsigned type)
{ {
const GLuint id = glCreateShader(type); GLuint* ss_ptr = new GLuint;
*ss_ptr = glCreateShader(type);
SharedShader ss(ss_ptr, [](GLuint* ss)
{
glDeleteShader(*ss);
delete ss;
});
std::ostringstream code; std::ostringstream code;
#if !defined(USE_GLES2) #if !defined(USE_GLES2)
@ -156,7 +166,7 @@ GLuint ShaderFilesManager::loadShader(const std::string &file, unsigned type)
code << "#define Converts_10bit_Vector\n"; code << "#define Converts_10bit_Vector\n";
} }
code << "//" << file << "\n"; code << "//" << full_path << "\n";
if (!CVS->isARBUniformBufferObjectUsable()) if (!CVS->isARBUniformBufferObjectUsable())
code << "#define UBO_DISABLED\n"; code << "#define UBO_DISABLED\n";
if (CVS->needsVertexIdWorkaround()) if (CVS->needsVertexIdWorkaround())
@ -191,34 +201,37 @@ GLuint ShaderFilesManager::loadShader(const std::string &file, unsigned type)
code << getHeader(); code << getHeader();
readFile(file, code); readFile(full_path, code);
Log::info("ShaderFilesManager", "Compiling shader : %s", file.c_str()); Log::info("ShaderFilesManager", "Compiling shader: %s",
full_path.c_str());
const std::string &source = code.str(); const std::string &source = code.str();
char const *source_pointer = source.c_str(); char const *source_pointer = source.c_str();
int len = (int)source.size(); int len = (int)source.size();
glShaderSource(id, 1, &source_pointer, &len); glShaderSource(*ss, 1, &source_pointer, &len);
glCompileShader(id); glCompileShader(*ss);
GLint result = GL_FALSE; GLint result = GL_FALSE;
glGetShaderiv(id, GL_COMPILE_STATUS, &result); glGetShaderiv(*ss, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE) if (result == GL_FALSE)
{ {
// failed to compile // failed to compile
int info_length; int info_length;
Log::error("ShaderFilesManager", "Error in shader %s", file.c_str()); Log::error("ShaderFilesManager", "Error in shader %s",
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &info_length); full_path.c_str());
glGetShaderiv(*ss, GL_INFO_LOG_LENGTH, &info_length);
if (info_length < 0) if (info_length < 0)
info_length = 1024; info_length = 1024;
char *error_message = new char[info_length]; char *error_message = new char[info_length];
error_message[0] = 0; error_message[0] = 0;
glGetShaderInfoLog(id, info_length, NULL, error_message); glGetShaderInfoLog(*ss, info_length, NULL, error_message);
Log::error("ShaderFilesManager", error_message); Log::error("ShaderFilesManager", error_message);
delete[] error_message; delete[] error_message;
return NULL;
} }
glGetError(); glGetError();
return id; return ss;
} // loadShader } // loadShader
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -226,34 +239,39 @@ GLuint ShaderFilesManager::loadShader(const std::string &file, unsigned type)
* \param file Filename of the shader to load. * \param file Filename of the shader to load.
* \param type Type of the shader. * \param type Type of the shader.
*/ */
GLuint ShaderFilesManager::addShaderFile(const std::string &file, unsigned type) SharedShader ShaderFilesManager::addShaderFile(const std::string& full_path,
unsigned type)
{ {
#ifdef DEBUG #ifdef DEBUG
// Make sure no duplicated shader is added somewhere else // Make sure no duplicated shader is added somewhere else
std::unordered_map<std::string, GLuint>::const_iterator i = auto i = m_shader_files_loaded.find(full_path);
m_shader_files_loaded.find(file);
assert(i == m_shader_files_loaded.end()); assert(i == m_shader_files_loaded.end());
#endif #endif
const GLuint id = loadShader(file, type); SharedShader ss = loadShader(full_path, type);
m_shader_files_loaded[file] = id; m_shader_files_loaded[full_path] = ss;
return id; return ss;
} // addShaderFile } // addShaderFile
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
/** Get a shader file. If the shader is not already in the cache it will be loaded and cached. /** Get a shader file. If the shader is not already in the cache it will be
* loaded and cached.
* \param file Filename of the shader to load. * \param file Filename of the shader to load.
* \param type Type of the shader. * \param type Type of the shader.
*/ */
GLuint ShaderFilesManager::getShaderFile(const std::string &file, unsigned type) SharedShader ShaderFilesManager::getShaderFile(const std::string &file,
unsigned type)
{ {
const std::string full_path = (file.find('/') != std::string::npos ||
file.find('\\') != std::string::npos) ?
file : file_manager->getShader(file);
// found in cache // found in cache
auto it = m_shader_files_loaded.find(file); auto it = m_shader_files_loaded.find(full_path);
if (it != m_shader_files_loaded.end()) if (it != m_shader_files_loaded.end())
return it->second; return it->second;
// add to the cache now // add to the cache now
return addShaderFile(file, type); return addShaderFile(full_path, type);
} // getShaderFile } // getShaderFile
#endif // !SERVER_ONLY #endif // !SERVER_ONLY

View File

@ -25,34 +25,63 @@
#include "utils/singleton.hpp" #include "utils/singleton.hpp"
#include <algorithm> #include <algorithm>
#include <cassert>
#include <string> #include <string>
#include <memory>
#include <unordered_map> #include <unordered_map>
typedef std::shared_ptr<GLuint> SharedShader;
class ShaderFilesManager : public Singleton<ShaderFilesManager>, NoCopy class ShaderFilesManager : public Singleton<ShaderFilesManager>, NoCopy
{ {
private: private:
/** /**
* Map from a filename to a shader indentifier. Used for caching shaders. * Map from a filename to a shader indentifier. Used for caching shaders.
*/ */
std::unordered_map<std::string, GLuint> m_shader_files_loaded; std::unordered_map<std::string, SharedShader> m_shader_files_loaded;
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
const std::string& getHeader(); const std::string& getHeader();
void readFile(const std::string& file, std::ostringstream& code); // ------------------------------------------------------------------------
void readFile(const std::string& file, std::ostringstream& code,
bool not_header = true);
// ------------------------------------------------------------------------
SharedShader addShaderFile(const std::string& full_path, unsigned type);
public: public:
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
ShaderFilesManager() {} ShaderFilesManager() {}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
~ShaderFilesManager() { clean(); } ~ShaderFilesManager()
{
clearAllShaderFiles();
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
void clean() { m_shader_files_loaded.clear(); } void clearAllShaderFiles()
{
clearUnusedShaderFiles();
assert(m_shader_files_loaded.empty());
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
GLuint loadShader(const std::string &file, unsigned type); void clearUnusedShaderFiles()
{
for (auto it = m_shader_files_loaded.begin();
it != m_shader_files_loaded.end();)
{
if (it->second.use_count() == 1 || !it->second)
{
it = m_shader_files_loaded.erase(it);
}
else
{
it++;
}
}
}
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
GLuint addShaderFile(const std::string &file, unsigned type); SharedShader loadShader(const std::string& full_path, unsigned type);
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
GLuint getShaderFile(const std::string &file, unsigned type); SharedShader getShaderFile(const std::string& file, unsigned type);
}; // ShaderFilesManager }; // ShaderFilesManager

View File

@ -60,13 +60,13 @@ void SPShader::addShaderFile(const std::string& name, GLint shader_type,
RenderPass rp) RenderPass rp)
{ {
#ifndef SERVER_ONLY #ifndef SERVER_ONLY
GLint shader_id = ShaderFilesManager::getInstance() auto shader_id = ShaderFilesManager::getInstance()
->getShaderFile(name, shader_type); ->getShaderFile(name, shader_type);
glAttachShader(m_program[rp], shader_id); if (shader_id)
GLint is_deleted = GL_TRUE; {
glGetShaderiv(shader_id, GL_DELETE_STATUS, &is_deleted); m_shaders.insert(shader_id);
if (is_deleted == GL_FALSE) glAttachShader(m_program[rp], *shader_id);
glDeleteShader(shader_id); }
#endif #endif
} // addShaderFile } // addShaderFile
@ -88,6 +88,14 @@ void SPShader::linkShaderFiles(RenderPass rp)
Log::error("SPShader", error_message); Log::error("SPShader", error_message);
delete[] error_message; delete[] error_message;
} }
// After linking all shaders can be detached
GLuint shaders[10] = {};
GLsizei count = 0;
glGetAttachedShaders(m_program[rp], 10, &count, shaders);
for (unsigned i = 0; i < count; i++)
{
glDetachShader(m_program[rp], shaders[i]);
}
#endif #endif
} // linkShaderFiles } // linkShaderFiles

View File

@ -27,7 +27,9 @@
#include <cstring> #include <cstring>
#include <functional> #include <functional>
#include <ostream> #include <ostream>
#include <memory>
#include <string> #include <string>
#include <unordered_set>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
@ -80,6 +82,8 @@ class SPShader : public NoCopy, public SPPerObjectUniform
private: private:
std::string m_name; std::string m_name;
std::unordered_set<std::shared_ptr<GLuint> > m_shaders;
GLuint m_program[RP_COUNT]; GLuint m_program[RP_COUNT];
std::vector<std::pair<unsigned, unsigned> > m_samplers[RP_COUNT]; std::vector<std::pair<unsigned, unsigned> > m_samplers[RP_COUNT];

View File

@ -248,8 +248,8 @@ bool handleContextMenuAction(s32 cmd_id)
case DEBUG_GRAPHICS_RELOAD_SHADERS: case DEBUG_GRAPHICS_RELOAD_SHADERS:
#ifndef SERVER_ONLY #ifndef SERVER_ONLY
Log::info("Debug", "Reloading shaders..."); Log::info("Debug", "Reloading shaders...");
ShaderFilesManager::getInstance()->clean();
ShaderBase::killShaders(); ShaderBase::killShaders();
ShaderFilesManager::getInstance()->clearAllShaderFiles();
#endif #endif
break; break;
case DEBUG_GRAPHICS_RESET: case DEBUG_GRAPHICS_RESET: