From f94a9c6027fd304a5718eeb16d9d4a22b734ab74 Mon Sep 17 00:00:00 2001 From: captainurist <73941350+captainurist@users.noreply.github.com> Date: Sun, 21 Jul 2024 13:53:05 +0100 Subject: [PATCH 1/3] OpenGLShader rework continued --- .../Renderer/NuklearOverlayRenderer.cpp | 53 ++--- .../Renderer/NuklearOverlayRenderer.h | 5 + .../Graphics/Renderer/OpenGLRenderer.cpp | 212 +++++++----------- src/Engine/Graphics/Renderer/OpenGLShader.cpp | 210 +++++++---------- src/Engine/Graphics/Renderer/OpenGLShader.h | 34 +-- 5 files changed, 197 insertions(+), 317 deletions(-) diff --git a/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.cpp b/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.cpp index 5654b70ad00..d476ebeb7ee 100644 --- a/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.cpp +++ b/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.cpp @@ -10,6 +10,7 @@ #include "Library/Logger/Logger.h" #include "Engine/Graphics/Image.h" +#include "Utility/DataPath.h" struct nk_vertex { float position[2]{}; @@ -22,11 +23,6 @@ struct nk_device { struct nk_draw_null_texture null; struct nk_font_atlas atlas; uint32_t vbo{}, vao{}, ebo{}; - int32_t attrib_pos{}; - int32_t attrib_uv{}; - int32_t attrib_col{}; - int32_t uniform_tex{}; - int32_t uniform_proj{}; }; struct nk_state { @@ -72,18 +68,11 @@ void NuklearOverlayRenderer::_initialize(nk_context *context) { } bool NuklearOverlayRenderer::_createDevice() { - _shader.build("nuklear", "glnuklear", _useOGLES); - if (!_shader.isValid()) { - logger->warning("Nuklear shader failed to compile!"); + reloadShaders(_useOGLES); + if (!_shader.isValid()) return false; - } nk_buffer_init_default(&_state->dev.cmds); - _state->dev.uniform_tex = _shader.uniformLocation("Texture"); - _state->dev.uniform_proj = _shader.uniformLocation("ProjMtx"); - _state->dev.attrib_pos = _shader.attribLocation("Position"); - _state->dev.attrib_uv = _shader.attribLocation("TexCoord"); - _state->dev.attrib_col = _shader.attribLocation("Color"); { GLsizei vs = sizeof(struct nk_vertex); size_t vp = offsetof(struct nk_vertex, position); @@ -98,13 +87,13 @@ bool NuklearOverlayRenderer::_createDevice() { glBindBuffer(GL_ARRAY_BUFFER, _state->dev.vbo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _state->dev.ebo); - glEnableVertexAttribArray((GLuint)_state->dev.attrib_pos); - glEnableVertexAttribArray((GLuint)_state->dev.attrib_uv); - glEnableVertexAttribArray((GLuint)_state->dev.attrib_col); + glEnableVertexAttribArray((GLuint)attrib_pos); + glEnableVertexAttribArray((GLuint)attrib_uv); + glEnableVertexAttribArray((GLuint)attrib_col); - glVertexAttribPointer((GLuint)_state->dev.attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void *)vp); - glVertexAttribPointer((GLuint)_state->dev.attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void *)vt); - glVertexAttribPointer((GLuint)_state->dev.attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void *)vc); + glVertexAttribPointer((GLuint)attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void *)vp); + glVertexAttribPointer((GLuint)attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void *)vt); + glVertexAttribPointer((GLuint)attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void *)vc); } glBindTexture(GL_TEXTURE_2D, 0); @@ -165,7 +154,7 @@ void NuklearOverlayRenderer::_cleanup() { nk_font_atlas_clear(&_state->dev.atlas); - _shader.reset(); + _shader.release(); glDeleteBuffers(1, &_state->dev.vbo); glDeleteBuffers(1, &_state->dev.ebo); glDeleteVertexArrays(1, &_state->dev.vao); @@ -215,8 +204,8 @@ void NuklearOverlayRenderer::render(nk_context *context, const Sizei &outputPres /* setup program */ _shader.use(); - glUniform1i(_state->dev.uniform_tex, 0); - glUniformMatrix4fv(_state->dev.uniform_proj, 1, GL_FALSE, &ortho[0][0]); + glUniform1i(uniform_tex, 0); + glUniformMatrix4fv(uniform_proj, 1, GL_FALSE, &ortho[0][0]); { /* convert from command queue into draw list and draw to screen */ const struct nk_draw_command *cmd; @@ -293,17 +282,11 @@ void NuklearOverlayRenderer::render(nk_context *context, const Sizei &outputPres } void NuklearOverlayRenderer::reloadShaders(bool useOGLES) { - if (_shader.isValid()) { - std::string name = "Nuklear"; - std::string message = "shader failed to reload!\nPlease consult the log and issue a bug report!"; - if (!_shader.reload(name, useOGLES)) { - logger->warning("{} {}", name, message); - } else { - _state->dev.uniform_tex = _shader.uniformLocation("Texture"); - _state->dev.uniform_proj = _shader.uniformLocation("ProjMtx"); - _state->dev.attrib_pos = _shader.attribLocation("Position"); - _state->dev.attrib_uv = _shader.attribLocation("TexCoord"); - _state->dev.attrib_col = _shader.attribLocation("Color"); - } + if (_shader.load(makeDataPath("shaders", "glnuklear.vert"), makeDataPath("shaders", "glnuklear.frag"), useOGLES)) { + uniform_tex = _shader.uniformLocation("Texture"); + uniform_proj = _shader.uniformLocation("ProjMtx"); + attrib_pos = _shader.attribLocation("Position"); + attrib_uv = _shader.attribLocation("TexCoord"); + attrib_col = _shader.attribLocation("Color"); } } diff --git a/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.h b/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.h index 6a1ef352e4d..bcfddfd2fae 100644 --- a/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.h +++ b/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.h @@ -31,5 +31,10 @@ class NuklearOverlayRenderer { std::unique_ptr _state; bool _useOGLES = false; OpenGLShader _shader; + int32_t attrib_pos = 0; + int32_t attrib_uv = 0; + int32_t attrib_col = 0; + int32_t uniform_tex = 0; + int32_t uniform_proj = 0; nk_tex_font *_defaultFont = nullptr; }; diff --git a/src/Engine/Graphics/Renderer/OpenGLRenderer.cpp b/src/Engine/Graphics/Renderer/OpenGLRenderer.cpp index da8d91d685b..7475c5c3cad 100644 --- a/src/Engine/Graphics/Renderer/OpenGLRenderer.cpp +++ b/src/Engine/Graphics/Renderer/OpenGLRenderer.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include // NOLINT: not a C system header. @@ -51,6 +52,7 @@ #include "NuklearOverlayRenderer.h" #include "OpenGLShader.h" +#include "Utility/DataPath.h" #ifndef LOWORD #define LOWORD(l) ((unsigned short)(((std::uintptr_t)(l)) & 0xFFFF)) @@ -4766,81 +4768,7 @@ void OpenGLRenderer::FillRectFast(unsigned int uX, unsigned int uY, unsigned int // gl shaders bool OpenGLRenderer::InitShaders() { - logger->info("initialising OpenGL shaders..."); - - std::string title = "CRITICAL ERROR: shader compilation failure"; - std::string name = "Terrain"; - std::string message = "shader failed to compile!\nPlease consult the log and consider issuing a bug report!"; - terrainshader.build(name, "glterrain", OpenGLES); - if (!terrainshader.isValid()) { - platform->showMessageBox(title, fmt::format("{} {}", name, message)); - return false; - } - - name = "Outdoor buildings"; - outbuildshader.build(name, "gloutbuild", OpenGLES); - if (!outbuildshader.isValid()) { - platform->showMessageBox(title, fmt::format("{} {}", name, message)); - return false; - } - - name = "Indoor BSP"; - bspshader.build(name, "glbspshader", OpenGLES); - if (!bspshader.isValid()) { - platform->showMessageBox(title, fmt::format("{} {}", name, message)); - return false; - } - - name = "Text"; - textshader.build(name, "gltextshader", OpenGLES); - if (!textshader.isValid()) { - platform->showMessageBox(title, fmt::format("{} {}", name, message)); - return false; - } - textVAO = 0; - - name = "Lines"; - lineshader.build(name, "gllinesshader", OpenGLES); - if (!lineshader.isValid()) { - platform->showMessageBox(title, fmt::format("{} {}", name, message)); - return false; - } - lineVAO = 0; - - name = "2D"; - twodshader.build(name, "gltwodshader", OpenGLES); - if (!twodshader.isValid()) { - platform->showMessageBox(title, fmt::format("{} {}", name, message)); - return false; - } - twodVAO = 0; - - name = "Billboards"; - billbshader.build(name, "glbillbshader", OpenGLES); - if (!billbshader.isValid()) { - platform->showMessageBox(title, fmt::format("{} {}", name, message)); - return false; - } - billbVAO = 0; - palbuf = 0; - - name = "Decals"; - decalshader.build(name, "gldecalshader", OpenGLES); - if (!decalshader.isValid()) { - platform->showMessageBox(title, fmt::format("{} {}", name, message)); - return false; - } - decalVAO = 0; - - name = "Forced perspective"; - forcepershader.build(name, "glforcepershader", OpenGLES); - if (!forcepershader.isValid()) { - platform->showMessageBox(title, fmt::format("{} {}", name, message)); - return false; - } - forceperVAO = 0; - - logger->info("shaders have been compiled successfully!"); + ReloadShaders(); return true; } @@ -4926,10 +4854,7 @@ bool OpenGLRenderer::Reinitialize(bool firstInit) { if (firstInit) { // initiate shaders - if (!InitShaders()) { - logger->warning("shader initialisation has failed!"); - return false; - } + InitShaders(); } // else { if (config->window.ReloadTex.value()) { @@ -4948,79 +4873,106 @@ bool OpenGLRenderer::Reinitialize(bool firstInit) { } void OpenGLRenderer::ReloadShaders() { - logger->info("reloading Shaders..."); + logger->info("Reloading shaders..."); glUseProgram(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); - std::string name = "Terrain"; - std::string message = "shader failed to reload!\nPlease consult the log and issue a bug report!"; - if (!terrainshader.reload(name, OpenGLES)) - logger->warning("{} {}", name, message); - name = "Outdoor buildings"; - if (!outbuildshader.reload(name, OpenGLES)) - logger->warning("{} {}", name, message); ReleaseTerrain(); - - name = "Indoor BSP"; - if (!bspshader.reload(name, OpenGLES)) - logger->warning("{} {}", name, message); ReleaseBSP(); - name = "Text"; - if (!textshader.reload(name, OpenGLES)) - logger->warning("{} {}", name, message); - glDeleteVertexArrays(1, &textVAO); - glDeleteBuffers(1, &textVBO); - textVAO = textVBO = 0; + if (textVAO) { + glDeleteVertexArrays(1, &textVAO); + textVAO = 0; + } + if (textVBO) { + glDeleteBuffers(1, &textVBO); + textVBO = 0; + } textvertscnt = 0; - name = "Lines"; - if (!lineshader.reload(name, OpenGLES)) - logger->warning("{} {}", name, message); - glDeleteVertexArrays(1, &lineVAO); - glDeleteBuffers(1, &lineVBO); - lineVAO = lineVBO = 0; + if (lineVAO) { + glDeleteVertexArrays(1, &lineVAO); + lineVAO = 0; + } + if (lineVBO) { + glDeleteBuffers(1, &lineVBO); + lineVBO = 0; + } linevertscnt = 0; - name = "2D"; - if (!twodshader.reload(name, OpenGLES)) - logger->warning("{} {}", name, message); - glDeleteVertexArrays(1, &twodVAO); - glDeleteBuffers(1, &twodVBO); - twodVAO = twodVBO = 0; + if (twodVAO) { + glDeleteVertexArrays(1, &twodVAO); + twodVAO = 0; + } + if (twodVBO) { + glDeleteBuffers(1, &twodVBO); + twodVBO = 0; + } twodvertscnt = 0; - name = "Billboards"; - if (!billbshader.reload(name, OpenGLES)) - logger->warning("{} {}", name, message); - glDeleteVertexArrays(1, &billbVAO); - glDeleteBuffers(1, &billbVBO); - billbVAO = billbVBO = 0; - glDeleteTextures(1, &paltex); - glDeleteBuffers(1, &palbuf); - paltex = palbuf = 0; + if (billbVAO) { + glDeleteVertexArrays(1, &billbVAO); + billbVAO = 0; + } + if (billbVBO) { + glDeleteBuffers(1, &billbVBO); + billbVBO = 0; + } + if (paltex) { + glDeleteTextures(1, &paltex); + paltex = 0; + } + if (palbuf) { + glDeleteBuffers(1, &palbuf); + palbuf = 0; + } billbstorecnt = 0; - name = "Decals"; - if (!decalshader.reload(name, OpenGLES)) - logger->warning("{} {}", name, message); - glDeleteVertexArrays(1, &decalVAO); - glDeleteBuffers(1, &decalVBO); - decalVAO = decalVBO = 0; + if (decalVAO) { + glDeleteVertexArrays(1, &decalVAO); + decalVAO = 0; + } + if (decalVBO) { + glDeleteBuffers(1, &decalVBO); + decalVBO = 0; + } numdecalverts = 0; - name = "Forced perspective"; - if (!forcepershader.reload(name, OpenGLES)) - logger->warning("{} {}", name, message); - glDeleteVertexArrays(1, &forceperVAO); - glDeleteBuffers(1, &forceperVBO); - forceperVAO = forceperVBO = 0; + if (forceperVAO) { + glDeleteVertexArrays(1, &forceperVAO); + forceperVAO = 0; + } + if (forceperVBO) { + glDeleteBuffers(1, &forceperVBO); + forceperVBO = 0; + } forceperstorecnt = 0; if (_overlayRenderer) { _overlayRenderer->reloadShaders(OpenGLES); } + + const std::initializer_list> shaders = { + {&terrainshader, "glterrain", "Terrain"}, + {&outbuildshader, "gloutbuild", "Outdoor buildings"}, + {&bspshader, "glbspshader", "Indoor BSP"}, + {&textshader, "gltextshader", "Text"}, + {&lineshader, "gllinesshader", "Lines"}, + {&twodshader, "gltwodshader", "2D"}, + {&billbshader, "glbillbshader", "Billboards"}, + {&decalshader, "gldecalshader", "Decals"}, + {&forcepershader, "glforcepershader", "Forced perspective"} + }; + + for (const auto &[shader, fileName, readableName] : shaders) { + if (!shader->load(makeDataPath("shaders", fmt::format("{}.vert", fileName)), makeDataPath("shaders", fmt::format("{}.frag", fileName)), OpenGLES)) { + platform->showMessageBox("CRITICAL ERROR: shader compilation failure", + fmt::format("{} shader failed to compile!\nPlease consult the log and consider issuing a bug report!", readableName)); + } + } + + logger->info("Shaders reloaded."); } void OpenGLRenderer::beginOverlays() { diff --git a/src/Engine/Graphics/Renderer/OpenGLShader.cpp b/src/Engine/Graphics/Renderer/OpenGLShader.cpp index 94cad0539e4..f50a2692628 100644 --- a/src/Engine/Graphics/Renderer/OpenGLShader.cpp +++ b/src/Engine/Graphics/Renderer/OpenGLShader.cpp @@ -6,87 +6,76 @@ #include // NOLINT: this is not a C system include. #include "Library/Logger/Logger.h" -#include "Library/Serialization/EnumSerialization.h" #include "Utility/Streams/FileInputStream.h" -#include "Utility/DataPath.h" -#include "Utility/Exception.h" - -namespace detail_extension { -MM_DEFINE_ENUM_SERIALIZATION_FUNCTIONS(GLenum, CASE_SENSITIVE, { - {GL_VERTEX_SHADER, "vert"}, - {GL_FRAGMENT_SHADER, "frag"}, - {GL_GEOMETRY_SHADER, "geom"}, - {GL_COMPUTE_SHADER, "comp"}, - {GL_TESS_CONTROL_SHADER, "tesc"}, - {GL_TESS_EVALUATION_SHADER, "tese"} -}) -} // namespace detail_extension - -namespace detail_name { -MM_DEFINE_ENUM_SERIALIZATION_FUNCTIONS(GLenum, CASE_SENSITIVE, { - {GL_VERTEX_SHADER, "vertex"}, - {GL_FRAGMENT_SHADER, "fragment"}, - {GL_GEOMETRY_SHADER, "geometry"}, - {GL_COMPUTE_SHADER, "compute"}, - {GL_TESS_CONTROL_SHADER, "tessellation control"}, - {GL_TESS_EVALUATION_SHADER, "tessellation evaluation"} -}) -} // namespace detail_name - -int OpenGLShader::build(std::string_view name, std::string_view filename, bool OpenGLES, bool reload) { - // 1. retrieve the vertex/fragment source code from filePath - GLuint vertex = load(name, filename, GL_VERTEX_SHADER, OpenGLES); - if (vertex == 0) - return 0; - GLuint fragment = load(name, filename, GL_FRAGMENT_SHADER, OpenGLES); - if (fragment == 0) - return 0; +static std::string compileErrors(int shader) { + GLint success = 1; + GLchar infoLog[2048]; + if (glIsShader(shader)) { + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) + glGetShaderInfoLog(shader, 2048, NULL, infoLog); + } else { + glGetProgramiv(shader, GL_LINK_STATUS, &success); + if (!success) + glGetProgramInfoLog(shader, 2048, NULL, infoLog); + } + + if (!success) { + if (infoLog[0]) { + return infoLog; + } else { + return "Unknown error"; + } + } + + return {}; +} + +OpenGLShader::~OpenGLShader() { + release(); +} + +bool OpenGLShader::load(std::string_view vertPath, std::string_view fragPath, bool openGLES) { + release(); - GLuint geometry = load(name, filename, GL_GEOMETRY_SHADER, OpenGLES, true); + GLuint vertex = loadShader(vertPath, GL_VERTEX_SHADER, openGLES); + if (vertex == 0) + return false; - // shader Program - int tempID = glCreateProgram(); - glAttachShader(tempID, vertex); - glAttachShader(tempID, fragment); - if (geometry != 0) - glAttachShader(tempID, geometry); - glLinkProgram(tempID); - bool NOerror = checkCompileErrors(tempID, name, "program"); - if (!NOerror) - tempID = 0; + GLuint fragment = loadShader(fragPath, GL_FRAGMENT_SHADER, openGLES); + if (fragment == 0) { + glDeleteShader(vertex); + return false; + } + + int result = glCreateProgram(); + glAttachShader(result, vertex); + glAttachShader(result, fragment); + glLinkProgram(result); // delete the shaders as they're linked into our program now and no longer necessery glDeleteShader(vertex); glDeleteShader(fragment); - if (geometry != 0) - glDeleteShader(geometry); - - if (tempID) { - // set var members on first load - if (reload == false) { - _id = tempID; - _filename = filename; - } - return tempID; + + std::string errors = compileErrors(result); + if (!errors.empty()) { + logger->error("Could not link shader program ['{}', '{}']:\n{}", vertPath, fragPath, errors); + glDeleteProgram(result); + return false; } - logger->error("shader compilation failure: {}", filename); - return 0; + _id = result; + return true; } -bool OpenGLShader::reload(std::string_view name, bool OpenGLES) { - int tryreload = build(name, _filename, OpenGLES, true); - - if (tryreload) { - reset(); - _id = tryreload; - return true; - } +void OpenGLShader::release() { + if (_id == 0) + return; - logger->info("shader reload failed, reverting..."); - return false; + glDeleteProgram(_id); + _id = 0; } int OpenGLShader::uniformLocation(const char *name) { @@ -107,73 +96,34 @@ void OpenGLShader::use() { glUseProgram(_id); } -void OpenGLShader::reset() { - if (_id == 0) - return; - - glDeleteProgram(_id); - _id = 0; -} - -std::string OpenGLShader::shaderTypeToExtension(int type) { - std::string result; - detail_extension::serialize(type, &result); - return result; -} +unsigned OpenGLShader::loadShader(std::string_view path, int type, bool openGLES) { + std::string source; + try { + source = FileInputStream(path).readAll(); + } catch (const std::exception &e) { + logger->error("Could not read shader source: {}", e.what()); // e.what() will contain file path. + return 0; + } -std::string OpenGLShader::shaderTypeToName(int type) { - std::string result; - detail_name::serialize(type, &result); - return result; -} + if (!openGLES) + source = "#version 410 core\n" + source; + else + source = "#version 320 es\n" + source; -bool OpenGLShader::checkCompileErrors(int shader, std::string_view name, std::string_view type) { - GLint success; - GLchar infoLog[1024]; - if (type != "program") { - glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - if (!success) { - glGetShaderInfoLog(shader, 1024, NULL, infoLog); - logger->error("{} {} shader compilation error:\n{}", name, type, infoLog); - return false; - } - } else { - glGetProgramiv(shader, GL_LINK_STATUS, &success); - if (!success) { - glGetProgramInfoLog(shader, 1024, NULL, infoLog); - logger->error("{} {} linking error:\n{}", name, type, infoLog); - return false; - } - } - return true; -} + // compile shader + const char *sources[1] = {source.data()}; + const GLint lengths[1] = {static_cast(source.size())}; -int OpenGLShader::load(std::string_view name, std::string_view filename, int type, bool OpenGLES, bool nonFatal) { - std::string directory = "shaders"; - std::string typeName = shaderTypeToName(type); - std::string path = makeDataPath(directory, fmt::format("{}.{}", filename, shaderTypeToExtension(type))); + GLuint result = glCreateShader(type); + glShaderSource(result, 1, sources, lengths); + glCompileShader(result); - try { - std::string shaderString; - if (!OpenGLES) - shaderString = "#version 410 core\n"; - else - shaderString = "#version 320 es\n"; - - FileInputStream stream(path); - shaderString += stream.readAll(); - - // compile shader - const char *shaderChar = shaderString.c_str(); - GLuint shaderHandler = glCreateShader(type); - glShaderSource(shaderHandler, 1, &shaderChar, NULL); - glCompileShader(shaderHandler); - checkCompileErrors(shaderHandler, name, shaderTypeToName(type)); - - return shaderHandler; - } catch (const Exception &e) { - if (!nonFatal) - logger->error("Error occured during reading {} {} shader file at path {}: {}", name, typeName, path, e.what()); + std::string errors = compileErrors(result); + if (!errors.empty()) { + logger->error("Could not compile shader '{}':\n{}", path, errors); + glDeleteShader(result); return 0; } + + return result; } diff --git a/src/Engine/Graphics/Renderer/OpenGLShader.h b/src/Engine/Graphics/Renderer/OpenGLShader.h index 7816aea945f..c3f135a6de7 100644 --- a/src/Engine/Graphics/Renderer/OpenGLShader.h +++ b/src/Engine/Graphics/Renderer/OpenGLShader.h @@ -1,43 +1,33 @@ -// Class for loading in and building gl shaders for use -// shamelessly stolen from: -// https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/shader.h - #pragma once #include +/** + * Class for loading in and building gl shaders. + * + * Code derived from: + * https://learnopengl.com/code_viewer_gh.php?code=includes/learnopengl/shader.h + */ class OpenGLShader { public: OpenGLShader() = default; + ~OpenGLShader(); [[nodiscard]] bool isValid() const { return _id != 0; } - // TODO(pskelton): consider map for uniform locations - - int build(std::string_view name, std::string_view filename, bool OpenGLES = false, bool reload = false); - - bool reload(std::string_view name, bool OpenGLES); + [[nodiscard]] bool load(std::string_view vertPath, std::string_view fragPath, bool openGLES); + void release(); - int uniformLocation(const char *name); - int attribLocation(const char *name); + [[nodiscard]] int uniformLocation(const char *name); + [[nodiscard]] int attribLocation(const char *name); - // activate the shader void use(); - void reset(); - private: - static std::string shaderTypeToExtension(int type); - static std::string shaderTypeToName(int type); - - // utility function for checking shader compilation/linking errors. - bool checkCompileErrors(int shader, std::string_view name, std::string_view type); - - int load(std::string_view name, std::string_view filename, int type, bool OpenGLES, bool nonFatal = false); + [[nodiscard]] unsigned loadShader(std::string_view path, int type, bool openGLES); private: unsigned _id = 0; - std::string _filename; }; From 9170dbd7af7398f90255a4e78e7f98a10a36c655 Mon Sep 17 00:00:00 2001 From: captainurist <73941350+captainurist@users.noreply.github.com> Date: Sun, 21 Jul 2024 14:00:30 +0100 Subject: [PATCH 2/3] Use Blobs for shader sources --- src/Engine/Graphics/Renderer/OpenGLShader.cpp | 42 ++++++++++--------- src/Engine/Graphics/Renderer/OpenGLShader.h | 5 ++- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/Engine/Graphics/Renderer/OpenGLShader.cpp b/src/Engine/Graphics/Renderer/OpenGLShader.cpp index f50a2692628..67d9446c076 100644 --- a/src/Engine/Graphics/Renderer/OpenGLShader.cpp +++ b/src/Engine/Graphics/Renderer/OpenGLShader.cpp @@ -38,13 +38,24 @@ OpenGLShader::~OpenGLShader() { } bool OpenGLShader::load(std::string_view vertPath, std::string_view fragPath, bool openGLES) { - release(); + Blob vertSource, fragSource; + try { + vertSource = Blob::fromFile(vertPath); + fragSource = Blob::fromFile(fragPath); + } catch (const std::exception &e) { + logger->error("Could not read shader source: {}", e.what()); // e.what() will contain file path. + return false; + } + + return load(vertSource, fragSource, openGLES); +} - GLuint vertex = loadShader(vertPath, GL_VERTEX_SHADER, openGLES); +bool OpenGLShader::load(const Blob &vertSource, const Blob &fragSource, bool openGLES) { + GLuint vertex = loadShader(vertSource, GL_VERTEX_SHADER, openGLES); if (vertex == 0) return false; - GLuint fragment = loadShader(fragPath, GL_FRAGMENT_SHADER, openGLES); + GLuint fragment = loadShader(fragSource, GL_FRAGMENT_SHADER, openGLES); if (fragment == 0) { glDeleteShader(vertex); return false; @@ -61,7 +72,7 @@ bool OpenGLShader::load(std::string_view vertPath, std::string_view fragPath, bo std::string errors = compileErrors(result); if (!errors.empty()) { - logger->error("Could not link shader program ['{}', '{}']:\n{}", vertPath, fragPath, errors); + logger->error("Could not link shader program ['{}', '{}']:\n{}", vertSource.displayPath(), fragSource.displayPath(), errors); glDeleteProgram(result); return false; } @@ -96,31 +107,24 @@ void OpenGLShader::use() { glUseProgram(_id); } -unsigned OpenGLShader::loadShader(std::string_view path, int type, bool openGLES) { - std::string source; - try { - source = FileInputStream(path).readAll(); - } catch (const std::exception &e) { - logger->error("Could not read shader source: {}", e.what()); // e.what() will contain file path. - return 0; - } - +unsigned OpenGLShader::loadShader(const Blob &source, int type, bool openGLES) { + std::string_view version; if (!openGLES) - source = "#version 410 core\n" + source; + version = "#version 410 core\n"; else - source = "#version 320 es\n" + source; + version = "#version 320 es\n"; // compile shader - const char *sources[1] = {source.data()}; - const GLint lengths[1] = {static_cast(source.size())}; + const char *sources[2] = {version.data(), static_cast(source.data())}; + const GLint lengths[2] = {static_cast(version.size()), static_cast(source.size())}; GLuint result = glCreateShader(type); - glShaderSource(result, 1, sources, lengths); + glShaderSource(result, 2, sources, lengths); glCompileShader(result); std::string errors = compileErrors(result); if (!errors.empty()) { - logger->error("Could not compile shader '{}':\n{}", path, errors); + logger->error("Could not compile shader '{}':\n{}", source.displayPath(), errors); glDeleteShader(result); return 0; } diff --git a/src/Engine/Graphics/Renderer/OpenGLShader.h b/src/Engine/Graphics/Renderer/OpenGLShader.h index c3f135a6de7..780db8cc4bd 100644 --- a/src/Engine/Graphics/Renderer/OpenGLShader.h +++ b/src/Engine/Graphics/Renderer/OpenGLShader.h @@ -2,6 +2,8 @@ #include +#include "Utility/Memory/Blob.h" + /** * Class for loading in and building gl shaders. * @@ -18,6 +20,7 @@ class OpenGLShader { } [[nodiscard]] bool load(std::string_view vertPath, std::string_view fragPath, bool openGLES); + [[nodiscard]] bool load(const Blob &vertSource, const Blob &fragSource, bool openGLES); void release(); [[nodiscard]] int uniformLocation(const char *name); @@ -26,7 +29,7 @@ class OpenGLShader { void use(); private: - [[nodiscard]] unsigned loadShader(std::string_view path, int type, bool openGLES); + [[nodiscard]] unsigned loadShader(const Blob &source, int type, bool openGLES); private: unsigned _id = 0; From f921b6b4ca858acfc95779b6417357f450e21742 Mon Sep 17 00:00:00 2001 From: captainurist <73941350+captainurist@users.noreply.github.com> Date: Sun, 21 Jul 2024 17:39:18 +0100 Subject: [PATCH 3/3] Review comments --- .../Renderer/NuklearOverlayRenderer.cpp | 26 +++++++++---------- .../Renderer/NuklearOverlayRenderer.h | 10 +++---- src/Engine/Graphics/Renderer/NullRenderer.cpp | 2 +- src/Engine/Graphics/Renderer/NullRenderer.h | 2 +- .../Graphics/Renderer/OpenGLRenderer.cpp | 13 ++++------ src/Engine/Graphics/Renderer/OpenGLRenderer.h | 3 +-- src/Engine/Graphics/Renderer/Renderer.h | 2 +- 7 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.cpp b/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.cpp index d476ebeb7ee..d406d26ccbb 100644 --- a/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.cpp +++ b/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.cpp @@ -87,13 +87,13 @@ bool NuklearOverlayRenderer::_createDevice() { glBindBuffer(GL_ARRAY_BUFFER, _state->dev.vbo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _state->dev.ebo); - glEnableVertexAttribArray((GLuint)attrib_pos); - glEnableVertexAttribArray((GLuint)attrib_uv); - glEnableVertexAttribArray((GLuint)attrib_col); + glEnableVertexAttribArray((GLuint)_attribPos); + glEnableVertexAttribArray((GLuint)_attribUv); + glEnableVertexAttribArray((GLuint)_attribCol); - glVertexAttribPointer((GLuint)attrib_pos, 2, GL_FLOAT, GL_FALSE, vs, (void *)vp); - glVertexAttribPointer((GLuint)attrib_uv, 2, GL_FLOAT, GL_FALSE, vs, (void *)vt); - glVertexAttribPointer((GLuint)attrib_col, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void *)vc); + glVertexAttribPointer((GLuint)_attribPos, 2, GL_FLOAT, GL_FALSE, vs, (void *)vp); + glVertexAttribPointer((GLuint)_attribUv, 2, GL_FLOAT, GL_FALSE, vs, (void *)vt); + glVertexAttribPointer((GLuint)_attribCol, 4, GL_UNSIGNED_BYTE, GL_TRUE, vs, (void *)vc); } glBindTexture(GL_TEXTURE_2D, 0); @@ -204,8 +204,8 @@ void NuklearOverlayRenderer::render(nk_context *context, const Sizei &outputPres /* setup program */ _shader.use(); - glUniform1i(uniform_tex, 0); - glUniformMatrix4fv(uniform_proj, 1, GL_FALSE, &ortho[0][0]); + glUniform1i(_uniformTex, 0); + glUniformMatrix4fv(_uniformProj, 1, GL_FALSE, &ortho[0][0]); { /* convert from command queue into draw list and draw to screen */ const struct nk_draw_command *cmd; @@ -283,10 +283,10 @@ void NuklearOverlayRenderer::render(nk_context *context, const Sizei &outputPres void NuklearOverlayRenderer::reloadShaders(bool useOGLES) { if (_shader.load(makeDataPath("shaders", "glnuklear.vert"), makeDataPath("shaders", "glnuklear.frag"), useOGLES)) { - uniform_tex = _shader.uniformLocation("Texture"); - uniform_proj = _shader.uniformLocation("ProjMtx"); - attrib_pos = _shader.attribLocation("Position"); - attrib_uv = _shader.attribLocation("TexCoord"); - attrib_col = _shader.attribLocation("Color"); + _uniformTex = _shader.uniformLocation("Texture"); + _uniformProj = _shader.uniformLocation("ProjMtx"); + _attribPos = _shader.attribLocation("Position"); + _attribUv = _shader.attribLocation("TexCoord"); + _attribCol = _shader.attribLocation("Color"); } } diff --git a/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.h b/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.h index bcfddfd2fae..c0e3b442974 100644 --- a/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.h +++ b/src/Engine/Graphics/Renderer/NuklearOverlayRenderer.h @@ -31,10 +31,10 @@ class NuklearOverlayRenderer { std::unique_ptr _state; bool _useOGLES = false; OpenGLShader _shader; - int32_t attrib_pos = 0; - int32_t attrib_uv = 0; - int32_t attrib_col = 0; - int32_t uniform_tex = 0; - int32_t uniform_proj = 0; + int32_t _attribPos = 0; + int32_t _attribUv = 0; + int32_t _attribCol = 0; + int32_t _uniformTex = 0; + int32_t _uniformProj = 0; nk_tex_font *_defaultFont = nullptr; }; diff --git a/src/Engine/Graphics/Renderer/NullRenderer.cpp b/src/Engine/Graphics/Renderer/NullRenderer.cpp index 1286514dd0a..3d2c4222fa4 100644 --- a/src/Engine/Graphics/Renderer/NullRenderer.cpp +++ b/src/Engine/Graphics/Renderer/NullRenderer.cpp @@ -136,7 +136,7 @@ void NullRenderer::ReleaseBSP() {} void NullRenderer::DrawTwodVerts() {} -void NullRenderer::ReloadShaders() {} +bool NullRenderer::ReloadShaders() { return true; } void NullRenderer::DoRenderBillboards_D3D() {} diff --git a/src/Engine/Graphics/Renderer/NullRenderer.h b/src/Engine/Graphics/Renderer/NullRenderer.h index 3ceac5ced62..04c50c47985 100644 --- a/src/Engine/Graphics/Renderer/NullRenderer.h +++ b/src/Engine/Graphics/Renderer/NullRenderer.h @@ -104,7 +104,7 @@ class NullRenderer : public BaseRenderer { virtual void DrawTwodVerts() override; - virtual void ReloadShaders() override; + virtual bool ReloadShaders() override; virtual void DoRenderBillboards_D3D() override; diff --git a/src/Engine/Graphics/Renderer/OpenGLRenderer.cpp b/src/Engine/Graphics/Renderer/OpenGLRenderer.cpp index 7475c5c3cad..1dd68615f1f 100644 --- a/src/Engine/Graphics/Renderer/OpenGLRenderer.cpp +++ b/src/Engine/Graphics/Renderer/OpenGLRenderer.cpp @@ -4766,12 +4766,6 @@ void OpenGLRenderer::FillRectFast(unsigned int uX, unsigned int uY, unsigned int return; } -// gl shaders -bool OpenGLRenderer::InitShaders() { - ReloadShaders(); - return true; -} - bool OpenGLRenderer::Reinitialize(bool firstInit) { BaseRenderer::Reinitialize(firstInit); @@ -4854,7 +4848,8 @@ bool OpenGLRenderer::Reinitialize(bool firstInit) { if (firstInit) { // initiate shaders - InitShaders(); + if (!ReloadShaders()) + return false; } // else { if (config->window.ReloadTex.value()) { @@ -4872,7 +4867,7 @@ bool OpenGLRenderer::Reinitialize(bool firstInit) { return BaseRenderer::Reinitialize(firstInit); } -void OpenGLRenderer::ReloadShaders() { +bool OpenGLRenderer::ReloadShaders() { logger->info("Reloading shaders..."); glUseProgram(0); glBindBuffer(GL_ARRAY_BUFFER, 0); @@ -4969,10 +4964,12 @@ void OpenGLRenderer::ReloadShaders() { if (!shader->load(makeDataPath("shaders", fmt::format("{}.vert", fileName)), makeDataPath("shaders", fmt::format("{}.frag", fileName)), OpenGLES)) { platform->showMessageBox("CRITICAL ERROR: shader compilation failure", fmt::format("{} shader failed to compile!\nPlease consult the log and consider issuing a bug report!", readableName)); + return false; } } logger->info("Shaders reloaded."); + return true; } void OpenGLRenderer::beginOverlays() { diff --git a/src/Engine/Graphics/Renderer/OpenGLRenderer.h b/src/Engine/Graphics/Renderer/OpenGLRenderer.h index 69c67fa196d..5367184ae83 100644 --- a/src/Engine/Graphics/Renderer/OpenGLRenderer.h +++ b/src/Engine/Graphics/Renderer/OpenGLRenderer.h @@ -127,7 +127,7 @@ class OpenGLRenderer : public BaseRenderer { void DrawBillboards(); virtual bool Reinitialize(bool firstInit) override; - virtual void ReloadShaders() override; + virtual bool ReloadShaders() override; virtual void flushAndScale() override; virtual void swapBuffers() override; @@ -171,7 +171,6 @@ class OpenGLRenderer : public BaseRenderer { int GPU_MAX_UNIFORM_COMP{}; int GPU_MAX_TOTAL_TEXTURES{}; - bool InitShaders(); OpenGLShader terrainshader; OpenGLShader outbuildshader; OpenGLShader bspshader; diff --git a/src/Engine/Graphics/Renderer/Renderer.h b/src/Engine/Graphics/Renderer/Renderer.h index a148810cc88..3da3fad2c08 100644 --- a/src/Engine/Graphics/Renderer/Renderer.h +++ b/src/Engine/Graphics/Renderer/Renderer.h @@ -180,7 +180,7 @@ class Renderer { virtual Sizei GetRenderDimensions() = 0; virtual Sizei GetPresentDimensions() = 0; virtual bool Reinitialize(bool firstInit = false) = 0; - virtual void ReloadShaders() = 0; + virtual bool ReloadShaders() = 0; virtual void DoRenderBillboards_D3D() = 0; virtual void flushAndScale() = 0;