refactor GLSLExtension.cpp & add 'param' shader preprocessor directive & add PostEffect class (WIP)
This commit is contained in:
parent
531334f059
commit
1feee3a809
@ -8,7 +8,9 @@ class BasicParser {
|
|||||||
using StringT = std::basic_string<CharT>;
|
using StringT = std::basic_string<CharT>;
|
||||||
using StringViewT = std::basic_string_view<CharT>;
|
using StringViewT = std::basic_string_view<CharT>;
|
||||||
|
|
||||||
|
void skipWhitespaceBasic(bool newline = true);
|
||||||
void skipWhitespaceHashComment(bool newline = true);
|
void skipWhitespaceHashComment(bool newline = true);
|
||||||
|
void skipWhitespaceCLikeComment(bool newline = true);
|
||||||
protected:
|
protected:
|
||||||
std::string_view filename;
|
std::string_view filename;
|
||||||
StringViewT source;
|
StringViewT source;
|
||||||
@ -16,6 +18,7 @@ protected:
|
|||||||
uint line = 1;
|
uint line = 1;
|
||||||
uint linestart = 0;
|
uint linestart = 0;
|
||||||
bool hashComment = false;
|
bool hashComment = false;
|
||||||
|
bool clikeComment = false;
|
||||||
|
|
||||||
void skipWhitespace(bool newline = true);
|
void skipWhitespace(bool newline = true);
|
||||||
void skip(size_t n);
|
void skip(size_t n);
|
||||||
@ -35,7 +38,7 @@ protected:
|
|||||||
StringT parseString(CharT chr, bool closeRequired = true);
|
StringT parseString(CharT chr, bool closeRequired = true);
|
||||||
|
|
||||||
parsing_error error(const std::string& message);
|
parsing_error error(const std::string& message);
|
||||||
public:
|
|
||||||
StringViewT readUntil(CharT c);
|
StringViewT readUntil(CharT c);
|
||||||
StringViewT readUntil(StringViewT s, bool nothrow);
|
StringViewT readUntil(StringViewT s, bool nothrow);
|
||||||
StringViewT readUntilWhitespace();
|
StringViewT readUntilWhitespace();
|
||||||
|
|||||||
@ -31,13 +31,9 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename CharT>
|
template<typename CharT>
|
||||||
void BasicParser<CharT>::skipWhitespace(bool newline) {
|
void BasicParser<CharT>::skipWhitespaceBasic(bool newline) {
|
||||||
if (hashComment) {
|
|
||||||
skipWhitespaceHashComment(newline);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
while (hasNext()) {
|
while (hasNext()) {
|
||||||
char next = source[pos];
|
CharT next = source[pos];
|
||||||
if (next == '\n') {
|
if (next == '\n') {
|
||||||
if (!newline) {
|
if (!newline) {
|
||||||
break;
|
break;
|
||||||
@ -55,23 +51,20 @@ void BasicParser<CharT>::skipWhitespace(bool newline) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename CharT>
|
template<typename CharT>
|
||||||
void BasicParser<CharT>::skipWhitespaceHashComment(bool newline) {
|
void BasicParser<CharT>::skipWhitespace(bool newline) {
|
||||||
while (hasNext()) {
|
if (hashComment) {
|
||||||
char next = source[pos];
|
skipWhitespaceHashComment(newline);
|
||||||
if (next == '\n') {
|
return;
|
||||||
if (!newline) {
|
} else if (clikeComment) {
|
||||||
break;
|
skipWhitespaceCLikeComment(newline);
|
||||||
}
|
return;
|
||||||
line++;
|
|
||||||
linestart = ++pos;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (is_whitespace(next)) {
|
|
||||||
pos++;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
skipWhitespaceBasic(newline);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename CharT>
|
||||||
|
void BasicParser<CharT>::skipWhitespaceHashComment(bool newline) {
|
||||||
|
skipWhitespaceBasic(newline);
|
||||||
if (hasNext() && source[pos] == '#') {
|
if (hasNext() && source[pos] == '#') {
|
||||||
if (!newline) {
|
if (!newline) {
|
||||||
readUntilEOL();
|
readUntilEOL();
|
||||||
@ -84,6 +77,39 @@ void BasicParser<CharT>::skipWhitespaceHashComment(bool newline) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename CharT>
|
||||||
|
void BasicParser<CharT>::skipWhitespaceCLikeComment(bool newline) {
|
||||||
|
skipWhitespaceBasic(newline);
|
||||||
|
if (hasNext() && source[pos] == '/' && pos + 1 < source.length()) {
|
||||||
|
pos++;
|
||||||
|
switch (source[pos]) {
|
||||||
|
case '*':
|
||||||
|
pos++;
|
||||||
|
while (hasNext()) {
|
||||||
|
if (source[pos] == '/' && source[pos-1] == '*') {
|
||||||
|
pos++;
|
||||||
|
skipWhitespace();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case '/':
|
||||||
|
if (!newline) {
|
||||||
|
readUntilEOL();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
skipLine();
|
||||||
|
if (hasNext() && (is_whitespace(source[pos]) || source[pos] == '/')) {
|
||||||
|
skipWhitespaceCLikeComment(newline);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
pos--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename CharT>
|
template<typename CharT>
|
||||||
void BasicParser<CharT>::skip(size_t n) {
|
void BasicParser<CharT>::skip(size_t n) {
|
||||||
n = std::min(n, source.length() - pos);
|
n = std::min(n, source.length() - pos);
|
||||||
|
|||||||
@ -1,23 +1,28 @@
|
|||||||
#include "GLSLExtension.hpp"
|
#include "GLSLExtension.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "debug/Logger.hpp"
|
||||||
#include "io/engine_paths.hpp"
|
#include "io/engine_paths.hpp"
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
|
#include "coders/BasicParser.hpp"
|
||||||
|
#include "graphics/core/PostEffect.hpp"
|
||||||
|
|
||||||
void GLSLExtension::setVersion(std::string version) {
|
static debug::Logger logger("glsl-extension");
|
||||||
this->version = std::move(version);
|
|
||||||
}
|
using Type = PostEffect::Param::Type;
|
||||||
|
|
||||||
void GLSLExtension::setPaths(const ResPaths* paths) {
|
void GLSLExtension::setPaths(const ResPaths* paths) {
|
||||||
this->paths = paths;
|
this->paths = paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLSLExtension::loadHeader(const std::string& name) {
|
void GLSLExtension::loadHeader(const std::string& name) {
|
||||||
|
if (paths == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
io::path file = paths->find("shaders/lib/" + name + ".glsl");
|
io::path file = paths->find("shaders/lib/" + name + ".glsl");
|
||||||
std::string source = io::read_string(file);
|
std::string source = io::read_string(file);
|
||||||
addHeader(name, "");
|
addHeader(name, "");
|
||||||
@ -48,6 +53,10 @@ const std::string& GLSLExtension::getDefine(const std::string& name) const {
|
|||||||
return found->second;
|
return found->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::unordered_map<std::string, std::string>& GLSLExtension::getDefines() const {
|
||||||
|
return defines;
|
||||||
|
}
|
||||||
|
|
||||||
bool GLSLExtension::hasDefine(const std::string& name) const {
|
bool GLSLExtension::hasDefine(const std::string& name) const {
|
||||||
return defines.find(name) != defines.end();
|
return defines.find(name) != defines.end();
|
||||||
}
|
}
|
||||||
@ -72,76 +81,125 @@ inline std::runtime_error parsing_error(
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline void parsing_warning(
|
inline void parsing_warning(
|
||||||
const io::path& file, uint linenum, const std::string& message
|
std::string_view file, uint linenum, const std::string& message
|
||||||
) {
|
) {
|
||||||
std::cerr << "file " + file.string() + ": warning: " + message +
|
logger.warning() << "file " + std::string(file) + ": warning: " + message +
|
||||||
" at line " + std::to_string(linenum)
|
" at line " + std::to_string(linenum);
|
||||||
<< std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void source_line(std::stringstream& ss, uint linenum) {
|
inline void source_line(std::stringstream& ss, uint linenum) {
|
||||||
ss << "#line " << linenum << "\n";
|
ss << "#line " << linenum << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::optional<PostEffect::Param::Type> param_type_from(
|
||||||
|
const std::string& name
|
||||||
|
) {
|
||||||
|
static const std::unordered_map<std::string, PostEffect::Param::Type> typeNames {
|
||||||
|
{"float", Type::FLOAT},
|
||||||
|
{"vec2", Type::VEC2},
|
||||||
|
{"vec3", Type::VEC3},
|
||||||
|
{"vec4", Type::VEC4},
|
||||||
|
};
|
||||||
|
const auto& found = typeNames.find(name);
|
||||||
|
if (found == typeNames.end()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GLSLParser : public BasicParser<char> {
|
||||||
|
public:
|
||||||
|
GLSLParser(GLSLExtension& glsl, std::string_view file, std::string_view source, bool header)
|
||||||
|
: BasicParser(file, source), glsl(glsl) {
|
||||||
|
if (!header) {
|
||||||
|
ss << "#version " << GLSLExtension::VERSION << '\n';
|
||||||
|
}
|
||||||
|
for (auto& entry : glsl.getDefines()) {
|
||||||
|
ss << "#define " << entry.first << " " << entry.second << '\n';
|
||||||
|
}
|
||||||
|
uint linenum = 1;
|
||||||
|
source_line(ss, linenum);
|
||||||
|
|
||||||
|
clikeComment = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool processPreprocessorDirective() {
|
||||||
|
skip(1);
|
||||||
|
|
||||||
|
auto name = parseName();
|
||||||
|
|
||||||
|
if (name == "version") {
|
||||||
|
parsing_warning(filename, line, "removed #version directive");
|
||||||
|
source_line(ss, line);
|
||||||
|
skipLine();
|
||||||
|
return false;
|
||||||
|
} else if (name == "include") {
|
||||||
|
skipWhitespace(false);
|
||||||
|
if (peekNoJump() != '<') {
|
||||||
|
throw error("'<' expected");
|
||||||
|
}
|
||||||
|
skip(1);
|
||||||
|
skipWhitespace(false);
|
||||||
|
auto headerName = parseName();
|
||||||
|
skipWhitespace(false);
|
||||||
|
if (peekNoJump() != '>') {
|
||||||
|
throw error("'>' expected");
|
||||||
|
}
|
||||||
|
skip(1);
|
||||||
|
skipWhitespace(false);
|
||||||
|
skipLine();
|
||||||
|
|
||||||
|
if (!glsl.hasHeader(headerName)) {
|
||||||
|
glsl.loadHeader(headerName);
|
||||||
|
}
|
||||||
|
ss << glsl.getHeader(headerName) << '\n';
|
||||||
|
source_line(ss, line);
|
||||||
|
return false;
|
||||||
|
} else if (name == "param") {
|
||||||
|
skipWhitespace(false);
|
||||||
|
auto typeName = parseName();
|
||||||
|
auto type = param_type_from(typeName);
|
||||||
|
if (!type.has_value()) {
|
||||||
|
throw error("unsupported param type " + util::quote(typeName));
|
||||||
|
}
|
||||||
|
skipWhitespace(false);
|
||||||
|
auto paramName = parseName();
|
||||||
|
if (params.find(paramName) != params.end()) {
|
||||||
|
throw error("duplicating param " + util::quote(paramName));
|
||||||
|
}
|
||||||
|
skipLine();
|
||||||
|
|
||||||
|
ss << "uniform " << typeName << " " << paramName << ";\n";
|
||||||
|
params[paramName] = PostEffect::Param(type.value());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string process() {
|
||||||
|
while (hasNext()) {
|
||||||
|
skipWhitespace(false);
|
||||||
|
if (!hasNext()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (source[pos] != '#' || processPreprocessorDirective()) {
|
||||||
|
pos = linestart;
|
||||||
|
ss << readUntilEOL() << '\n';
|
||||||
|
skip(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
GLSLExtension& glsl;
|
||||||
|
std::unordered_map<std::string, PostEffect::Param> params;
|
||||||
|
std::stringstream ss;
|
||||||
|
};
|
||||||
|
|
||||||
std::string GLSLExtension::process(
|
std::string GLSLExtension::process(
|
||||||
const io::path& file, const std::string& source, bool header
|
const io::path& file, const std::string& source, bool header
|
||||||
) {
|
) {
|
||||||
std::stringstream ss;
|
std::string filename = file.string();
|
||||||
size_t pos = 0;
|
GLSLParser parser(*this, filename, source, header);
|
||||||
uint linenum = 1;
|
return parser.process();
|
||||||
if (!header) {
|
|
||||||
ss << "#version " << version << '\n';
|
|
||||||
}
|
|
||||||
for (auto& entry : defines) {
|
|
||||||
ss << "#define " << entry.first << " " << entry.second << '\n';
|
|
||||||
}
|
|
||||||
source_line(ss, linenum);
|
|
||||||
while (pos < source.length()) {
|
|
||||||
size_t endline = source.find('\n', pos);
|
|
||||||
if (endline == std::string::npos) {
|
|
||||||
endline = source.length();
|
|
||||||
}
|
|
||||||
// parsing preprocessor directives
|
|
||||||
if (source[pos] == '#') {
|
|
||||||
std::string line = source.substr(pos + 1, endline - pos);
|
|
||||||
util::trim(line);
|
|
||||||
// parsing 'include' directive
|
|
||||||
if (line.find("include") != std::string::npos) {
|
|
||||||
line = line.substr(7);
|
|
||||||
util::trim(line);
|
|
||||||
if (line.length() < 3) {
|
|
||||||
throw parsing_error(
|
|
||||||
file, linenum, "invalid 'include' syntax"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (line[0] != '<' || line[line.length() - 1] != '>') {
|
|
||||||
throw parsing_error(
|
|
||||||
file, linenum, "expected '#include <filename>' syntax"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
std::string name = line.substr(1, line.length() - 2);
|
|
||||||
if (!hasHeader(name)) {
|
|
||||||
loadHeader(name);
|
|
||||||
}
|
|
||||||
source_line(ss, 1);
|
|
||||||
ss << getHeader(name) << '\n';
|
|
||||||
pos = endline + 1;
|
|
||||||
linenum++;
|
|
||||||
source_line(ss, linenum);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// removing extra 'include' directives
|
|
||||||
else if (line.find("version") != std::string::npos) {
|
|
||||||
parsing_warning(file, linenum, "removed #version directive");
|
|
||||||
pos = endline + 1;
|
|
||||||
linenum++;
|
|
||||||
source_line(ss, linenum);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
linenum++;
|
|
||||||
ss << source.substr(pos, endline + 1 - pos);
|
|
||||||
pos = endline + 1;
|
|
||||||
}
|
|
||||||
return ss.str();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,15 +9,8 @@
|
|||||||
class ResPaths;
|
class ResPaths;
|
||||||
|
|
||||||
class GLSLExtension {
|
class GLSLExtension {
|
||||||
std::unordered_map<std::string, std::string> headers;
|
|
||||||
std::unordered_map<std::string, std::string> defines;
|
|
||||||
std::string version = "330 core";
|
|
||||||
|
|
||||||
const ResPaths* paths = nullptr;
|
|
||||||
void loadHeader(const std::string& name);
|
|
||||||
public:
|
public:
|
||||||
void setPaths(const ResPaths* paths);
|
void setPaths(const ResPaths* paths);
|
||||||
void setVersion(std::string version);
|
|
||||||
|
|
||||||
void define(const std::string& name, std::string value);
|
void define(const std::string& name, std::string value);
|
||||||
void undefine(const std::string& name);
|
void undefine(const std::string& name);
|
||||||
@ -26,12 +19,22 @@ public:
|
|||||||
const std::string& getHeader(const std::string& name) const;
|
const std::string& getHeader(const std::string& name) const;
|
||||||
const std::string& getDefine(const std::string& name) const;
|
const std::string& getDefine(const std::string& name) const;
|
||||||
|
|
||||||
|
const std::unordered_map<std::string, std::string>& getDefines() const;
|
||||||
|
|
||||||
bool hasHeader(const std::string& name) const;
|
bool hasHeader(const std::string& name) const;
|
||||||
bool hasDefine(const std::string& name) const;
|
bool hasDefine(const std::string& name) const;
|
||||||
|
void loadHeader(const std::string& name);
|
||||||
|
|
||||||
std::string process(
|
std::string process(
|
||||||
const io::path& file,
|
const io::path& file,
|
||||||
const std::string& source,
|
const std::string& source,
|
||||||
bool header = false
|
bool header = false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static inline std::string VERSION = "330 core";
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, std::string> headers;
|
||||||
|
std::unordered_map<std::string, std::string> defines;
|
||||||
|
|
||||||
|
const ResPaths* paths = nullptr;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -16,7 +16,7 @@ using namespace toml;
|
|||||||
class TomlReader : BasicParser<char> {
|
class TomlReader : BasicParser<char> {
|
||||||
dv::value root;
|
dv::value root;
|
||||||
|
|
||||||
// modified version of BaseParser.parseString
|
// modified version of BasicParser.parseString
|
||||||
// todo: extract common part
|
// todo: extract common part
|
||||||
std::string parseMultilineString() {
|
std::string parseMultilineString() {
|
||||||
pos += 2;
|
pos += 2;
|
||||||
|
|||||||
15
src/graphics/core/PostEffect.cpp
Normal file
15
src/graphics/core/PostEffect.cpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include "PostEffect.hpp"
|
||||||
|
|
||||||
|
#include "Shader.hpp"
|
||||||
|
|
||||||
|
PostEffect::Param::Param() : type(Type::FLOAT) {}
|
||||||
|
|
||||||
|
PostEffect::Param::Param(Type type) : type(type) {}
|
||||||
|
|
||||||
|
PostEffect::PostEffect(std::unique_ptr<Shader> shader)
|
||||||
|
: shader(std::move(shader)) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void PostEffect::use() {
|
||||||
|
shader->use();
|
||||||
|
}
|
||||||
29
src/graphics/core/PostEffect.hpp
Normal file
29
src/graphics/core/PostEffect.hpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <variant>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
class Shader;
|
||||||
|
|
||||||
|
class PostEffect {
|
||||||
|
public:
|
||||||
|
struct Param {
|
||||||
|
enum class Type { FLOAT, VEC2, VEC3, VEC4 };
|
||||||
|
using Value = std::variant<float, glm::vec2, glm::vec3, glm::vec4>;
|
||||||
|
|
||||||
|
Type type;
|
||||||
|
|
||||||
|
Param();
|
||||||
|
Param(Type type);
|
||||||
|
};
|
||||||
|
|
||||||
|
PostEffect(std::unique_ptr<Shader> shader);
|
||||||
|
|
||||||
|
void use();
|
||||||
|
private:
|
||||||
|
std::unique_ptr<Shader> shader;
|
||||||
|
std::unordered_map<std::string, Param> params;
|
||||||
|
};
|
||||||
32
test/coders/GLSLExtension.cpp
Normal file
32
test/coders/GLSLExtension.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "coders/commons.hpp"
|
||||||
|
#include "coders/GLSLExtension.hpp"
|
||||||
|
|
||||||
|
TEST(GLSLExtension, processing) {
|
||||||
|
GLSLExtension glsl;
|
||||||
|
glsl.addHeader("sum",
|
||||||
|
"// sum function for glsl\n"
|
||||||
|
"float sum(float a, float b) {\n"
|
||||||
|
" return a + b;\n"
|
||||||
|
"}\n"
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
auto processed = glsl.process("test.glsl",
|
||||||
|
"in vec2 v_uv;\n"
|
||||||
|
"uniform sampler2D u_screen;\n"
|
||||||
|
"\n"
|
||||||
|
"#include /* hell\no */ < sum >\n"
|
||||||
|
"#param float p_intensity\n"
|
||||||
|
"\n"
|
||||||
|
"vec4 effect() {\n"
|
||||||
|
" vec4 color = texture(u_screen, v_uv);\n"
|
||||||
|
" return mix(color, 1.0 - color, p_intensity);\n"
|
||||||
|
"}\n",
|
||||||
|
false);
|
||||||
|
std::cout << processed << std::endl;
|
||||||
|
} catch (const parsing_error& err) {
|
||||||
|
std::cerr << err.errorLog() << std::endl;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user