Skip to content
Snippets Groups Projects
Commit 1203b30c authored by Joseph Walton-Rivers's avatar Joseph Walton-Rivers
Browse files

add 2D fallback shader

parent 56586f45
No related branches found
No related tags found
No related merge requests found
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <fggl/gfx/ogl/renderer.hpp> #include <fggl/gfx/ogl/renderer.hpp>
#include <fggl/gfx/paint.hpp> #include <fggl/gfx/paint.hpp>
#include "fggl/data/model.hpp" #include "fggl/data/model.hpp"
#include "fggl/gfx/ogl4/fallback.hpp"
#include <glm/ext/matrix_clip_space.hpp> #include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_transform.hpp> #include <glm/ext/matrix_transform.hpp>
...@@ -137,7 +138,7 @@ namespace fggl::gfx { ...@@ -137,7 +138,7 @@ namespace fggl::gfx {
using data::Mesh2D; using data::Mesh2D;
using data::Vertex2D; using data::Vertex2D;
OpenGL4Backend::OpenGL4Backend(const Window &owner) : fggl::gfx::Graphics() { OpenGL4Backend::OpenGL4Backend(const Window &owner) : fggl::gfx::Graphics(), m_canvasPipeline(INVALID_SHADER_ID) {
// initialise GLEW, or fail // initialise GLEW, or fail
// FIXME this binds the graphics stack to GLFW :'( // FIXME this binds the graphics stack to GLFW :'(
int version = gladLoadGLLoader( (GLADloadproc)glfwGetProcAddress ); int version = gladLoadGLLoader( (GLADloadproc)glfwGetProcAddress );
...@@ -166,8 +167,13 @@ namespace fggl::gfx { ...@@ -166,8 +167,13 @@ namespace fggl::gfx {
m_cache = std::make_unique<ShaderCache>(storage); m_cache = std::make_unique<ShaderCache>(storage);
// setup 2D rendering system // setup 2D rendering system
ShaderConfig shader2DConfig = ShaderFromName("shader2D"); ShaderConfig shader2DConfig = ShaderFromName("canvas");
m_cache->load(shader2DConfig); m_canvasPipeline = m_cache->load(shader2DConfig);
if ( m_canvasPipeline == INVALID_SHADER_ID) {
debug::error("failed to load shader2D - using fallback");
m_canvasPipeline = m_cache->get(ogl4::FALLBACK_CANVAS_PIPELINE);
}
ShaderConfig shader3DConfig = ShaderFromName("phong"); ShaderConfig shader3DConfig = ShaderFromName("phong");
m_cache->load(shader3DConfig); m_cache->load(shader3DConfig);
...@@ -191,8 +197,7 @@ namespace fggl::gfx { ...@@ -191,8 +197,7 @@ namespace fggl::gfx {
return; return;
} }
auto shader2D = m_cache->get("shader2D"); m_canvasRenderer->render(m_canvasPipeline, paint);
m_canvasRenderer->render(shader2D, paint);
} }
void OpenGL4Backend::drawScene(ecs3::World &world) { void OpenGL4Backend::drawScene(ecs3::World &world) {
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
#include "fggl/gfx/ogl/types.hpp" #include "fggl/gfx/ogl/types.hpp"
#include <fggl/gfx/ogl/shader.hpp> #include <fggl/gfx/ogl/shader.hpp>
#include "fggl/gfx/ogl4/fallback.hpp"
#include <iostream> #include <iostream>
#include <vector> #include <vector>
...@@ -21,17 +22,10 @@ ...@@ -21,17 +22,10 @@
using namespace fggl::gfx; using namespace fggl::gfx;
bool ShaderCache::compileShader(const std::string& fname, GLuint sid) { bool ShaderCache::compileShaderFromSource(const std::string& source, GLuint sid) {
std::string source;
bool result = m_storage->load(fggl::data::Data, fname, &source);
if ( !result ) {
spdlog::warn("could not load shader source from disk: {}", fname.c_str());
return false;
}
// upload and compile shader // upload and compile shader
const auto *src_cstr = (const GLchar *)source.c_str(); const char* src = source.c_str();
glShaderSource(sid, 1, &src_cstr, 0); glShaderSource(sid, 1, &src, nullptr);
glCompileShader(sid); glCompileShader(sid);
// check it worked // check it worked
...@@ -44,15 +38,25 @@ bool ShaderCache::compileShader(const std::string& fname, GLuint sid) { ...@@ -44,15 +38,25 @@ bool ShaderCache::compileShader(const std::string& fname, GLuint sid) {
char* infoLog = new char[maxLength]; char* infoLog = new char[maxLength];
glGetShaderInfoLog(sid, maxLength, &maxLength, infoLog); glGetShaderInfoLog(sid, maxLength, &maxLength, infoLog);
spdlog::warn("could not compile shader, '{}': {}", fname, infoLog); spdlog::warn("could not compile shader source: {}", infoLog);
delete[] infoLog; delete[] infoLog;
return false; return false;
} }
return true; return true;
} }
bool ShaderCache::readAndCompileShader(const std::string &filename, GLuint shader) {
std::string source;
bool result = m_storage->load(fggl::data::Data, filename, &source);
if ( !result ) {
spdlog::warn("could not load shader source from disk: {}", filename.c_str());
return false;
}
return compileShaderFromSource(source, shader);
}
ShaderCache::ShaderCache(std::shared_ptr<fggl::data::Storage> storage) : m_storage(storage), m_shaders(), m_binary(true) { ShaderCache::ShaderCache(std::shared_ptr<fggl::data::Storage> storage) : m_storage(storage), m_shaders(), m_binary(true) {
if ( !GLAD_GL_ARB_get_program_binary ) { if ( !GLAD_GL_ARB_get_program_binary ) {
...@@ -63,6 +67,7 @@ ShaderCache::ShaderCache(std::shared_ptr<fggl::data::Storage> storage) : m_stora ...@@ -63,6 +67,7 @@ ShaderCache::ShaderCache(std::shared_ptr<fggl::data::Storage> storage) : m_stora
if ( GLAD_GL_ARB_shading_language_include ) { if ( GLAD_GL_ARB_shading_language_include ) {
setupIncludes(); setupIncludes();
} }
initFallbackPipelines();
} }
void ShaderCache::setupIncludes() { void ShaderCache::setupIncludes() {
...@@ -80,14 +85,14 @@ void ShaderCache::setupIncludes() { ...@@ -80,14 +85,14 @@ void ShaderCache::setupIncludes() {
} }
bool ShaderCache::loadFromDisk(GLuint pid, const ShaderConfig& config) { bool ShaderCache::loadFromDisk(GLuint pid, const std::string& pipelineName) {
BinaryCache cache; BinaryCache cache;
auto fname = "shader_" + config.name + ".bin"; auto fname = "shader_" + pipelineName + ".bin";
bool status = m_storage->load( fggl::data::Cache, fname, &cache ); bool status = m_storage->load( fggl::data::Cache, fname, &cache );
if ( !status ) { if ( !status ) {
spdlog::info("cached shader '{}' could not be loaded from disk", config.name); spdlog::info("cached shader '{}' could not be loaded from disk", pipelineName);
return false; return false;
} }
...@@ -96,12 +101,11 @@ bool ShaderCache::loadFromDisk(GLuint pid, const ShaderConfig& config) { ...@@ -96,12 +101,11 @@ bool ShaderCache::loadFromDisk(GLuint pid, const ShaderConfig& config) {
return result; return result;
} }
void ShaderCache::saveToDisk(GLuint pid, const ShaderConfig& config) { void ShaderCache::saveToDisk(GLuint pid, const std::string& pipelineName) {
BinaryCache cache; BinaryCache cache;
cacheSave(pid, &cache); cacheSave(pid, &cache);
auto fname = "shader_" + config.name + ".bin"; auto fname = "shader_" + pipelineName + ".bin";
m_storage->save( fggl::data::Cache, fname, &cache); m_storage->save( fggl::data::Cache, fname, &cache);
} }
...@@ -125,7 +129,7 @@ GLuint ShaderCache::load(const ShaderConfig& config) { ...@@ -125,7 +129,7 @@ GLuint ShaderCache::load(const ShaderConfig& config) {
if ( m_binary ) { if ( m_binary ) {
// if we have support for shader cache, give that a go // if we have support for shader cache, give that a go
bool worked = loadFromDisk(pid, config); bool worked = loadFromDisk(pid, config.name);
if ( worked ) { if ( worked ) {
m_shaders[config.name] = pid; m_shaders[config.name] = pid;
return pid; return pid;
...@@ -136,17 +140,17 @@ GLuint ShaderCache::load(const ShaderConfig& config) { ...@@ -136,17 +140,17 @@ GLuint ShaderCache::load(const ShaderConfig& config) {
// TODO actual shader loading // TODO actual shader loading
GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); GLuint vertShader = glCreateShader(GL_VERTEX_SHADER);
compileShader(config.vertex, vertShader); readAndCompileShader(config.vertex, vertShader);
glAttachShader(pid, vertShader); glAttachShader(pid, vertShader);
GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER); GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER);
compileShader(config.fragment, fragShader); readAndCompileShader(config.fragment, fragShader);
glAttachShader(pid, fragShader); glAttachShader(pid, fragShader);
GLuint geomShader; GLuint geomShader = 0;
if ( config.hasGeom ) { if ( config.hasGeom ) {
geomShader = glCreateShader(GL_GEOMETRY_SHADER); geomShader = glCreateShader(GL_GEOMETRY_SHADER);
compileShader( config.geometry, geomShader ); readAndCompileShader( config.geometry, geomShader );
glAttachShader(pid, geomShader); glAttachShader(pid, geomShader);
} }
...@@ -175,11 +179,11 @@ GLuint ShaderCache::load(const ShaderConfig& config) { ...@@ -175,11 +179,11 @@ GLuint ShaderCache::load(const ShaderConfig& config) {
glDeleteShader( geomShader ); glDeleteShader( geomShader );
} }
return false; return INVALID_SHADER_ID;
} }
if ( m_binary ) { if ( m_binary ) {
saveToDisk(pid, config); saveToDisk(pid, config.name);
} }
// update the cache and return // update the cache and return
...@@ -187,6 +191,76 @@ GLuint ShaderCache::load(const ShaderConfig& config) { ...@@ -187,6 +191,76 @@ GLuint ShaderCache::load(const ShaderConfig& config) {
return pid; return pid;
} }
GLuint ShaderCache::load(const ShaderSources& sources, bool allowBinaryCache) {
spdlog::debug("starting shader program generation for {}", sources.name);
GLuint pid = glCreateProgram();
if ( m_binary && allowBinaryCache ) {
// if we have support for shader cache, give that a go
bool worked = loadFromDisk(pid, sources.name);
if ( worked ) {
m_shaders[sources.name] = pid;
return pid;
}
spdlog::debug("could not use cached shader for '{}', doing full compile.", sources.name);
}
// TODO actual shader loading
GLuint vertShader = glCreateShader(GL_VERTEX_SHADER);
compileShaderFromSource(sources.vertexSource, vertShader);
glAttachShader(pid, vertShader);
GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER);
compileShaderFromSource(sources.fragmentSource, fragShader);
glAttachShader(pid, fragShader);
GLuint geomShader = INVALID_SHADER_ID;
if ( !sources.geometrySource.empty() ) {
geomShader = glCreateShader(GL_GEOMETRY_SHADER);
compileShaderFromSource(sources.geometrySource, geomShader);
glAttachShader(pid, geomShader);
}
glLinkProgram(pid);
glDetachShader(pid, vertShader);
glDetachShader(pid, fragShader);
if ( geomShader != INVALID_SHADER_ID ) {
glDetachShader(pid, geomShader);
}
GLint linked = GL_FALSE;
glGetProgramiv(pid, GL_LINK_STATUS, &linked);
if ( linked == GL_FALSE ) {
// get the error
std::array<char, 512> infoLog;
glGetProgramInfoLog(pid, infoLog.size(), nullptr, infoLog.data());
spdlog::warn("linking shader program '{}' failed: {}", sources.name, infoLog.data());
// cleanup
glDeleteProgram( pid );
glDeleteShader( vertShader );
glDeleteShader( fragShader );
if ( geomShader != INVALID_SHADER_ID ) {
glDeleteShader( geomShader );
}
return INVALID_SHADER_ID;
}
if ( m_binary && allowBinaryCache ) {
saveToDisk(pid, sources.name);
}
// update the cache and return
m_shaders[ sources.name ] = pid;
return pid;
}
void ShaderCache::cacheSave(GLuint pid, BinaryCache* cache) { void ShaderCache::cacheSave(GLuint pid, BinaryCache* cache) {
GLsizei length; GLsizei length;
glGetProgramiv(pid, GL_PROGRAM_BINARY_LENGTH, &length); glGetProgramiv(pid, GL_PROGRAM_BINARY_LENGTH, &length);
...@@ -209,6 +283,16 @@ bool ShaderCache::cacheLoad(GLuint pid, const BinaryCache* cache) { ...@@ -209,6 +283,16 @@ bool ShaderCache::cacheLoad(GLuint pid, const BinaryCache* cache) {
return status == GL_TRUE; return status == GL_TRUE;
} }
void ShaderCache::initFallbackPipelines() {
// canvas fallback pipeline
load({
.name = ogl4::FALLBACK_CANVAS_PIPELINE,
.vertexSource = ogl4::FALLBACK_CANVAS_VERTEX_SHADER,
.fragmentSource = ogl4::FALLBACK_CANVAS_FRAGMENT_SHADER,
.geometrySource = ""
}, false);
}
template<> template<>
bool fggl::data::fggl_deserialize(std::filesystem::path &data, fggl::gfx::BinaryCache *out) { bool fggl::data::fggl_deserialize(std::filesystem::path &data, fggl::gfx::BinaryCache *out) {
auto f = auto f =
......
...@@ -119,6 +119,7 @@ namespace fggl::gfx { ...@@ -119,6 +119,7 @@ namespace fggl::gfx {
std::unique_ptr<ogl4::CanvasRenderer> m_canvasRenderer; std::unique_ptr<ogl4::CanvasRenderer> m_canvasRenderer;
std::unique_ptr<ogl4::DebugRenderer> m_debugRenderer; std::unique_ptr<ogl4::DebugRenderer> m_debugRenderer;
std::unique_ptr<ShaderCache> m_cache; std::unique_ptr<ShaderCache> m_cache;
GLuint m_canvasPipeline;
}; };
using OpenGL4 = OpenGL4Backend; using OpenGL4 = OpenGL4Backend;
......
...@@ -25,6 +25,8 @@ ...@@ -25,6 +25,8 @@
namespace fggl::gfx { namespace fggl::gfx {
constexpr GLuint INVALID_SHADER_ID = 0;
struct ShaderConfig { struct ShaderConfig {
// required // required
std::string name; std::string name;
...@@ -36,6 +38,13 @@ namespace fggl::gfx { ...@@ -36,6 +38,13 @@ namespace fggl::gfx {
bool hasGeom = false; bool hasGeom = false;
}; };
struct ShaderSources {
std::string name;
std::string vertexSource;
std::string fragmentSource;
std::string geometrySource;
};
inline ShaderConfig ShaderFromName(const std::string &name) { inline ShaderConfig ShaderFromName(const std::string &name) {
return { return {
name, name,
...@@ -56,10 +65,19 @@ namespace fggl::gfx { ...@@ -56,10 +65,19 @@ namespace fggl::gfx {
~ShaderCache() = default; ~ShaderCache() = default;
GLuint load(const ShaderConfig &config); GLuint load(const ShaderConfig &config);
GLuint load(const ShaderSources &sources, bool allowBinaryCache);
GLuint getOrLoad(const ShaderConfig &config); GLuint getOrLoad(const ShaderConfig &config);
GLuint get(const std::string &name); GLuint get(const std::string &name);
/**
* Fallback pipelines.
*
* All fallback pipelines are prefixed with "fallback_". They should be used as 'last resort' if there is
* no matching shader on disk for this game.
*/
void initFallbackPipelines();
private: private:
std::shared_ptr<fggl::data::Storage> m_storage; std::shared_ptr<fggl::data::Storage> m_storage;
...@@ -69,11 +87,12 @@ namespace fggl::gfx { ...@@ -69,11 +87,12 @@ namespace fggl::gfx {
void setupIncludes(); void setupIncludes();
// opengl operations // opengl operations
bool compileShader(const std::string &, GLuint); bool readAndCompileShader(const std::string& filename, GLuint shader);
bool compileShaderFromSource(const std::string& source, GLuint);
// file io operations // file io operations
bool loadFromDisk(GLuint pid, const ShaderConfig &config); bool loadFromDisk(GLuint pid, const std::string &pipelineName);
void saveToDisk(GLuint pid, const ShaderConfig &config); void saveToDisk(GLuint pid, const std::string &pipelineName);
bool m_binary; bool m_binary;
void cacheSave(GLuint pid, BinaryCache *cache); void cacheSave(GLuint pid, BinaryCache *cache);
......
/*
* This file is part of FGGL.
*
* FGGL is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* FGGL 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with FGGL.
* If not, see <https://www.gnu.org/licenses/>.
*/
//
// Created by webpigeon on 22/06/22.
//
#ifndef FGGL_GFX_OGL4_FALLBACK_HPP
#define FGGL_GFX_OGL4_FALLBACK_HPP
/**
* Fallback shaders.
*
* Embedded in the library, so that the user can use the library without having to include the shaders.
*/
namespace fggl::gfx::ogl4 {
constexpr const char* FALLBACK_CANVAS_PIPELINE = "fallback_canvas";
constexpr const char* FALLBACK_CANVAS_VERTEX_SHADER = R"glsl(
#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec3 aColour;
layout (location = 2) in vec2 aTexPos;
uniform mat4 projection;
out vec3 colour;
out vec2 texPos;
void main() {
gl_Position = projection * vec4(aPos, 0.0, 1.0);
colour = aColour;
texPos = aTexPos;
})glsl";
constexpr const char* FALLBACK_CANVAS_FRAGMENT_SHADER = R"glsl(
#version 330 core
uniform sampler2D tex;
in vec3 colour;
in vec2 texPos;
out vec4 fragColour;
void main() {
fragColour = vec4(colour.xyz, texture(tex, texPos).r);
})glsl";
} // namespace fggl::gfx::ogl4
#endif //FGGL_GFX_OGL4_FALLBACK_HPP
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment