Allow all shader files to be loaded only once

Todo: preload
This commit is contained in:
Benau 2016-12-18 10:51:05 +08:00
parent 7894e5c864
commit 2a9656bfdc
9 changed files with 301 additions and 262 deletions

View File

@ -919,7 +919,6 @@ void IrrDriver::applyResolutionSettings()
// FIXME: this load sequence is (mostly) duplicated from main.cpp!!
// That's just error prone
// (we're sure to update main.cpp at some point and forget this one...)
ShaderBase::updateShaders();
VAOManager::getInstance()->kill();
resetTextureTable();
cleanUnicolorTextures();
@ -930,6 +929,7 @@ void IrrDriver::applyResolutionSettings()
}
delete m_renderer;
initDevice();
ShaderBase::updateShaders();
font_manager = new FontManager();
font_manager->loadFonts();

View File

@ -19,198 +19,16 @@
#ifndef SERVER_ONLY
#include "graphics/shader.hpp"
#include "graphics/central_settings.hpp"
#include "graphics/gl_headers.hpp"
#include "graphics/irr_driver.hpp"
#include "graphics/shared_gpu_objects.hpp"
#include "graphics/spherical_harmonics.hpp"
#include "io/file_manager.hpp"
#include "utils/log.hpp"
#include <fstream>
#include <sstream>
#include <stdio.h>
std::string ShaderBase::m_shader_header = "";
std::vector<void(*)()> ShaderBase::m_all_kill_functions;
// ----------------------------------------------------------------------------
/** Returns a string with the content of header.txt (which contains basic
* shader defines).
*/
const std::string& ShaderBase::getHeader()
{
// Only read file first time
if (m_shader_header.empty())
{
std::ifstream stream(file_manager->getShader("header.txt"), std::ios::in);
if (stream.is_open())
{
std::string line = "";
while (getline(stream, line))
m_shader_header += "\n" + line;
stream.close();
}
} // if m_shader_header.empty()
return m_shader_header;
} // getHeader
// ----------------------------------------------------------------------------
/** Loads a single shader.
* \param file Filename of the shader to load.
* \param type Type of the shader.
*/
GLuint ShaderBase::loadShader(const std::string &file, unsigned type)
{
GLuint id = glCreateShader(type);
std::ostringstream code;
#if !defined(USE_GLES2)
code << "#version " << CVS->getGLSLVersion()<<"\n";
#else
if (CVS->isGLSL())
code << "#version 300 es\n";
#endif
#if !defined(USE_GLES2)
// Some drivers report that the compute shaders extension is available,
// but they report only OpenGL 3.x version, and thus these extensions
// must be enabled manually. Otherwise the shaders compilation will fail
// because STK tries to use extensions which are available, but disabled
// by default.
if (type == GL_COMPUTE_SHADER)
{
if (CVS->isARBComputeShaderUsable())
code << "#extension GL_ARB_compute_shader : enable\n";
if (CVS->isARBImageLoadStoreUsable())
code << "#extension GL_ARB_shader_image_load_store : enable\n";
if (CVS->isARBArraysOfArraysUsable())
code << "#extension GL_ARB_arrays_of_arrays : enable\n";
}
#endif
if (CVS->isAMDVertexShaderLayerUsable())
code << "#extension GL_AMD_vertex_shader_layer : enable\n";
if (CVS->isARBExplicitAttribLocationUsable())
code << "#extension GL_ARB_explicit_attrib_location : enable\n";
if (CVS->isAZDOEnabled())
{
code << "#extension GL_ARB_bindless_texture : enable\n";
code << "#define Use_Bindless_Texture\n";
}
code << "//" << file << "\n";
if (!CVS->isARBUniformBufferObjectUsable())
code << "#define UBO_DISABLED\n";
if (CVS->isAMDVertexShaderLayerUsable())
code << "#define VSLayer\n";
if (CVS->needsRGBBindlessWorkaround())
code << "#define SRGBBindlessFix\n";
#if !defined(USE_GLES2)
//shader compilation fails with some drivers if there is no precision qualifier
if (type == GL_FRAGMENT_SHADER)
code << "precision mediump float;\n";
#else
int range[2], precision;
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, range, &precision);
if (precision > 0)
code << "precision highp float;\n";
else
code << "precision mediump float;\n";
#endif
code << "#define MAX_BONES " << SharedGPUObjects::getMaxMat4Size() << "\n";
code << getHeader();
std::ifstream stream(file_manager->getShader(file), std::ios::in);
if (stream.is_open())
{
std::string Line = "";
while (getline(stream, Line))
{
const std::string stk_include = "#stk_include";
std::size_t pos = Line.find(stk_include);
if (pos != std::string::npos)
{
std::size_t pos = Line.find("\"");
if (pos == std::string::npos)
{
Log::error("shader", "Invalid #stk_include line: '%s'.", Line.c_str());
continue;
}
std::string filename = Line.substr(pos+1);
pos = filename.find("\"");
if (pos == std::string::npos)
{
Log::error("shader", "Invalid #stk_include line: '%s'.", Line.c_str());
continue;
}
filename = filename.substr(0, pos);
std::ifstream include_stream(file_manager->getShader(filename), std::ios::in);
if (!include_stream.is_open())
{
Log::error("shader", "Couldn't open included shader: '%s'.", filename.c_str());
continue;
}
std::string include_line = "";
while (getline(include_stream, include_line))
{
code << "\n" << include_line;
}
include_stream.close();
}
else
{
code << "\n" << Line;
}
}
stream.close();
}
else
{
Log::error("shader", "Can not open '%s'.", file.c_str());
}
Log::info("shader", "Compiling shader : %s", file.c_str());
const std::string &source = code.str();
char const *source_pointer = source.c_str();
int len = source.size();
glShaderSource(id, 1, &source_pointer, &len);
glCompileShader(id);
GLint result = GL_FALSE;
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE)
{
int info_length;
Log::error("GLWrap", "Error in shader %s", file.c_str());
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &info_length);
if (info_length<0)
info_length = 1024;
char *error_message = new char[info_length];
error_message[0] = 0;
glGetShaderInfoLog(id, info_length, NULL, error_message);
Log::error("GLWrap", error_message);
delete[] error_message;
}
glGetError();
return id;
} // loadShader
// ----------------------------------------------------------------------------
/** Loads a transform feedback buffer shader with a given number of varying
* parameters.
@ -239,7 +57,7 @@ int ShaderBase::loadTFBProgram(const std::string &shader_name,
glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, &info_log_length);
char *error_message = new char[info_log_length];
glGetProgramInfoLog(m_program, info_log_length, NULL, error_message);
Log::error("GLWrap", error_message);
Log::error("ShaderBase", error_message);
delete[] error_message;
}

View File

@ -22,6 +22,7 @@
#include "graphics/central_settings.hpp"
#include "graphics/gl_headers.hpp"
#include "graphics/shader_files_manager.hpp"
#include "graphics/shared_gpu_objects.hpp"
#include "utils/singleton.hpp"
@ -40,13 +41,6 @@
*/
class ShaderBase
{
private:
// Static members
/** Stores the context of header.txt, to avoid reading
* this file repeatedly. */
static std::string m_shader_header;
protected:
/** Maintains a list of all shaders. */
static std::vector<void (*)()> m_all_kill_functions;
@ -75,8 +69,12 @@ protected:
void loadAndAttachShader(GLint shader_type, const std::string &name,
Types ... args)
{
GLint shader_id = loadShader(name, shader_type);
GLint shader_id = ShaderFilesManager::getInstance()
->getShaderFile(name, shader_type);
glAttachShader(m_program, shader_id);
GLint is_deleted = GL_TRUE;
glGetShaderiv(shader_id, GL_DELETE_STATUS, &is_deleted);
if (is_deleted == GL_FALSE)
glDeleteShader(shader_id);
loadAndAttachShader(args...);
} // loadAndAttachShader
@ -89,9 +87,6 @@ protected:
loadAndAttachShader(shader_type, std::string(name), args...);
} // loadAndAttachShader
// ------------------------------------------------------------------------
const std::string& getHeader();
GLuint loadShader(const std::string &file, unsigned type);
void setAttribute(AttributeType type);
public:

View File

@ -681,6 +681,7 @@ ShaderBasedRenderer::~ShaderBasedRenderer()
delete m_spherical_harmonics;
delete m_skybox;
delete m_rtts;
ShaderFilesManager::kill();
}
// ----------------------------------------------------------------------------

View File

@ -0,0 +1,228 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef SERVER_ONLY
#include "graphics/shader_files_manager.hpp"
#include "graphics/central_settings.hpp"
#include "graphics/shared_gpu_objects.hpp"
#include "io/file_manager.hpp"
#include "utils/log.hpp"
#include <fstream>
#include <sstream>
std::string ShaderFilesManager::m_shader_header = "";
// ----------------------------------------------------------------------------
/** Returns a string with the content of header.txt (which contains basic
* shader defines).
*/
const std::string& ShaderFilesManager::getHeader()
{
// Only read file first time
if (m_shader_header.empty())
{
std::ifstream stream(file_manager->getShader("header.txt"),
std::ios::in);
if (stream.is_open())
{
std::string line = "";
while (getline(stream, line))
m_shader_header += "\n" + line;
stream.close();
}
} // if m_shader_header.empty()
return m_shader_header;
} // getHeader
// ----------------------------------------------------------------------------
/** Loads a single shader file, and add it to the loaded list
* \param file Filename of the shader to load.
* \param type Type of the shader.
*/
GLuint ShaderFilesManager::addShaderFile(const std::string &file,
unsigned type)
{
GLuint id = glCreateShader(type);
std::ostringstream code;
#if !defined(USE_GLES2)
code << "#version " << CVS->getGLSLVersion()<<"\n";
#else
if (CVS->isGLSL())
code << "#version 300 es\n";
#endif
#if !defined(USE_GLES2)
// Some drivers report that the compute shaders extension is available,
// but they report only OpenGL 3.x version, and thus these extensions
// must be enabled manually. Otherwise the shaders compilation will fail
// because STK tries to use extensions which are available, but disabled
// by default.
if (type == GL_COMPUTE_SHADER)
{
if (CVS->isARBComputeShaderUsable())
code << "#extension GL_ARB_compute_shader : enable\n";
if (CVS->isARBImageLoadStoreUsable())
code << "#extension GL_ARB_shader_image_load_store : enable\n";
if (CVS->isARBArraysOfArraysUsable())
code << "#extension GL_ARB_arrays_of_arrays : enable\n";
}
#endif
if (CVS->isAMDVertexShaderLayerUsable())
code << "#extension GL_AMD_vertex_shader_layer : enable\n";
if (CVS->isARBExplicitAttribLocationUsable())
code << "#extension GL_ARB_explicit_attrib_location : enable\n";
if (CVS->isAZDOEnabled())
{
code << "#extension GL_ARB_bindless_texture : enable\n";
code << "#define Use_Bindless_Texture\n";
}
code << "//" << file << "\n";
if (!CVS->isARBUniformBufferObjectUsable())
code << "#define UBO_DISABLED\n";
if (CVS->isAMDVertexShaderLayerUsable())
code << "#define VSLayer\n";
if (CVS->needsRGBBindlessWorkaround())
code << "#define SRGBBindlessFix\n";
#if !defined(USE_GLES2)
// shader compilation fails with some drivers if there is no precision
// qualifier
if (type == GL_FRAGMENT_SHADER)
code << "precision mediump float;\n";
#else
int range[2], precision;
glGetShaderPrecisionFormat(GL_FRAGMENT_SHADER, GL_HIGH_FLOAT, range,
&precision);
if (precision > 0)
code << "precision highp float;\n";
else
code << "precision mediump float;\n";
#endif
code << "#define MAX_BONES " << SharedGPUObjects::getMaxMat4Size() << "\n";
code << getHeader();
std::ifstream stream(file_manager->getShader(file), std::ios::in);
if (stream.is_open())
{
std::string line = "";
while (getline(stream, line))
{
const std::string stk_include = "#stk_include";
std::size_t pos = line.find(stk_include);
if (pos != std::string::npos)
{
std::size_t pos = line.find("\"");
if (pos == std::string::npos)
{
Log::error("ShaderFilesManager", "Invalid #stk_include"
" line: '%s'.", line.c_str());
continue;
}
std::string filename = line.substr(pos + 1);
pos = filename.find("\"");
if (pos == std::string::npos)
{
Log::error("ShaderFilesManager", "Invalid #stk_include"
" line: '%s'.", line.c_str());
continue;
}
filename = filename.substr(0, pos);
std::ifstream include_stream(file_manager->getShader(filename),
std::ios::in);
if (!include_stream.is_open())
{
Log::error("ShaderFilesManager", "Couldn't open included"
" shader: '%s'.", filename.c_str());
continue;
}
std::string include_line = "";
while (getline(include_stream, include_line))
{
code << "\n" << include_line;
}
include_stream.close();
}
else
{
code << "\n" << line;
}
}
stream.close();
}
else
{
Log::error("ShaderFilesManager", "Can not open '%s'.", file.c_str());
}
Log::info("ShaderFilesManager", "Compiling shader : %s", file.c_str());
const std::string &source = code.str();
char const *source_pointer = source.c_str();
int len = source.size();
glShaderSource(id, 1, &source_pointer, &len);
glCompileShader(id);
GLint result = GL_FALSE;
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
if (result == GL_FALSE)
{
int info_length;
Log::error("ShaderFilesManager", "Error in shader %s", file.c_str());
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &info_length);
if (info_length < 0)
info_length = 1024;
char *error_message = new char[info_length];
error_message[0] = 0;
glGetShaderInfoLog(id, info_length, NULL, error_message);
Log::error("ShaderFilesManager", error_message);
delete[] error_message;
}
glGetError();
m_shader_files_loaded[file] = id;
return id;
} // addShaderFile
// ----------------------------------------------------------------------------
GLuint ShaderFilesManager::getShaderFile(const std::string &file,
unsigned type)
{
std::unordered_map<std::string, GLuint>::const_iterator i =
m_shader_files_loaded.find(file);
if (i != m_shader_files_loaded.end())
return i->second;
else
return addShaderFile(file, type);
} // getShaderFile
#endif // !SERVER_ONLY

View File

@ -0,0 +1,59 @@
// SuperTuxKart - a fun racing game with go-kart
// Copyright (C) 2016 SuperTuxKart-Team
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#ifndef SERVER_ONLY
#ifndef HEADER_SHADER_FILES_MANAGER_HPP
#define HEADER_SHADER_FILES_MANAGER_HPP
#include "graphics/gl_headers.hpp"
#include "utils/no_copy.hpp"
#include "utils/singleton.hpp"
#include <algorithm>
#include <string>
#include <unordered_map>
class ShaderFilesManager : public Singleton<ShaderFilesManager>, NoCopy
{
private:
/** Stores the context of header.txt, to avoid reading
* this file repeatedly. */
static std::string m_shader_header;
std::unordered_map<std::string, GLuint> m_shader_files_loaded;
// ------------------------------------------------------------------------
const std::string& getHeader();
public:
// ------------------------------------------------------------------------
ShaderFilesManager() {}
// ------------------------------------------------------------------------
~ShaderFilesManager() { clean(); }
// ------------------------------------------------------------------------
void clean() { m_shader_files_loaded.clear(); }
// ------------------------------------------------------------------------
GLuint addShaderFile(const std::string &file, unsigned type);
// ------------------------------------------------------------------------
GLuint getShaderFile(const std::string &file, unsigned type);
}; // ShaderFilesManager
#endif
#endif // !SERVER_ONLY

View File

@ -174,69 +174,6 @@ static std::string loadHeader()
return result;
} // loadHeader
// ----------------------------------------------------------------------------
// Mostly from shader tutorial
GLuint loadShader(const char * file, unsigned type)
{
GLuint Id = glCreateShader(type);
char versionString[20];
#if !defined(USE_GLES2)
sprintf(versionString, "#version %d\n", CVS->getGLSLVersion());
#else
if (CVS->isGLSL())
sprintf(versionString, "#version 300 es\n");
#endif
std::string Code = versionString;
if (CVS->isAMDVertexShaderLayerUsable())
Code += "#extension GL_AMD_vertex_shader_layer : enable\n";
if (CVS->isAZDOEnabled())
{
Code += "#extension GL_ARB_bindless_texture : enable\n";
Code += "#define Use_Bindless_Texture\n";
}
std::ifstream Stream(file, std::ios::in);
Code += "//" + std::string(file) + "\n";
if (!CVS->isARBUniformBufferObjectUsable())
Code += "#define UBO_DISABLED\n";
if (CVS->isAMDVertexShaderLayerUsable())
Code += "#define VSLayer\n";
if (CVS->needsRGBBindlessWorkaround())
Code += "#define SRGBBindlessFix\n";
Code += loadHeader();
if (Stream.is_open())
{
std::string Line = "";
while (getline(Stream, Line))
Code += "\n" + Line;
Stream.close();
}
GLint Result = GL_FALSE;
int InfoLogLength;
Log::info("GLWrap", "Compiling shader : %s", file);
char const * SourcePointer = Code.c_str();
int length = (int)strlen(SourcePointer);
glShaderSource(Id, 1, &SourcePointer, &length);
glCompileShader(Id);
glGetShaderiv(Id, GL_COMPILE_STATUS, &Result);
if (Result == GL_FALSE)
{
Log::error("GLWrap", "Error in shader %s", file);
glGetShaderiv(Id, GL_INFO_LOG_LENGTH, &InfoLogLength);
if (InfoLogLength<0)
InfoLogLength = 1024;
char *ErrorMessage = new char[InfoLogLength];
ErrorMessage[0] = 0;
glGetShaderInfoLog(Id, InfoLogLength, NULL, ErrorMessage);
Log::error("GLWrap", ErrorMessage);
delete[] ErrorMessage;
}
glGetError();
return Id;
} // loadShader
// ----------------------------------------------------------------------------
void Shaders::loadShaders()
{

View File

@ -236,6 +236,7 @@ bool handleContextMenuAction(s32 cmd_id)
case DEBUG_GRAPHICS_RELOAD_SHADERS:
#ifndef SERVER_ONLY
Log::info("Debug", "Reloading shaders...");
ShaderFilesManager::getInstance()->clean();
ShaderBase::updateShaders();
#endif
break;