diff --git a/src/modules/graphics/Shader.cpp b/src/modules/graphics/Shader.cpp index b91a9c694..eea1e4a95 100644 --- a/src/modules/graphics/Shader.cpp +++ b/src/modules/graphics/Shader.cpp @@ -22,6 +22,7 @@ #include "Shader.h" #include "Graphics.h" #include "math/MathModule.h" +#include "common/Range.h" // glslang #include "libraries/glslang/glslang/Public/ShaderLang.h" @@ -425,6 +426,111 @@ static const Version versions[] = { "#version 430 core", "#version 320 es" }, }; +enum CommentType +{ + COMMENT_NONE, + COMMENT_LINE, + COMMENT_BLOCK, +}; + +static void parseComments(const std::string &src, std::vector &comments) +{ + CommentType commenttype = COMMENT_NONE; + Range comment; + + const char *srcbytes = src.data(); + size_t len = src.length(); + + for (size_t i = 0; i < len; i++) + { + char curchar = srcbytes[i]; + + if (commenttype == COMMENT_NONE) + { + if (curchar == '/' && i + 1 < len) + { + char nextchar = srcbytes[i + 1]; + if (nextchar == '/') + { + commenttype = COMMENT_LINE; + comment = Range(i, 1); + } + else if (nextchar == '*') + { + commenttype = COMMENT_BLOCK; + comment = Range(i, 1); + } + } + } + else if (commenttype == COMMENT_LINE) + { + if (curchar == '\n') + { + commenttype = COMMENT_NONE; + comment.last = i; + comments.push_back(comment); + } + } + else if (commenttype == COMMENT_BLOCK) + { + if (curchar == '/' && i > 0 && srcbytes[i - 1] == '*') + { + commenttype = COMMENT_NONE; + comment.last = i; + comments.push_back(comment); + } + } + } + + if (commenttype == COMMENT_LINE) + { + comment.last = len - 1; + comments.push_back(comment); + } +} + +static bool inComment(size_t i, const std::vector &comments) +{ + Range r(i, 1); + + for (const Range &comment : comments) + { + if (comment.contains(r)) + return true; + } + + return false; +} + +static bool textSearch(const std::string &src, const std::string &str, const std::vector &comments) +{ + size_t start = 0; + size_t found = std::string::npos; + + while ((found = src.find(str, start)) != std::string::npos) + { + if (!inComment(found, comments)) + return true; + start = found + str.size(); + } + + return false; +} + +static bool regexSearch(const std::string &src, const std::string &rstr, const std::vector &comments) +{ + std::regex r(rstr); + + for (auto it = std::sregex_iterator(src.begin(), src.end(), r); it != std::sregex_iterator(); it++) + { + const std::smatch &m = *it; + if (!inComment(m.position(), comments)) + return true; + } + + return false; +} + static Shader::Language getTargetLanguage(const std::string &src) { std::regex r("^\\s*#pragma language (\\w+)"); @@ -435,33 +541,30 @@ static Shader::Language getTargetLanguage(const std::string &src) return lang; } -static Shader::EntryPoint getVertexEntryPoint(const std::string &src) +static Shader::EntryPoint getVertexEntryPoint(const std::string &src, const std::vector &comments) { - std::smatch m; - - if (std::regex_search(src, m, std::regex("void\\s+vertexmain\\s*\\("))) + if (regexSearch(src, "void\\s+vertexmain\\s*\\(", comments)) return Shader::ENTRYPOINT_RAW; - if (std::regex_search(src, m, std::regex("vec4\\s+position\\s*\\("))) + if (regexSearch(src, "vec4\\s+position\\s*\\(", comments)) return Shader::ENTRYPOINT_HIGHLEVEL; return Shader::ENTRYPOINT_NONE; } -static Shader::EntryPoint getPixelEntryPoint(const std::string &src, bool &mrt) +static Shader::EntryPoint getPixelEntryPoint(const std::string &src, const std::vector &comments, bool &mrt) { mrt = false; - std::smatch m; - if (std::regex_search(src, m, std::regex("void\\s+pixelmain\\s*\\("))) + if (regexSearch(src, "void\\s+pixelmain\\s*\\(", comments)) return Shader::ENTRYPOINT_RAW; - if (std::regex_search(src, m, std::regex("vec4\\s+effect\\s*\\("))) + if (regexSearch(src, "vec4\\s+effect\\s*\\(", comments)) return Shader::ENTRYPOINT_HIGHLEVEL; - if (std::regex_search(src, m, std::regex("void\\s+effect\\s*\\("))) + if (regexSearch(src, "void\\s+effect\\s*\\(", comments)) { - if (src.find("love_RenderTargets") != std::string::npos || src.find("love_Canvases") != std::string::npos) + if (textSearch(src, "love_RenderTargets", comments) || textSearch(src, "love_Canvases", comments)) mrt = true; return Shader::ENTRYPOINT_CUSTOM; } @@ -469,10 +572,9 @@ static Shader::EntryPoint getPixelEntryPoint(const std::string &src, bool &mrt) return Shader::ENTRYPOINT_NONE; } -static Shader::EntryPoint getComputeEntryPoint(const std::string &src) { - std::smatch m; - - if (std::regex_search(src, m, std::regex("void\\s+computemain\\s*\\("))) +static Shader::EntryPoint getComputeEntryPoint(const std::string &src, const std::vector &comments) +{ + if (regexSearch(src, "void\\s+computemain\\s*\\(", comments)) return Shader::ENTRYPOINT_RAW; return Shader::ENTRYPOINT_NONE; @@ -489,11 +591,14 @@ Shader *Shader::standardShaders[Shader::STANDARD_MAX_ENUM] = {nullptr}; Shader::SourceInfo Shader::getSourceInfo(const std::string &src) { + std::vector comments; + glsl::parseComments(src, comments); + SourceInfo info = {}; info.language = glsl::getTargetLanguage(src); - info.stages[SHADERSTAGE_VERTEX] = glsl::getVertexEntryPoint(src); - info.stages[SHADERSTAGE_PIXEL] = glsl::getPixelEntryPoint(src, info.usesMRT); - info.stages[SHADERSTAGE_COMPUTE] = glsl::getComputeEntryPoint(src); + info.stages[SHADERSTAGE_VERTEX] = glsl::getVertexEntryPoint(src, comments); + info.stages[SHADERSTAGE_PIXEL] = glsl::getPixelEntryPoint(src, comments, info.usesMRT); + info.stages[SHADERSTAGE_COMPUTE] = glsl::getComputeEntryPoint(src, comments); if (info.stages[SHADERSTAGE_COMPUTE]) info.language = LANGUAGE_GLSL4; return info;