From 2a9656bfdc995ad177067ba0e2a754f914dbddaa Mon Sep 17 00:00:00 2001 From: Benau Date: Sun, 18 Dec 2016 10:51:05 +0800 Subject: [PATCH] Allow all shader files to be loaded only once Todo: preload --- sources.cmake | 2 +- src/graphics/irr_driver.cpp | 2 +- src/graphics/shader.cpp | 184 +------------------- src/graphics/shader.hpp | 19 +-- src/graphics/shader_based_renderer.cpp | 1 + src/graphics/shader_files_manager.cpp | 228 +++++++++++++++++++++++++ src/graphics/shader_files_manager.hpp | 59 +++++++ src/graphics/shaders.cpp | 63 ------- src/utils/debug.cpp | 5 +- 9 files changed, 301 insertions(+), 262 deletions(-) create mode 100644 src/graphics/shader_files_manager.cpp create mode 100644 src/graphics/shader_files_manager.hpp diff --git a/sources.cmake b/sources.cmake index d4f28ae4d..ba4868d71 100644 --- a/sources.cmake +++ b/sources.cmake @@ -1,5 +1,5 @@ # Modify this file to change the last-modified date when you add/remove a file. -# This will then trigger a new cmake run automatically. +# This will then trigger a new cmake run automatically. file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp") file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp") file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*") diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 6b92b20c5..909420162 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -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(); diff --git a/src/graphics/shader.cpp b/src/graphics/shader.cpp index f2f704978..2383cab50 100644 --- a/src/graphics/shader.cpp +++ b/src/graphics/shader.cpp @@ -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 #include #include -std::string ShaderBase::m_shader_header = ""; std::vector 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; } diff --git a/src/graphics/shader.hpp b/src/graphics/shader.hpp index 8e6ab5e93..ac4bd5e2a 100644 --- a/src/graphics/shader.hpp +++ b/src/graphics/shader.hpp @@ -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 m_all_kill_functions; @@ -75,9 +69,13 @@ 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); - glDeleteShader(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: diff --git a/src/graphics/shader_based_renderer.cpp b/src/graphics/shader_based_renderer.cpp index 4c8e3e278..512f74b6f 100644 --- a/src/graphics/shader_based_renderer.cpp +++ b/src/graphics/shader_based_renderer.cpp @@ -681,6 +681,7 @@ ShaderBasedRenderer::~ShaderBasedRenderer() delete m_spherical_harmonics; delete m_skybox; delete m_rtts; + ShaderFilesManager::kill(); } // ---------------------------------------------------------------------------- diff --git a/src/graphics/shader_files_manager.cpp b/src/graphics/shader_files_manager.cpp new file mode 100644 index 000000000..9b4adb40a --- /dev/null +++ b/src/graphics/shader_files_manager.cpp @@ -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 +#include + +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::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 diff --git a/src/graphics/shader_files_manager.hpp b/src/graphics/shader_files_manager.hpp new file mode 100644 index 000000000..9fbba938b --- /dev/null +++ b/src/graphics/shader_files_manager.hpp @@ -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 +#include +#include + +class ShaderFilesManager : public Singleton, NoCopy +{ +private: + /** Stores the context of header.txt, to avoid reading + * this file repeatedly. */ + static std::string m_shader_header; + + std::unordered_map 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 diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp index ed8e6c238..d00b06dbc 100644 --- a/src/graphics/shaders.cpp +++ b/src/graphics/shaders.cpp @@ -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() { diff --git a/src/utils/debug.cpp b/src/utils/debug.cpp index 0d7e351d1..6b4468ef1 100644 --- a/src/utils/debug.cpp +++ b/src/utils/debug.cpp @@ -236,9 +236,10 @@ bool handleContextMenuAction(s32 cmd_id) case DEBUG_GRAPHICS_RELOAD_SHADERS: #ifndef SERVER_ONLY Log::info("Debug", "Reloading shaders..."); - ShaderBase::updateShaders(); + ShaderFilesManager::getInstance()->clean(); + ShaderBase::updateShaders(); #endif - break; + break; case DEBUG_GRAPHICS_RESET: if (physics) physics->setDebugMode(IrrDebugDrawer::DM_NONE);