From fa1a14d963f759d940100781fc7016f89063d495 Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Wed, 11 Dec 2024 21:44:28 +0100 Subject: [PATCH] Move global shader related variables to new ShaderCache singleton This fixes crash at exit now that SDL is a Singleton, too. --- src/App.cpp | 8 ++- src/ShaderCache.cpp | 119 +++++++++++++++++++++++++++++++++++++++ src/ShaderCache.hpp | 32 +++++++++++ src/TextureCache.hpp | 3 + src/freetype.cpp | 13 +++-- src/freetype.hpp | 4 +- src/jngl/FrameBuffer.cpp | 24 +++++--- src/jngl/ImageData.cpp | 5 +- src/jngl/Video.cpp | 2 +- src/jngl/shapes.cpp | 10 ++-- src/jngl/sprite.cpp | 62 +++++++++++--------- src/main.cpp | 108 +---------------------------------- src/main.hpp | 13 ----- src/spriteimpl.cpp | 29 +++++----- src/texture.cpp | 41 ++++++-------- src/texture.hpp | 10 ---- src/window.cpp | 11 ++-- 17 files changed, 266 insertions(+), 228 deletions(-) create mode 100644 src/ShaderCache.cpp create mode 100644 src/ShaderCache.hpp diff --git a/src/App.cpp b/src/App.cpp index 9e8ff5d8..bd665b4f 100644 --- a/src/App.cpp +++ b/src/App.cpp @@ -66,9 +66,13 @@ void App::atExit(std::function f) { void App::callAtExitFunctions() { auto tmp = std::move(callAtExit); assert(callAtExit.empty()); - for (const auto& f : tmp) { - f(); + + // destroy Singletons in the reverse order that they were created: + const auto end = tmp.rend(); + for (auto it = tmp.rbegin(); it != end; ++it) { + (*it)(); } + if (!callAtExit.empty()) { internal::warn("The destructor of a Singleton caused the creation of another Singleton. " "Use handleIfAlive inside of destructors of Singletons."); diff --git a/src/ShaderCache.cpp b/src/ShaderCache.cpp new file mode 100644 index 00000000..a91bec44 --- /dev/null +++ b/src/ShaderCache.cpp @@ -0,0 +1,119 @@ +// Copyright 2024 Jan Niklas Hasse +// For conditions of distribution and use, see copyright notice in LICENSE.txt +#include "ShaderCache.hpp" + +#include "jngl/Shader.hpp" +#include "opengl.hpp" +#include "spriteimpl.hpp" + +#include + +namespace jngl { + +ShaderCache::ShaderCache() { + Shader vertexShader(R"(#version 300 es + in mediump vec2 position; + uniform highp mat3 modelview; + uniform mediump mat4 projection; + + void main() { + vec3 tmp = modelview * vec3(position, 1); + gl_Position = projection * vec4(tmp.x, tmp.y, 0, 1); + })", + Shader::Type::VERTEX, R"(#version 100 + attribute mediump vec2 position; + uniform highp mat3 modelview; + uniform mediump mat4 projection; + + void main() { + vec3 tmp = modelview * vec3(position, 1); + gl_Position = projection * vec4(tmp.x, tmp.y, 0, 1); + })"); + Shader fragmentShader(R"(#version 300 es + uniform lowp vec4 color; + out lowp vec4 outColor; + + void main() { + outColor = color; + })", + Shader::Type::FRAGMENT, R"(#version 100 + uniform lowp vec4 color; + + void main() { + gl_FragColor = color; + })"); + simpleShaderProgram = std::make_unique(vertexShader, fragmentShader); + simpleModelviewUniform = simpleShaderProgram->getUniformLocation("modelview"); + simpleColorUniform = simpleShaderProgram->getUniformLocation("color"); + + { + textureVertexShader = std::make_unique(R"(#version 300 es + in mediump vec2 position; + in mediump vec2 inTexCoord; + uniform highp mat3 modelview; + uniform mediump mat4 projection; + out mediump vec2 texCoord; + + void main() { + vec3 tmp = modelview * vec3(position, 1); + gl_Position = projection * vec4(tmp.x, tmp.y, 0, 1); + texCoord = inTexCoord; + })", + Shader::Type::VERTEX, R"(#version 100 + attribute mediump vec2 position; + attribute mediump vec2 inTexCoord; + uniform highp mat3 modelview; + uniform mediump mat4 projection; + varying mediump vec2 texCoord; + + void main() { + vec3 tmp = modelview * vec3(position, 1); + gl_Position = projection * vec4(tmp.x, tmp.y, 0, 1); + texCoord = inTexCoord; + })"); + Shader fragmentShader(R"(#version 300 es + uniform sampler2D tex; + uniform lowp vec4 spriteColor; + + in mediump vec2 texCoord; + + out lowp vec4 outColor; + + void main() { + outColor = texture(tex, texCoord) * spriteColor; + })", + Shader::Type::FRAGMENT, R"(#version 100 + uniform sampler2D tex; + uniform lowp vec4 spriteColor; + + varying mediump vec2 texCoord; + + void main() { + gl_FragColor = texture2D(tex, texCoord) * spriteColor; + })"); + textureShaderProgram = + std::make_unique(*textureVertexShader, fragmentShader); + shaderSpriteColorUniform = textureShaderProgram->getUniformLocation("spriteColor"); + modelviewUniform = textureShaderProgram->getUniformLocation("modelview"); + } +} + +ShaderCache::~ShaderCache() = default; + +ShaderProgram::Context ShaderCache::useSimpleShaderProgram() { + return useSimpleShaderProgram(opengl::modelview, gShapeColor); +} + +ShaderProgram::Context ShaderCache::useSimpleShaderProgram(const Mat3& modelview, Rgba color) { + auto context = simpleShaderProgram->use(); + glUniform4f(simpleColorUniform, color.getRed(), color.getGreen(), color.getBlue(), + color.getAlpha()); + glUniformMatrix3fv(simpleModelviewUniform, 1, GL_FALSE, modelview.data); + + assert(simpleShaderProgram->getAttribLocation("position") == 0); + glEnableVertexAttribArray(0); + + return context; +} + +} // namespace jngl diff --git a/src/ShaderCache.hpp b/src/ShaderCache.hpp new file mode 100644 index 00000000..234c6b8a --- /dev/null +++ b/src/ShaderCache.hpp @@ -0,0 +1,32 @@ +// Copyright 2024 Jan Niklas Hasse +// For conditions of distribution and use, see copyright notice in LICENSE.txt +#pragma once + +#include "jngl/Rgba.hpp" +#include "jngl/ShaderProgram.hpp" +#include "jngl/Singleton.hpp" + +namespace jngl { + +class Mat3; + +class ShaderCache : public Singleton { +public: + ShaderCache(); + ~ShaderCache(); + + ShaderProgram::Context useSimpleShaderProgram(); + ShaderProgram::Context useSimpleShaderProgram(const Mat3& modelview, Rgba color); + + std::unique_ptr textureVertexShader; + std::unique_ptr textureShaderProgram; + int shaderSpriteColorUniform; + int modelviewUniform; + +private: + std::unique_ptr simpleShaderProgram; + int simpleModelviewUniform; + int simpleColorUniform; +}; + +} // namespace jngl diff --git a/src/TextureCache.hpp b/src/TextureCache.hpp index 3fa32c8e..2c77e1be 100644 --- a/src/TextureCache.hpp +++ b/src/TextureCache.hpp @@ -8,6 +8,7 @@ namespace jngl { +class Sprite; class Texture; class TextureCache : public Singleton { @@ -16,6 +17,8 @@ class TextureCache : public Singleton { void insert(std::string_view filename, std::shared_ptr); void remove(std::string_view filename); + std::unordered_map> sprites; + private: // https://www.cppstories.com/2021/heterogeneous-access-cpp20/ struct string_hash { diff --git a/src/freetype.cpp b/src/freetype.cpp index 6b11ce33..ce385678 100644 --- a/src/freetype.cpp +++ b/src/freetype.cpp @@ -4,6 +4,7 @@ #define _LIBCPP_DISABLE_DEPRECATION_WARNINGS #include "freetype.hpp" +#include "ShaderCache.hpp" #include "helper.hpp" #include "jngl/ScaleablePixels.hpp" #include "jngl/matrix.hpp" @@ -92,7 +93,7 @@ Character::Character(const char32_t ch, const unsigned int fontHeight, FT_Face f void Character::draw(Mat3& modelview) const { if (texture_) { - glUniformMatrix3fv(Texture::modelviewUniform, 1, GL_FALSE, + glUniformMatrix3fv(ShaderCache::handle().modelviewUniform, 1, GL_FALSE, Mat3(modelview).translate(left_, top_).data); texture_->draw(); } @@ -241,8 +242,8 @@ void FontImpl::setLineHeight(Pixels h) { } void FontImpl::print(Mat3 modelview, const std::string& text, Rgba color) { - auto context = Texture::textureShaderProgram->use(); - glUniform4f(Texture::shaderSpriteColorUniform, color.getRed(), color.getGreen(), + auto context = ShaderCache::handle().textureShaderProgram->use(); + glUniform4f(ShaderCache::handle().shaderSpriteColorUniform, color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); std::vector lines(splitlines(text)); @@ -262,9 +263,9 @@ void FontImpl::print(Mat3 modelview, const std::string& text, Rgba color) { } void FontImpl::print(const ScaleablePixels x, const ScaleablePixels y, const std::string& text) { - auto context = Texture::textureShaderProgram->use(); - glUniform4f(Texture::shaderSpriteColorUniform, gFontColor.getRed(), gFontColor.getGreen(), - gFontColor.getBlue(), gFontColor.getAlpha()); + auto context = ShaderCache::handle().textureShaderProgram->use(); + glUniform4f(ShaderCache::handle().shaderSpriteColorUniform, gFontColor.getRed(), + gFontColor.getGreen(), gFontColor.getBlue(), gFontColor.getAlpha()); const int xRounded = static_cast(std::lround(static_cast(Pixels{ x }))); const int yRounded = static_cast(std::lround(static_cast(Pixels{ y }))); std::vector lines(splitlines(text)); diff --git a/src/freetype.hpp b/src/freetype.hpp index 98d1ac18..ac0eb2fe 100644 --- a/src/freetype.hpp +++ b/src/freetype.hpp @@ -1,6 +1,5 @@ // Copyright 2007-2024 Jan Niklas Hasse // For conditions of distribution and use, see copyright notice in LICENSE.txt - #pragma once #include "jngl/Pixels.hpp" @@ -12,9 +11,12 @@ #include FT_STROKER_H #include +#include namespace jngl { +class Finally; + constexpr double LINE_HEIGHT_FACOTR = 1. / .63; extern Rgba gFontColor; diff --git a/src/jngl/FrameBuffer.cpp b/src/jngl/FrameBuffer.cpp index b5178689..d2739282 100644 --- a/src/jngl/FrameBuffer.cpp +++ b/src/jngl/FrameBuffer.cpp @@ -3,6 +3,7 @@ #include "FrameBuffer.hpp" +#include "../ShaderCache.hpp" #include "../main.hpp" #include "../spriteimpl.hpp" #include "../texture.hpp" @@ -105,21 +106,24 @@ void FrameBuffer::draw(const Vec2 position, const ShaderProgram* const shaderPro jngl::translate(position); opengl::scale(1, -1); jngl::translate(0, -impl->height / getScaleFactor()); - auto context = shaderProgram ? shaderProgram->use() : Texture::textureShaderProgram->use(); + auto context = + shaderProgram ? shaderProgram->use() : ShaderCache::handle().textureShaderProgram->use(); if (shaderProgram) { glUniformMatrix3fv(shaderProgram->getUniformLocation("modelview"), 1, GL_FALSE, opengl::modelview.data); } else { - glUniform4f(Texture::shaderSpriteColorUniform, gSpriteColor.getRed(), + glUniform4f(ShaderCache::handle().shaderSpriteColorUniform, gSpriteColor.getRed(), gSpriteColor.getGreen(), gSpriteColor.getBlue(), gSpriteColor.getAlpha()); - glUniformMatrix3fv(Texture::modelviewUniform, 1, GL_FALSE, opengl::modelview.data); + glUniformMatrix3fv(ShaderCache::handle().modelviewUniform, 1, GL_FALSE, + opengl::modelview.data); } impl->texture.draw(); popMatrix(); } void FrameBuffer::draw(Mat3 modelview, const ShaderProgram* const shaderProgram) const { - auto context = shaderProgram ? shaderProgram->use() : Texture::textureShaderProgram->use(); + auto context = + shaderProgram ? shaderProgram->use() : ShaderCache::handle().textureShaderProgram->use(); if (shaderProgram) { glUniformMatrix3fv(shaderProgram->getUniformLocation("modelview"), 1, GL_FALSE, modelview.scale(1, -1) @@ -127,9 +131,9 @@ void FrameBuffer::draw(Mat3 modelview, const ShaderProgram* const shaderProgram) -impl->height / getScaleFactor() / 2 }) .data); } else { - glUniform4f(Texture::shaderSpriteColorUniform, gSpriteColor.getRed(), + glUniform4f(ShaderCache::handle().shaderSpriteColorUniform, gSpriteColor.getRed(), gSpriteColor.getGreen(), gSpriteColor.getBlue(), gSpriteColor.getAlpha()); - glUniformMatrix3fv(Texture::modelviewUniform, 1, GL_FALSE, + glUniformMatrix3fv(ShaderCache::handle().modelviewUniform, 1, GL_FALSE, modelview.scale(1, -1) .translate({ -impl->width / getScaleFactor() / 2, -impl->height / getScaleFactor() / 2 }) @@ -142,14 +146,16 @@ void FrameBuffer::drawMesh(const std::vector& vertexes, const ShaderProgram* const shaderProgram) const { pushMatrix(); scale(getScaleFactor()); - auto context = shaderProgram ? shaderProgram->use() : Texture::textureShaderProgram->use(); + auto context = + shaderProgram ? shaderProgram->use() : ShaderCache::handle().textureShaderProgram->use(); if (shaderProgram) { glUniformMatrix3fv(shaderProgram->getUniformLocation("modelview"), 1, GL_FALSE, opengl::modelview.data); } else { - glUniform4f(Texture::shaderSpriteColorUniform, gSpriteColor.getRed(), + glUniform4f(ShaderCache::handle().shaderSpriteColorUniform, gSpriteColor.getRed(), gSpriteColor.getGreen(), gSpriteColor.getBlue(), gSpriteColor.getAlpha()); - glUniformMatrix3fv(Texture::modelviewUniform, 1, GL_FALSE, opengl::modelview.data); + glUniformMatrix3fv(ShaderCache::handle().modelviewUniform, 1, GL_FALSE, + opengl::modelview.data); } impl->texture.drawMesh(vertexes); popMatrix(); diff --git a/src/jngl/ImageData.cpp b/src/jngl/ImageData.cpp index 6b4ed2be..38359922 100644 --- a/src/jngl/ImageData.cpp +++ b/src/jngl/ImageData.cpp @@ -1,10 +1,11 @@ -// Copyright 2021-2023 Jan Niklas Hasse +// Copyright 2021-2024 Jan Niklas Hasse // For conditions of distribution and use, see copyright notice in LICENSE.txt #include "ImageData.hpp" #include "../helper.hpp" -#include "../jngl/debug.hpp" #include "../main.hpp" +#include "Finally.hpp" +#include "debug.hpp" #ifdef ANDROID #include "../android/fopen.hpp" diff --git a/src/jngl/Video.cpp b/src/jngl/Video.cpp index b1dfb3a9..f37999f3 100644 --- a/src/jngl/Video.cpp +++ b/src/jngl/Video.cpp @@ -1,6 +1,5 @@ // Copyright 2018-2024 Jan Niklas Hasse // For conditions of distribution and use, see copyright notice in LICENSE.txt - #include "Video.hpp" #include @@ -16,6 +15,7 @@ #include "../theoraplay/theoraplay.h" #include "Channel.hpp" #include "Shader.hpp" +#include "ShaderProgram.hpp" #include "screen.hpp" #include "time.hpp" diff --git a/src/jngl/shapes.cpp b/src/jngl/shapes.cpp index 1ae936bf..1d1c932d 100644 --- a/src/jngl/shapes.cpp +++ b/src/jngl/shapes.cpp @@ -1,9 +1,8 @@ // Copyright 2012-2024 Jan Niklas Hasse // For conditions of distribution and use, see copyright notice in LICENSE.txt - #include "shapes.hpp" -#include "../main.hpp" +#include "../ShaderCache.hpp" #include "../opengl.hpp" #include "../spriteimpl.hpp" #include "Alpha.hpp" @@ -65,8 +64,8 @@ void drawEllipse(const Vec2 position, const float width, const float height, void drawEllipse(Mat3 modelview, float width, float height, float startAngle) { glBindVertexArray(opengl::vaoStream); - auto tmp = - useSimpleShaderProgram(modelview.scale(static_cast(getScaleFactor())), gShapeColor); + auto tmp = ShaderCache::handle().useSimpleShaderProgram( + modelview.scale(static_cast(getScaleFactor())), gShapeColor); std::vector vertexes; vertexes.push_back(0.f); vertexes.push_back(0.f); @@ -101,7 +100,8 @@ void drawCircle(Mat3 modelview, const float radius, const Rgba color) { void drawCircle(Mat3 modelview, const Rgba color) { glBindVertexArray(opengl::vaoStream); - auto tmp = useSimpleShaderProgram(modelview.scale(static_cast(getScaleFactor())), color); + auto tmp = ShaderCache::handle().useSimpleShaderProgram( + modelview.scale(static_cast(getScaleFactor())), color); // clang-format off const static float vertexes[] = { 1.f, 0.f, 0.9951847f, 0.09801714f, 0.9807853f, 0.1950903f, 0.9569403f, 0.2902847f, diff --git a/src/jngl/sprite.cpp b/src/jngl/sprite.cpp index b2104d53..ecf11cdd 100644 --- a/src/jngl/sprite.cpp +++ b/src/jngl/sprite.cpp @@ -7,6 +7,7 @@ #include "sprite.hpp" +#include "../ShaderCache.hpp" #include "../TextureCache.hpp" #include "../helper.hpp" #include "../log.hpp" @@ -155,10 +156,10 @@ void Sprite::step() { void Sprite::draw() const { pushMatrix(); opengl::translate(static_cast(position.x), static_cast(position.y)); - auto context = Texture::textureShaderProgram->use(); - glUniform4f(Texture::shaderSpriteColorUniform, gSpriteColor.getRed(), gSpriteColor.getGreen(), - gSpriteColor.getBlue(), gSpriteColor.getAlpha()); - glUniformMatrix3fv(Texture::modelviewUniform, 1, GL_FALSE, opengl::modelview.data); + auto context = ShaderCache::handle().textureShaderProgram->use(); + glUniform4f(ShaderCache::handle().shaderSpriteColorUniform, gSpriteColor.getRed(), + gSpriteColor.getGreen(), gSpriteColor.getBlue(), gSpriteColor.getAlpha()); + glUniformMatrix3fv(ShaderCache::handle().modelviewUniform, 1, GL_FALSE, opengl::modelview.data); texture->draw(); popMatrix(); } @@ -170,24 +171,25 @@ void Sprite::draw(Mat3 modelview, const ShaderProgram* const shaderProgram) cons void Sprite::draw(Mat3 modelview, Rgba color) const { modelview *= boost::qvm::translation_mat(boost::qvm::vec({ -width / 2., -height / 2. })); - auto context = Texture::textureShaderProgram->use(); - glUniform4f(Texture::shaderSpriteColorUniform, color.getRed(), color.getGreen(), + auto context = ShaderCache::handle().textureShaderProgram->use(); + glUniform4f(ShaderCache::handle().shaderSpriteColorUniform, color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha()); - glUniformMatrix3fv(Texture::modelviewUniform, 1, GL_FALSE, modelview.data); + glUniformMatrix3fv(ShaderCache::handle().modelviewUniform, 1, GL_FALSE, modelview.data); texture->draw(); } void Sprite::draw(Mat3 modelview, Alpha alpha, const ShaderProgram* const shaderProgram) const { modelview *= boost::qvm::translation_mat(boost::qvm::vec({ -width / 2., -height / 2. })); - auto context = shaderProgram ? shaderProgram->use() : Texture::textureShaderProgram->use(); + auto context = + shaderProgram ? shaderProgram->use() : ShaderCache::handle().textureShaderProgram->use(); if (shaderProgram) { glUniformMatrix3fv(shaderProgram->getUniformLocation("modelview"), 1, GL_FALSE, modelview.data); } else { - glUniform4f(Texture::shaderSpriteColorUniform, gSpriteColor.getRed(), + glUniform4f(ShaderCache::handle().shaderSpriteColorUniform, gSpriteColor.getRed(), gSpriteColor.getGreen(), gSpriteColor.getBlue(), alpha.getAlpha()); - glUniformMatrix3fv(Texture::modelviewUniform, 1, GL_FALSE, modelview.data); + glUniformMatrix3fv(ShaderCache::handle().modelviewUniform, 1, GL_FALSE, modelview.data); } texture->draw(); } @@ -195,15 +197,17 @@ void Sprite::draw(Mat3 modelview, Alpha alpha, const ShaderProgram* const shader void Sprite::draw(const ShaderProgram* const shaderProgram) const { pushMatrix(); opengl::translate(static_cast(position.x), static_cast(position.y)); - auto context = shaderProgram ? shaderProgram->use() : Texture::textureShaderProgram->use(); + auto context = + shaderProgram ? shaderProgram->use() : ShaderCache::handle().textureShaderProgram->use(); if (shaderProgram) { glUniformMatrix3fv(shaderProgram->getUniformLocation("modelview"), 1, GL_FALSE, opengl::modelview.data); } else { - glUniform4f(Texture::shaderSpriteColorUniform, gSpriteColor.getRed(), + glUniform4f(ShaderCache::handle().shaderSpriteColorUniform, gSpriteColor.getRed(), gSpriteColor.getGreen(), gSpriteColor.getBlue(), gSpriteColor.getAlpha()); - glUniformMatrix3fv(Texture::modelviewUniform, 1, GL_FALSE, opengl::modelview.data); + glUniformMatrix3fv(ShaderCache::handle().modelviewUniform, 1, GL_FALSE, + opengl::modelview.data); } texture->draw(); popMatrix(); @@ -227,9 +231,10 @@ void Sprite::Batch::draw(Mat3 modelview) const { } auto Sprite::batch(const ShaderProgram* const shaderProgram) const -> Batch { - auto context = shaderProgram ? shaderProgram->use() : Texture::textureShaderProgram->use(); + auto context = + shaderProgram ? shaderProgram->use() : ShaderCache::handle().textureShaderProgram->use(); if (!shaderProgram) { - glUniform4f(Texture::shaderSpriteColorUniform, gSpriteColor.getRed(), + glUniform4f(ShaderCache::handle().shaderSpriteColorUniform, gSpriteColor.getRed(), gSpriteColor.getGreen(), gSpriteColor.getBlue(), gSpriteColor.getAlpha()); } texture->bind(); @@ -237,7 +242,7 @@ auto Sprite::batch(const ShaderProgram* const shaderProgram) const -> Batch { std::move(context), boost::qvm::translation_mat(boost::qvm::vec({ -width / 2., -height / 2. })), shaderProgram ? shaderProgram->getUniformLocation("modelview") - : Texture::modelviewUniform }) }; + : ShaderCache::handle().modelviewUniform }) }; } void Sprite::drawScaled(float xfactor, float yfactor, @@ -245,15 +250,17 @@ void Sprite::drawScaled(float xfactor, float yfactor, pushMatrix(); opengl::translate(static_cast(position.x), static_cast(position.y)); opengl::scale(xfactor, yfactor); - auto context = shaderProgram ? shaderProgram->use() : Texture::textureShaderProgram->use(); + auto context = + shaderProgram ? shaderProgram->use() : ShaderCache::handle().textureShaderProgram->use(); if (shaderProgram) { glUniformMatrix3fv(shaderProgram->getUniformLocation("modelview"), 1, GL_FALSE, opengl::modelview.data); } else { - glUniform4f(Texture::shaderSpriteColorUniform, gSpriteColor.getRed(), + glUniform4f(ShaderCache::handle().shaderSpriteColorUniform, gSpriteColor.getRed(), gSpriteColor.getGreen(), gSpriteColor.getBlue(), gSpriteColor.getAlpha()); - glUniformMatrix3fv(Texture::modelviewUniform, 1, GL_FALSE, opengl::modelview.data); + glUniformMatrix3fv(ShaderCache::handle().modelviewUniform, 1, GL_FALSE, + opengl::modelview.data); } texture->draw(); popMatrix(); @@ -282,14 +289,15 @@ void Sprite::drawMesh(Mat3 modelview, const std::vector& vertexes, return; } modelview.scale(getScaleFactor()); - auto context = shaderProgram ? shaderProgram->use() : Texture::textureShaderProgram->use(); + auto context = + shaderProgram ? shaderProgram->use() : ShaderCache::handle().textureShaderProgram->use(); if (shaderProgram) { glUniformMatrix3fv(shaderProgram->getUniformLocation("modelview"), 1, GL_FALSE, modelview.data); } else { - glUniform4f(Texture::shaderSpriteColorUniform, gSpriteColor.getRed(), + glUniform4f(ShaderCache::handle().shaderSpriteColorUniform, gSpriteColor.getRed(), gSpriteColor.getGreen(), gSpriteColor.getBlue(), gSpriteColor.getAlpha()); - glUniformMatrix3fv(Texture::modelviewUniform, 1, GL_FALSE, modelview.data); + glUniformMatrix3fv(ShaderCache::handle().modelviewUniform, 1, GL_FALSE, modelview.data); } texture->drawMesh(vertexes); } @@ -302,14 +310,16 @@ void Sprite::drawMesh(const std::vector& vertexes, pushMatrix(); opengl::translate(static_cast(position.x), static_cast(position.y)); scale(getScaleFactor()); - auto context = shaderProgram ? shaderProgram->use() : Texture::textureShaderProgram->use(); + auto context = + shaderProgram ? shaderProgram->use() : ShaderCache::handle().textureShaderProgram->use(); if (shaderProgram) { glUniformMatrix3fv(shaderProgram->getUniformLocation("modelview"), 1, GL_FALSE, opengl::modelview.data); } else { - glUniform4f(Texture::shaderSpriteColorUniform, gSpriteColor.getRed(), + glUniform4f(ShaderCache::handle().shaderSpriteColorUniform, gSpriteColor.getRed(), gSpriteColor.getGreen(), gSpriteColor.getBlue(), gSpriteColor.getAlpha()); - glUniformMatrix3fv(Texture::modelviewUniform, 1, GL_FALSE, opengl::modelview.data); + glUniformMatrix3fv(ShaderCache::handle().modelviewUniform, 1, GL_FALSE, + opengl::modelview.data); } texture->drawMesh(vertexes); popMatrix(); @@ -321,7 +331,7 @@ void Sprite::setBytes(const unsigned char* const bytes) { } const Shader& Sprite::vertexShader() { - return *Texture::textureVertexShader; + return *ShaderCache::handle().textureVertexShader; } #ifndef NOPNG diff --git a/src/main.cpp b/src/main.cpp index 83e240f3..9e6e97db 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,9 +4,9 @@ #include "main.hpp" #include "App.hpp" +#include "ShaderCache.hpp" #include "jngl/Alpha.hpp" #include "jngl/ScaleablePixels.hpp" -#include "jngl/Shader.hpp" #include "jngl/matrix.hpp" #include "jngl/other.hpp" #include "jngl/screen.hpp" @@ -17,7 +17,6 @@ #include "log.hpp" #include "paths.hpp" #include "spriteimpl.hpp" -#include "texture.hpp" #include "windowptr.hpp" #include @@ -57,9 +56,6 @@ optional configPath; std::vector args; Rgb backgroundColor(1, 1, 1); std::stack modelviewStack; -std::unique_ptr simpleShaderProgram; -int simpleModelviewUniform; -int simpleColorUniform; void clearBackgroundColor() { glClearColor(backgroundColor.getRed(), backgroundColor.getGreen(), backgroundColor.getBlue(), @@ -99,91 +95,6 @@ bool Init(const int width, const int height, const int canvasWidth, const int ca updateProjection(width, height, width, height); - Shader vertexShader(R"(#version 300 es - in mediump vec2 position; - uniform highp mat3 modelview; - uniform mediump mat4 projection; - - void main() { - vec3 tmp = modelview * vec3(position, 1); - gl_Position = projection * vec4(tmp.x, tmp.y, 0, 1); - })", Shader::Type::VERTEX, R"(#version 100 - attribute mediump vec2 position; - uniform highp mat3 modelview; - uniform mediump mat4 projection; - - void main() { - vec3 tmp = modelview * vec3(position, 1); - gl_Position = projection * vec4(tmp.x, tmp.y, 0, 1); - })" - ); - Shader fragmentShader(R"(#version 300 es - uniform lowp vec4 color; - out lowp vec4 outColor; - - void main() { - outColor = color; - })", Shader::Type::FRAGMENT, R"(#version 100 - uniform lowp vec4 color; - - void main() { - gl_FragColor = color; - })" - ); - simpleShaderProgram = std::make_unique(vertexShader, fragmentShader); - simpleModelviewUniform = simpleShaderProgram->getUniformLocation("modelview"); - simpleColorUniform = simpleShaderProgram->getUniformLocation("color"); - - { - Texture::textureVertexShader = new Shader(R"(#version 300 es - in mediump vec2 position; - in mediump vec2 inTexCoord; - uniform highp mat3 modelview; - uniform mediump mat4 projection; - out mediump vec2 texCoord; - - void main() { - vec3 tmp = modelview * vec3(position, 1); - gl_Position = projection * vec4(tmp.x, tmp.y, 0, 1); - texCoord = inTexCoord; - })", Shader::Type::VERTEX, R"(#version 100 - attribute mediump vec2 position; - attribute mediump vec2 inTexCoord; - uniform highp mat3 modelview; - uniform mediump mat4 projection; - varying mediump vec2 texCoord; - - void main() { - vec3 tmp = modelview * vec3(position, 1); - gl_Position = projection * vec4(tmp.x, tmp.y, 0, 1); - texCoord = inTexCoord; - })"); - Shader fragmentShader(R"(#version 300 es - uniform sampler2D tex; - uniform lowp vec4 spriteColor; - - in mediump vec2 texCoord; - - out lowp vec4 outColor; - - void main() { - outColor = texture(tex, texCoord) * spriteColor; - })", Shader::Type::FRAGMENT, R"(#version 100 - uniform sampler2D tex; - uniform lowp vec4 spriteColor; - - varying mediump vec2 texCoord; - - void main() { - gl_FragColor = texture2D(tex, texCoord) * spriteColor; - })"); - Texture::textureShaderProgram = - new ShaderProgram(*Texture::textureVertexShader, fragmentShader); - Texture::shaderSpriteColorUniform = - Texture::textureShaderProgram->getUniformLocation("spriteColor"); - Texture::modelviewUniform = Texture::textureShaderProgram->getUniformLocation("modelview"); - } - glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -273,7 +184,6 @@ void hideWindow() { if (pWindow) { App::instance().callAtExitFunctions(); } - simpleShaderProgram.reset(); unloadAll(); pWindow.Delete(); } @@ -836,22 +746,6 @@ void writeConfig(const std::string& key, const std::string& value) { } #endif -ShaderProgram::Context useSimpleShaderProgram() { - return useSimpleShaderProgram(opengl::modelview, gShapeColor); -} - -ShaderProgram::Context useSimpleShaderProgram(const Mat3& modelview, Rgba color) { - auto context = jngl::simpleShaderProgram->use(); - glUniform4f(simpleColorUniform, color.getRed(), color.getGreen(), color.getBlue(), - color.getAlpha()); - glUniformMatrix3fv(simpleModelviewUniform, 1, GL_FALSE, modelview.data); - - assert(simpleShaderProgram->getAttribLocation("position") == 0); - glEnableVertexAttribArray(0); - - return context; -} - int round(double v) { assert(!std::isnan(v)); return static_cast(std::lround(v)); diff --git a/src/main.hpp b/src/main.hpp index c8348957..be79a822 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -3,10 +3,6 @@ #pragma once -#include "jngl/Rgba.hpp" -#include "jngl/ShaderProgram.hpp" - -#include #include #include #if defined(__has_include) && __has_include() @@ -19,9 +15,6 @@ using std::experimental::optional; namespace jngl { -class Mat3; -class ShaderProgram; - void clearBackgroundColor(); bool Init(int width, int height, int canvasWidth, int canvasHeight); @@ -31,11 +24,5 @@ void updateProjection(int windowWidth, int windowHeight, int, int); extern std::string pathPrefix; extern optional configPath; extern std::vector args; -extern std::unique_ptr simpleShaderProgram; -extern int simpleModelviewUniform; -extern int simpleColorUniform; - -ShaderProgram::Context useSimpleShaderProgram(); -ShaderProgram::Context useSimpleShaderProgram(const Mat3& modelview, Rgba color); } // namespace jngl diff --git a/src/spriteimpl.cpp b/src/spriteimpl.cpp index 8c251746..2f4f107f 100644 --- a/src/spriteimpl.cpp +++ b/src/spriteimpl.cpp @@ -1,6 +1,5 @@ // Copyright 2007-2024 Jan Niklas Hasse // For conditions of distribution and use, see copyright notice in LICENSE.txt - #include "spriteimpl.hpp" #include "TextureCache.hpp" @@ -8,7 +7,6 @@ #include "jngl/ImageData.hpp" #include "jngl/message.hpp" #include "jngl/screen.hpp" -#include "texture.hpp" #include "windowptr.hpp" namespace jngl { @@ -48,17 +46,16 @@ void setSpriteAlpha(unsigned char alpha) { gSpriteColor.setAlpha(Alpha::u8(alpha)); } -std::unordered_map> sprites_; - // halfLoad is used, if we only want to find out the width or height of an image. Load won't throw // an exception then Sprite& GetSprite(const std::string& filename, const Sprite::LoadType loadType) { - auto it = sprites_.find(filename); - if (it == sprites_.end()) { // texture hasn't been loaded yet? + auto& sprites = TextureCache::handle().sprites; + auto it = sprites.find(filename); + if (it == sprites.end()) { // texture hasn't been loaded yet? if (loadType != Sprite::LoadType::HALF) { pWindow.ThrowIfNull(); } - return *sprites_.emplace(filename, std::make_shared(filename, loadType)) + return *sprites.emplace(filename, std::make_shared(filename, loadType)) .first->second; } return *(it->second); @@ -88,7 +85,7 @@ Finally loadSprite(const std::string& filename) { } Sprite::Loader::Loader(std::string filename) noexcept : filename(std::move(filename)) { - if (sprites_.count(this->filename) == 0) { + if (TextureCache::handle().sprites.count(this->filename) == 0) { imageDataFuture = std::async(std::launch::async, [this]() { auto tmp = ImageData::load(this->filename, getScaleFactor()); tmp->pixels(); // TODO: Not needed when Sprite loading and jngl::load will be reworked @@ -106,17 +103,18 @@ Sprite::Loader::~Loader() noexcept { } std::shared_ptr Sprite::Loader::shared() const { - if (auto it = sprites_.find(filename); it != sprites_.end()) { + auto& sprites = TextureCache::handle().sprites; + if (auto it = sprites.find(filename); it != sprites.end()) { return it->second; } auto imageData = imageDataFuture.get(); double scale = imageData->getImageWidth() == imageData->getWidth() ? getScaleFactor() : 1; - return sprites_.try_emplace(filename, std::make_shared(*imageData, scale, filename)) + return sprites.try_emplace(filename, std::make_shared(*imageData, scale, filename)) .first->second; } Sprite::Loader::operator bool() const { - if (sprites_.count(filename) > 0) { + if (TextureCache::handle().sprites.count(filename) > 0) { return true; } return imageDataFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready; @@ -127,17 +125,16 @@ Sprite* Sprite::Loader::operator->() const { } void unload(const std::string& filename) { - auto it = sprites_.find(filename); - if (it != sprites_.end()) { - sprites_.erase(it); + auto& sprites = TextureCache::handle().sprites; + auto it = sprites.find(filename); + if (it != sprites.end()) { + sprites.erase(it); } TextureCache::handle().remove(filename); } void unloadAll() { - sprites_.clear(); TextureCache::destroy(); - Texture::unloadShader(); } int getWidth(const std::string& filename) { diff --git a/src/texture.cpp b/src/texture.cpp index 4635a4d9..c0dd456d 100644 --- a/src/texture.cpp +++ b/src/texture.cpp @@ -1,20 +1,14 @@ // Copyright 2010-2024 Jan Niklas Hasse // For conditions of distribution and use, see copyright notice in LICENSE.txt - #include "texture.hpp" -#include "jngl/Shader.hpp" +#include "ShaderCache.hpp" #include "jngl/Vertex.hpp" #include namespace jngl { -ShaderProgram* Texture::textureShaderProgram = nullptr; -Shader* Texture::textureVertexShader = nullptr; -int Texture::shaderSpriteColorUniform = -1; -int Texture::modelviewUniform = -1; - Texture::Texture(const float preciseWidth, const float preciseHeight, const int width, const int height, const GLubyte* const* const rowPointers, GLenum format, const GLubyte* const data) : texture_(opengl::genAndBindTexture()) { @@ -38,11 +32,13 @@ Texture::Texture(const float preciseWidth, const float preciseHeight, const int glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer_); glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(GLfloat), vertexes.data(), GL_STATIC_DRAW); - const GLint posAttrib = textureShaderProgram->getAttribLocation("position"); + const GLint posAttrib = + ShaderCache::handle().textureShaderProgram->getAttribLocation("position"); glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), nullptr); glEnableVertexAttribArray(posAttrib); - const GLint texCoordAttrib = textureShaderProgram->getAttribLocation("inTexCoord"); + const GLint texCoordAttrib = + ShaderCache::handle().textureShaderProgram->getAttribLocation("inTexCoord"); glVertexAttribPointer(texCoordAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), reinterpret_cast(2 * sizeof(float))); // NOLINT glEnableVertexAttribArray(texCoordAttrib); @@ -61,8 +57,8 @@ Texture::Texture(const float preciseWidth, const float preciseHeight, const int } Texture::~Texture() { - if (textureShaderProgram) { - // if the textureShaderProgram is nullptr, this means unloadShader() has been called by + if (ShaderCache::handleIfAlive()) { + // if the ShaderCache doesn't exist anymore, this means unloadAll() has been called by // hideWindow() and the OpenGL context doesn't exist anymore. It's unnecessary to delete // OpenGL resources in that case. It might even lead to crashes when the OpenGL function // pointers have been unloaded (Windows). @@ -100,18 +96,19 @@ void Texture::drawClipped(const float xstart, const float xend, const float ysta vertexes[7] = vertexes[11] = yend; glBindVertexArray(opengl::vaoStream); - auto tmp = textureShaderProgram->use(); - glUniform4f(shaderSpriteColorUniform, red, green, blue, alpha); - glUniformMatrix3fv(modelviewUniform, 1, GL_FALSE, opengl::modelview.data); + auto& shaderCache = ShaderCache::handle(); + auto tmp = shaderCache.textureShaderProgram->use(); + glUniform4f(shaderCache.shaderSpriteColorUniform, red, green, blue, alpha); + glUniformMatrix3fv(shaderCache.modelviewUniform, 1, GL_FALSE, opengl::modelview.data); glBindBuffer(GL_ARRAY_BUFFER, opengl::vboStream); // VAO does NOT save the VBO binding glBufferData(GL_ARRAY_BUFFER, static_cast(vertexes.size() * sizeof(float)), vertexes.data(), GL_STREAM_DRAW); - const GLint posAttrib = textureShaderProgram->getAttribLocation("position"); + const GLint posAttrib = shaderCache.textureShaderProgram->getAttribLocation("position"); glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), nullptr); glEnableVertexAttribArray(posAttrib); - const GLint texCoordAttrib = textureShaderProgram->getAttribLocation("inTexCoord"); + const GLint texCoordAttrib = shaderCache.textureShaderProgram->getAttribLocation("inTexCoord"); glVertexAttribPointer(texCoordAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), reinterpret_cast(2 * sizeof(float))); // NOLINT glEnableVertexAttribArray(texCoordAttrib); @@ -126,11 +123,12 @@ void Texture::drawMesh(const std::vector& vertexes) const { glBufferData(GL_ARRAY_BUFFER, static_cast(vertexes.size() * sizeof(vertexes[0])), vertexes.data(), GL_STREAM_DRAW); - const GLint posAttrib = textureShaderProgram->getAttribLocation("position"); + auto& shaderCache = ShaderCache::handle(); + const GLint posAttrib = shaderCache.textureShaderProgram->getAttribLocation("position"); glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), nullptr); glEnableVertexAttribArray(posAttrib); - const GLint texCoordAttrib = textureShaderProgram->getAttribLocation("inTexCoord"); + const GLint texCoordAttrib = shaderCache.textureShaderProgram->getAttribLocation("inTexCoord"); glVertexAttribPointer(texCoordAttrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), reinterpret_cast(2 * sizeof(float))); // NOLINT glEnableVertexAttribArray(texCoordAttrib); @@ -151,13 +149,6 @@ float Texture::getPreciseHeight() const { return vertexes[5]; } -void Texture::unloadShader() { - delete textureVertexShader; - textureVertexShader = nullptr; - delete textureShaderProgram; - textureShaderProgram = nullptr; -} - void Texture::setBytes(const unsigned char* const bytes, const int width, const int height) const { glBindTexture(GL_TEXTURE_2D, texture_); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, bytes); diff --git a/src/texture.hpp b/src/texture.hpp index 2452e1c4..f3f71cd1 100644 --- a/src/texture.hpp +++ b/src/texture.hpp @@ -1,14 +1,9 @@ // Copyright 2010-2024 Jan Niklas Hasse // For conditions of distribution and use, see copyright notice in LICENSE.txt - #pragma once -#include "jngl/ShaderProgram.hpp" #include "opengl.hpp" -#include -#include -#include #include namespace jngl { @@ -33,13 +28,8 @@ class Texture { [[nodiscard]] GLuint getID() const; [[nodiscard]] float getPreciseWidth() const; [[nodiscard]] float getPreciseHeight() const; - static void unloadShader(); void setBytes(const unsigned char*, int width, int height) const; - static ShaderProgram* textureShaderProgram; - static Shader* textureVertexShader; - static int shaderSpriteColorUniform; - static int modelviewUniform; private: GLuint texture_ = 0; GLuint vertexBuffer_ = 0; diff --git a/src/window.cpp b/src/window.cpp index 65188ee0..3c43209a 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -3,17 +3,16 @@ #include "window.hpp" +#include "ShaderCache.hpp" #include "audio.hpp" #include "freetype.hpp" #include "jngl/ScaleablePixels.hpp" #include "jngl/font.hpp" -#include "jngl/matrix.hpp" #include "jngl/other.hpp" #include "jngl/screen.hpp" #include "jngl/time.hpp" #include "jngl/work.hpp" #include "log.hpp" -#include "main.hpp" #include "spriteimpl.hpp" #include "windowptr.hpp" @@ -549,7 +548,7 @@ void Window::initGlObjects() { void Window::drawTriangle(const Vec2 a, const Vec2 b, const Vec2 c) { glBindVertexArray(opengl::vaoStream); - auto tmp = useSimpleShaderProgram(); + auto tmp = ShaderCache::handle().useSimpleShaderProgram(); const float vertexes[] = { static_cast(a.x * getScaleFactor()), static_cast(a.y * getScaleFactor()), static_cast(b.x * getScaleFactor()), static_cast(b.y * getScaleFactor()), @@ -564,13 +563,15 @@ void Window::drawTriangle(const Vec2 a, const Vec2 b, const Vec2 c) { void Window::drawLine(Mat3 modelview, const Vec2 b) const { glBindVertexArray(vaoLine); - auto tmp = useSimpleShaderProgram(modelview.scale(b * getScaleFactor()), gShapeColor); + auto tmp = ShaderCache::handle().useSimpleShaderProgram(modelview.scale(b * getScaleFactor()), + gShapeColor); glDrawArrays(GL_LINES, 0, 2); } void Window::drawSquare(Mat3 modelview, Rgba color) const { glBindVertexArray(vaoSquare); - auto context = useSimpleShaderProgram(modelview.scale(getScaleFactor()), color); + auto context = + ShaderCache::handle().useSimpleShaderProgram(modelview.scale(getScaleFactor()), color); glDrawArrays(GL_TRIANGLE_FAN, 0, 4); }