#include "shaderc.hpp" #include "athena/FileReader.hpp" #include "logvisor/logvisor.hpp" #include "hecl/hecl.hpp" #include "hecl/PipelineBase.hpp" #include #include #include #include #include #include #include #include #include using namespace std::literals; namespace hecl::shaderc { static logvisor::Module Log("shaderc"); constexpr std::regex::flag_type RegexFlags = std::regex::ECMAScript | std::regex::optimize; static const char* StageNames[] = { "hecl::PipelineStage::Vertex", "hecl::PipelineStage::Fragment", "hecl::PipelineStage::Geometry", "hecl::PipelineStage::Control", "hecl::PipelineStage::Evaluation" }; const std::string* Compiler::getFileContents(SystemStringView path) { // TODO: Heterogeneous lookup when C++20 available auto search = m_fileContents.find(path.data()); if (search == m_fileContents.end()) { athena::io::FileReader r(path); if (r.hasError()) return nullptr; auto len = r.length(); auto data = r.readBytes(len); search = m_fileContents.insert(std::make_pair(path.data(), std::string((char*)data.get(), len))).first; } return &search->second; } void Compiler::addInputFile(SystemStringView file) { if (std::find(m_inputFiles.begin(), m_inputFiles.end(), file) == m_inputFiles.end()) m_inputFiles.emplace_back(file); } void Compiler::addDefine(std::string_view var, std::string_view val) { m_defines[var.data()] = val; } constexpr auto ShaderHeaderTemplate = FMT_STRING( "class Shader_{} : public hecl::GeneralShader\n" "{{\n" "public:\n" " static const boo::VertexFormatInfo VtxFmt;\n" " static const boo::AdditionalPipelineInfo PipelineInfo;\n" " static constexpr bool HasHash = true;\n" " static constexpr uint64_t Hash() {{ return 0x{:016X}; }}\n" " static constexpr bool HasStageHash = true;\n" " template \n" " static constexpr uint64_t StageHash();\n" "}};\n\n"); constexpr auto StageObjectHeaderTemplate = FMT_STRING( "template\n" "class StageObject_{} : public hecl::StageBinary\n" "{{\n" " static const hecl::StageBinary Prototype;\n" "public:\n" " StageObject_{}(hecl::StageConverter& conv, hecl::FactoryCtx& ctx, const Shader_{}& in)\n" " : hecl::StageBinary(Prototype) {{}}\n" "}};\n" "STAGEOBJECT_PROTOTYPE_DECLARATIONS(StageObject_{})\n\n"); constexpr auto StageObjectImplTemplate = FMT_STRING( "template<>\n" "const hecl::StageBinary\n" "StageObject_{}::Prototype = \n" "{{{}_{}_{}_data, sizeof({}_{}_{}_data)}};\n\n"); struct CompileSubStageAction { template static bool Do(const std::string& name, const std::string& basename, const std::string& stage, std::stringstream& out) { fmt::print(out, StageObjectImplTemplate, P::Name, S::Name, name, P::Name, S::Name, basename, P::Name, S::Name, basename, P::Name, S::Name); return true; } }; struct CompileStageAction { template static bool Do(const std::string& name, const std::string& basename, const std::string& stage, std::stringstream& out) { std::pair data = CompileShader(stage); if (data.second == 0) return false; fmt::print(out, FMT_STRING("static const uint8_t {}_{}_{}_data[] = {{\n"), name, P::Name, S::Name); for (size_t i = 0; i < data.second;) { out << " "; for (int j = 0; j < 10 && i < data.second; ++i, ++j) fmt::print(out, FMT_STRING("0x{:02X}, "), data.first.get()[i]); out << "\n"; } out << "};\n\n"; fmt::print(out, StageObjectImplTemplate, P::Name, S::Name, name, P::Name, S::Name, name, P::Name, S::Name, name, P::Name, S::Name); return true; } }; template bool Compiler::StageAction(StageType type, const std::string& name, const std::string& basename, const std::string& stage, std::stringstream& implOut) { switch (type) { case StageType::Vertex: return Action::template Do(name, basename, stage, implOut); case StageType::Fragment: return Action::template Do(name, basename, stage, implOut); case StageType::Geometry: return Action::template Do(name, basename, stage, implOut); case StageType::Control: return Action::template Do(name, basename, stage, implOut); case StageType::Evaluation: return Action::template Do(name, basename, stage, implOut); default: break; } Log.report(logvisor::Error, FMT_STRING("Unknown stage type")); return false; } static const std::regex regWord(R"((\w+))", RegexFlags); template bool Compiler::StageAction(const std::string& platforms, StageType type, const std::string& name, const std::string& basename, const std::string& stage, std::stringstream& implOut) { std::smatch match; auto begin = platforms.cbegin(); auto end = platforms.cend(); while (std::regex_search(begin, end, match, regWord)) { std::string plat = match[1].str(); std::transform(plat.begin(), plat.end(), plat.begin(), ::tolower); if (plat == "glsl") { if (!StageAction(type, name, basename, stage, implOut) || !StageAction(type, name, basename, stage, implOut) #if HECL_NOUVEAU_NX || !StageAction(type, name, basename, stage, implOut) #endif ) return false; } else if (plat == "opengl") { if (!StageAction(type, name, basename, stage, implOut)) return false; } else if (plat == "vulkan") { if (!StageAction(type, name, basename, stage, implOut)) return false; } else if (plat == "nx") { #if HECL_NOUVEAU_NX if (!StageAction(type, name, basename, stage, implOut)) return false; #endif } else if (plat == "d3d11" || plat == "hlsl") { #if _WIN32 if (!StageAction(type, name, basename, stage, implOut)) return false; #endif } else if (plat == "metal") { #if __APPLE__ if (!StageAction(type, name, basename, stage, implOut)) return false; #endif } else { Log.report(logvisor::Error, FMT_STRING("Unknown platform '{}'"), plat); return false; } begin = match.suffix().first; } return true; } static const std::regex regInclude(R"(#\s*include\s+\"(.*)\")", RegexFlags); static const std::regex regDefine(R"(#\s*define\s+(\w+)\s*(.*))", RegexFlags); static const std::regex regShaderEx(R"(#\s*shader\s+(\w+)\s*:\s*(\w+))", RegexFlags); static const std::regex regShader(R"(#\s*shader\s+(\w+))", RegexFlags); static const std::regex regAttributeEx(R"(#\s*attribute\s+(\w+)\s+([0-9]+))", RegexFlags); static const std::regex regAttribute(R"(#\s*attribute\s+(\w+))", RegexFlags); static const std::regex regInstAttributeEx(R"(#\s*instattribute\s+(\w+)\s+([0-9]+))", RegexFlags); static const std::regex regInstAttribute(R"(#\s*instattribute\s+(\w+))", RegexFlags); static const std::regex regSrcFac(R"(#\s*srcfac\s+(\w+))", RegexFlags); static const std::regex regDstFac(R"(#\s*dstfac\s+(\w+))", RegexFlags); static const std::regex regPrim(R"(#\s*primitive\s+(\w+))", RegexFlags); static const std::regex regZTest(R"(#\s*depthtest\s+(\w+))", RegexFlags); static const std::regex regDepthWrite(R"(#\s*depthwrite\s+(\w+))", RegexFlags); static const std::regex regColorWrite(R"(#\s*colorwrite\s+(\w+))", RegexFlags); static const std::regex regAlphaWrite(R"(#\s*alphawrite\s+(\w+))", RegexFlags); static const std::regex regCulling(R"(#\s*culling\s+(\w+))", RegexFlags); static const std::regex regPatchSize(R"(#\s*patchsize\s+(\w+))", RegexFlags); static const std::regex regOverwriteAlpha(R"(#\s*overwritealpha\s+(\w+))", RegexFlags); static const std::regex regDepthAttachment(R"(#\s*depthattachment\s+(\w+))", RegexFlags); static const std::regex regVertex(R"(#\s*vertex\s+(.*))", RegexFlags); static const std::regex regFragment(R"(#\s*fragment\s+(.*))", RegexFlags); static const std::regex regGeometry(R"(#\s*geometry\s+(.*))", RegexFlags); static const std::regex regControl(R"(#\s*control\s+(.*))", RegexFlags); static const std::regex regEvaluation(R"(#\s*evaluation\s+(.*))", RegexFlags); bool Compiler::includeFile(SystemStringView file, std::string& out, int depth) { if (depth > 32) { Log.report(logvisor::Error, FMT_STRING(_SYS_STR("Too many levels of includes (>32) at '{}'")), file); return false; } const std::string* data = getFileContents(file); if (!data) { Log.report(logvisor::Error, FMT_STRING(_SYS_STR("Unable to access '{}'")), file); return false; } const std::string& sdata = *data; SystemString directory; auto slashPos = file.find_last_of(_SYS_STR("/\\")); if (slashPos != SystemString::npos) directory = SystemString(file.data(), slashPos); else directory = _SYS_STR("."); auto begin = sdata.cbegin(); auto end = sdata.cend(); while (begin != end) { std::string::const_iterator nextBegin; auto findPos = sdata.find('\n', begin - sdata.begin()); if (findPos == std::string::npos) nextBegin = end; else nextBegin = sdata.begin() + findPos + 1; std::smatch subMatch; if (std::regex_search(begin, nextBegin, subMatch, regInclude)) { std::string path = subMatch[1].str(); if (path.empty()) { Log.report(logvisor::Error, FMT_STRING(_SYS_STR("Empty path provided to include in '{}'")), file); return false; } hecl::SystemString pathStr(hecl::SystemStringConv(path).sys_str()); if (!hecl::IsAbsolute(pathStr)) pathStr = directory + _SYS_STR('/') + pathStr; if (!includeFile(pathStr, out, depth + 1)) return false; } else { out.insert(out.end(), begin, nextBegin); } begin = nextBegin; } return true; } constexpr std::string_view BlendFactorToStr(boo::BlendFactor fac) { switch (fac) { case boo::BlendFactor::Zero: default: return "boo::BlendFactor::Zero"sv; case boo::BlendFactor::One: return "boo::BlendFactor::One"sv; case boo::BlendFactor::SrcColor: return "boo::BlendFactor::SrcColor"sv; case boo::BlendFactor::InvSrcColor: return "boo::BlendFactor::InvSrcColor"sv; case boo::BlendFactor::DstColor: return "boo::BlendFactor::DstColor"sv; case boo::BlendFactor::InvDstColor: return "boo::BlendFactor::InvDstColor"sv; case boo::BlendFactor::SrcAlpha: return "boo::BlendFactor::SrcAlpha"sv; case boo::BlendFactor::InvSrcAlpha: return "boo::BlendFactor::InvSrcAlpha"sv; case boo::BlendFactor::DstAlpha: return "boo::BlendFactor::DstAlpha"sv; case boo::BlendFactor::InvDstAlpha: return "boo::BlendFactor::InvDstAlpha"sv; case boo::BlendFactor::SrcColor1: return "boo::BlendFactor::SrcColor1"sv; case boo::BlendFactor::InvSrcColor1: return "boo::BlendFactor::InvSrcColor1"sv; case boo::BlendFactor::Subtract: return "boo::BlendFactor::Subtract"sv; } } inline bool StrToBlendFactor(std::string str, boo::BlendFactor& fac) { std::transform(str.begin(), str.end(), str.begin(), ::tolower); if (str == "zero") fac = boo::BlendFactor::Zero; else if (str == "one") fac = boo::BlendFactor::One; else if (str == "srccolor") fac = boo::BlendFactor::SrcColor; else if (str == "invsrccolor") fac = boo::BlendFactor::InvSrcColor; else if (str == "dstcolor") fac = boo::BlendFactor::DstColor; else if (str == "invdstcolor") fac = boo::BlendFactor::InvDstColor; else if (str == "srcalpha") fac = boo::BlendFactor::SrcAlpha; else if (str == "invsrcalpha") fac = boo::BlendFactor::InvSrcAlpha; else if (str == "dstalpha") fac = boo::BlendFactor::DstAlpha; else if (str == "invdstalpha") fac = boo::BlendFactor::InvDstAlpha; else if (str == "srccolor1") fac = boo::BlendFactor::SrcColor1; else if (str == "invsrccolor1") fac = boo::BlendFactor::InvSrcColor1; else if (str == "subtract") fac = boo::BlendFactor::Subtract; else { Log.report(logvisor::Error, FMT_STRING("Unrecognized blend mode '{}'"), str); return false; } return true; } constexpr std::string_view PrimitiveToStr(boo::Primitive prim) { switch (prim) { case boo::Primitive::Triangles: default: return "boo::Primitive::Triangles"sv; case boo::Primitive::TriStrips: return "boo::Primitive::TriStrips"sv; case boo::Primitive::Patches: return "boo::Primitive::Patches"sv; } } inline bool StrToPrimitive(std::string str, boo::Primitive& prim) { std::transform(str.begin(), str.end(), str.begin(), ::tolower); if (str == "triangles") prim = boo::Primitive::Triangles; else if (str == "tristrips") prim = boo::Primitive::TriStrips; else if (str == "patches") prim = boo::Primitive::Patches; else { Log.report(logvisor::Error, FMT_STRING("Unrecognized primitive '{}'"), str); return false; } return true; } constexpr std::string_view ZTestToStr(boo::ZTest ztest) { switch (ztest) { case boo::ZTest::None: default: return "boo::ZTest::None"sv; case boo::ZTest::LEqual: return "boo::ZTest::LEqual"sv; case boo::ZTest::Greater: return "boo::ZTest::Greater"sv; case boo::ZTest::GEqual: return "boo::ZTest::GEqual"sv; case boo::ZTest::Equal: return "boo::ZTest::Equal"sv; } } inline bool StrToZTest(std::string str, boo::ZTest& ztest) { std::transform(str.begin(), str.end(), str.begin(), ::tolower); if (str == "none") ztest = boo::ZTest::None; else if (str == "lequal") ztest = boo::ZTest::LEqual; else if (str == "greater") ztest = boo::ZTest::Greater; else if (str == "gequal") ztest = boo::ZTest::GEqual; else if (str == "equal") ztest = boo::ZTest::Equal; else { Log.report(logvisor::Error, FMT_STRING("Unrecognized ztest '{}'"), str); return false; } return true; } constexpr std::string_view CullModeToStr(boo::CullMode cull) { switch (cull) { case boo::CullMode::None: default: return "boo::CullMode::None"sv; case boo::CullMode::Backface: return "boo::CullMode::Backface"sv; case boo::CullMode::Frontface: return "boo::CullMode::Frontface"sv; } } inline bool StrToCullMode(std::string str, boo::CullMode& cull) { std::transform(str.begin(), str.end(), str.begin(), ::tolower); if (str == "none") cull = boo::CullMode::None; else if (str == "backface") cull = boo::CullMode::Backface; else if (str == "frontface") cull = boo::CullMode::Frontface; else { Log.report(logvisor::Error, FMT_STRING("Unrecognized cull mode '{}'"), str); return false; } return true; } constexpr std::string_view BoolToStr(bool b) { return b ? "true"sv : "false"sv; } inline bool StrToBool(std::string str, bool& b) { std::transform(str.begin(), str.end(), str.begin(), ::tolower); if (strtol(str.c_str(), nullptr, 0)) b = true; else if (str == "true") b = true; else if (str == "false") b = false; else { Log.report(logvisor::Error, FMT_STRING("Unrecognized bool '{}'"), str); return false; } return true; } template constexpr void SemanticOut(std::stringstream& out, const std::pair& attr, const Format& fmtstr) { fmt::print(out, fmtstr, True(attr.first & boo::VertexSemantic::Instanced) ? " | boo::VertexSemantic::Instanced" : "", attr.second); } bool Compiler::compileFile(SystemStringView file, std::string_view baseName, std::pair& out) { std::string includesPass; if (!includeFile(file, includesPass)) return false; std::string shaderName(baseName); std::string shaderBase; std::vector> shaderAttributes; bool shaderAttributesReset = false; boo::AdditionalPipelineInfo shaderInfo = {}; std::string stagePlatforms; StageType stageType; auto stageBegin = includesPass.cend(); auto stageEnd = includesPass.cend(); struct ShaderStageUse { std::bitset<6> stages; std::array stageHashes = {}; std::set platforms; }; std::unordered_map shaderStageUses; std::unordered_map shaderBases; auto _DoCompile = [&]() { if (stageBegin == includesPass.end()) return true; if (shaderName.empty()) { Log.report(logvisor::Error, FMT_STRING("`#shader ` must be issued before stages")); return false; } std::string stage(stageBegin, stageEnd); for (const auto& define : m_defines) { std::string::size_type pos = 0; while ((pos = stage.find(define.first, pos)) != std::string::npos) { stage = std::string(stage.begin(), stage.begin() + pos) + define.second + std::string(stage.begin() + pos + define.first.size(), stage.end()); } } stageBegin = includesPass.end(); ShaderStageUse& uses = shaderStageUses[shaderName]; uses.stages.set(size_t(stageType)); uses.stageHashes[size_t(stageType)] = XXH64(stage.c_str(), stage.size(), 0); uses.platforms.insert(stagePlatforms); return StageAction(stagePlatforms, stageType, shaderName, shaderBase, stage, out.second); }; auto DoCompile = [&](std::string platform, StageType type, std::string::const_iterator end, std::string::const_iterator begin) { stageEnd = end; bool ret = _DoCompile(); stagePlatforms = std::move(platform); stageType = type; stageBegin = begin; return ret; }; auto DoShader = [&]() { if (shaderBase.empty() && shaderStageUses.find(shaderName) == shaderStageUses.cend()) return true; ShaderStageUse& uses = shaderStageUses[shaderName]; if (uses.stages.test(5)) return true; fmt::print(out.first, ShaderHeaderTemplate, shaderName, XXH64(shaderName.c_str(), shaderName.size(), 0)); fmt::print(out.first, StageObjectHeaderTemplate, shaderName, shaderName, shaderName, shaderName); if (!shaderBase.empty()) { shaderBases[shaderName] = shaderBase; for (int i = 0; i < 5; ++i) { if (uses.stages.test(size_t(i))) continue; std::string useBase = shaderBase; std::unordered_map::const_iterator baseUses = shaderStageUses.find(useBase); while (baseUses == shaderStageUses.cend() || !baseUses->second.stages.test(size_t(i))) { auto search = shaderBases.find(useBase); if (search == shaderBases.cend()) break; useBase = search->second; baseUses = shaderStageUses.find(useBase); } if (baseUses == shaderStageUses.cend() || !baseUses->second.stages.test(size_t(i))) continue; for (const std::string& basePlatforms : baseUses->second.platforms) { uses.stageHashes[size_t(i)] = baseUses->second.stageHashes[size_t(i)]; StageAction(basePlatforms, StageType(i), shaderName, useBase, {}, out.second); } } shaderBase.clear(); } fmt::print(out.second, FMT_STRING("static const boo::VertexElementDescriptor {}_vtxfmtelems[] = {{\n"), shaderName); for (const auto& attr : shaderAttributes) { switch (attr.first & boo::VertexSemantic::SemanticMask) { case boo::VertexSemantic::Position2: SemanticOut(out.second, attr, FMT_STRING("{{boo::VertexSemantic::Position2{}, {}}},\n")); break; case boo::VertexSemantic::Position3: SemanticOut(out.second, attr, FMT_STRING("{{boo::VertexSemantic::Position3{}, {}}},\n")); break; case boo::VertexSemantic::Position4: SemanticOut(out.second, attr, FMT_STRING("{{boo::VertexSemantic::Position4{}, {}}},\n")); break; case boo::VertexSemantic::Normal3: SemanticOut(out.second, attr, FMT_STRING("{{boo::VertexSemantic::Normal3{}, {}}},\n")); break; case boo::VertexSemantic::Normal4: SemanticOut(out.second, attr, FMT_STRING("{{boo::VertexSemantic::Normal4{}, {}}},\n")); break; case boo::VertexSemantic::Color: SemanticOut(out.second, attr, FMT_STRING("{{boo::VertexSemantic::Color{}, {}}},\n")); break; case boo::VertexSemantic::ColorUNorm: SemanticOut(out.second, attr, FMT_STRING("{{boo::VertexSemantic::ColorUNorm{}, {}}},\n")); break; case boo::VertexSemantic::UV2: SemanticOut(out.second, attr, FMT_STRING("{{boo::VertexSemantic::UV2{}, {}}},\n")); break; case boo::VertexSemantic::UV4: SemanticOut(out.second, attr, FMT_STRING("{{boo::VertexSemantic::UV4{}, {}}},\n")); break; case boo::VertexSemantic::Weight: SemanticOut(out.second, attr, FMT_STRING("{{boo::VertexSemantic::Weight{}, {}}},\n")); break; case boo::VertexSemantic::ModelView: SemanticOut(out.second, attr, FMT_STRING("{{boo::VertexSemantic::ModelView{}, {}}},\n")); break; default: SemanticOut(out.second, attr, FMT_STRING("{{boo::VertexSemantic::None{}, {}}},\n")); break; } } out.second << "};\n"; fmt::print(out.second, FMT_STRING("const boo::VertexFormatInfo Shader_{}::VtxFmt = {{ {}_vtxfmtelems }};\n\n"), shaderName, shaderName); fmt::print(out.second, FMT_STRING("const boo::AdditionalPipelineInfo Shader_{}::PipelineInfo = {{\n"), shaderName); out.second << BlendFactorToStr(shaderInfo.srcFac); out.second << ", "; out.second << BlendFactorToStr(shaderInfo.dstFac); out.second << ", "; out.second << PrimitiveToStr(shaderInfo.prim); out.second << ", "; out.second << ZTestToStr(shaderInfo.depthTest); out.second << ",\n"; out.second << BoolToStr(shaderInfo.depthWrite); out.second << ", "; out.second << BoolToStr(shaderInfo.colorWrite); out.second << ", "; out.second << BoolToStr(shaderInfo.alphaWrite); out.second << ", "; out.second << CullModeToStr(shaderInfo.culling); out.second << ", "; fmt::print(out.second, FMT_STRING("{}, "), shaderInfo.patchSize); out.second << BoolToStr(shaderInfo.overwriteAlpha); out.second << ", "; out.second << BoolToStr(shaderInfo.depthAttachment); out.second << ", "; out.second << "};\n\n"; uses.stages.set(5); return true; }; auto AddAttribute = [&](std::string semantic, std::string idx, bool inst) { if (shaderAttributesReset) { shaderAttributes.clear(); shaderAttributesReset = false; } boo::VertexSemantic orsem = inst ? boo::VertexSemantic::Instanced : boo::VertexSemantic::None; int idxNum = int(strtoul(idx.c_str(), nullptr, 10)); std::transform(semantic.begin(), semantic.end(), semantic.begin(), ::tolower); if (semantic == "position2") shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::Position2 | orsem, idxNum)); else if (semantic == "position3") shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::Position3 | orsem, idxNum)); else if (semantic == "position4") shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::Position4 | orsem, idxNum)); else if (semantic == "normal3") shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::Normal3 | orsem, idxNum)); else if (semantic == "normal4") shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::Normal4 | orsem, idxNum)); else if (semantic == "color") shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::Color | orsem, idxNum)); else if (semantic == "colorunorm") shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::ColorUNorm | orsem, idxNum)); else if (semantic == "uv2") shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::UV2 | orsem, idxNum)); else if (semantic == "uv4") shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::UV4 | orsem, idxNum)); else if (semantic == "weight") shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::Weight | orsem, idxNum)); else if (semantic == "modelview") shaderAttributes.push_back(std::make_pair(boo::VertexSemantic::ModelView | orsem, idxNum)); else { Log.report(logvisor::Error, FMT_STRING("Unrecognized vertex semantic '{}'"), semantic); return false; } return true; }; auto begin = includesPass.cbegin(); auto end = includesPass.cend(); std::string* defineContinue = nullptr; while (begin != end) { std::string::const_iterator nextBegin; auto findPos = includesPass.find('\n', begin - includesPass.cbegin()); if (findPos == std::string::npos) nextBegin = end; else nextBegin = includesPass.cbegin() + findPos + 1; std::smatch subMatch; if (defineContinue) { std::string extraLine; if (findPos == std::string::npos) extraLine = std::string(begin, end); else extraLine = std::string(begin, includesPass.cbegin() + findPos); *defineContinue += extraLine; if (!defineContinue->empty() && defineContinue->back() == '\r') defineContinue->pop_back(); if (!defineContinue->empty() && defineContinue->back() == '\\') defineContinue->pop_back(); else defineContinue = nullptr; } else if (std::regex_search(begin, nextBegin, subMatch, regDefine)) { std::string& defOut = m_defines[subMatch[1].str()]; defOut = subMatch[2].str(); if (!defOut.empty() && defOut.back() == '\r') defOut.pop_back(); if (!defOut.empty() && defOut.back() == '\\') { defOut.pop_back(); defineContinue = &defOut; } } else if (std::regex_search(begin, nextBegin, subMatch, regShaderEx)) { stageEnd = begin; if (!_DoCompile() || !DoShader()) return false; shaderName = subMatch[1].str(); shaderBase = subMatch[2].str(); shaderAttributesReset = true; // shaderAttributes.clear(); // shaderInfo = boo::AdditionalPipelineInfo(); } else if (std::regex_search(begin, nextBegin, subMatch, regShader)) { stageEnd = begin; if (!_DoCompile() || !DoShader()) return false; shaderName = subMatch[1].str(); shaderAttributesReset = true; // shaderAttributes.clear(); // shaderInfo = boo::AdditionalPipelineInfo(); } else if (std::regex_search(begin, nextBegin, subMatch, regAttributeEx)) { if (!AddAttribute(subMatch[1].str(), subMatch[2].str(), false)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regAttribute)) { if (!AddAttribute(subMatch[1].str(), "0", false)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regInstAttributeEx)) { if (!AddAttribute(subMatch[1].str(), subMatch[2].str(), true)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regInstAttribute)) { if (!AddAttribute(subMatch[1].str(), "0", true)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regSrcFac)) { if (!StrToBlendFactor(subMatch[1].str(), shaderInfo.srcFac)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regDstFac)) { if (!StrToBlendFactor(subMatch[1].str(), shaderInfo.dstFac)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regPrim)) { if (!StrToPrimitive(subMatch[1].str(), shaderInfo.prim)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regZTest)) { if (!StrToZTest(subMatch[1].str(), shaderInfo.depthTest)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regDepthWrite)) { if (!StrToBool(subMatch[1].str(), shaderInfo.depthWrite)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regColorWrite)) { if (!StrToBool(subMatch[1].str(), shaderInfo.colorWrite)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regAlphaWrite)) { if (!StrToBool(subMatch[1].str(), shaderInfo.alphaWrite)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regCulling)) { if (!StrToCullMode(subMatch[1].str(), shaderInfo.culling)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regPatchSize)) { auto str = subMatch[1].str(); char* endptr; shaderInfo.patchSize = uint32_t(strtoul(str.c_str(), &endptr, 0)); if (endptr == str.c_str()) { Log.report(logvisor::Error, FMT_STRING("Non-unsigned-integer value for #patchsize directive")); return false; } } else if (std::regex_search(begin, nextBegin, subMatch, regOverwriteAlpha)) { if (!StrToBool(subMatch[1].str(), shaderInfo.overwriteAlpha)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regDepthAttachment)) { if (!StrToBool(subMatch[1].str(), shaderInfo.depthAttachment)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regVertex)) { if (!DoCompile(subMatch[1].str(), StageType::Vertex, begin, nextBegin)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regFragment)) { if (!DoCompile(subMatch[1].str(), StageType::Fragment, begin, nextBegin)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regGeometry)) { if (!DoCompile(subMatch[1].str(), StageType::Geometry, begin, nextBegin)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regControl)) { if (!DoCompile(subMatch[1].str(), StageType::Control, begin, nextBegin)) return false; } else if (std::regex_search(begin, nextBegin, subMatch, regEvaluation)) { if (!DoCompile(subMatch[1].str(), StageType::Evaluation, begin, nextBegin)) return false; } begin = nextBegin; } stageEnd = begin; if (!_DoCompile() || !DoShader()) return false; for (const auto& shader : shaderStageUses) { for (int i = 0; i < 5; ++i) { if (shader.second.stageHashes[i]) { out.first << "template <> constexpr uint64_t Shader_"; out.first << shader.first; fmt::print(out.first, FMT_STRING("::StageHash<{}>() {{ return 0x{:016X}; }}\n"), StageNames[i], shader.second.stageHashes[i]); } } } out.first << "\n"; out.first << "#define UNIVERSAL_PIPELINES_"; out.first << baseName; for (const auto& shader : shaderStageUses) { out.first << " \\\n"; out.first << "::Shader_"; out.first << shader.first; } out.first << "\n"; out.first << "#define STAGES_"; out.first << baseName; out.first << "(P, S)"; for (const auto& shader : shaderStageUses) { out.first << " \\\n"; out.first << "::StageObject_"; out.first << shader.first; out.first << ","; } out.first << "\n"; return true; } bool Compiler::compile(std::string_view baseName, std::pair& out) { out.first << "#pragma once\n" "#include \"hecl/PipelineBase.hpp\"\n\n"; fmt::print(out.second, FMT_STRING("#include \"{}.hpp\"\n\n"), baseName); for (const auto& file : m_inputFiles) if (!compileFile(file, baseName, out)) return false; return true; } } // namespace hecl::shaderc