Files
XCEngine/engine/src/Resources/Shader/Internal/ShaderAuthoringDirectiveUtils.cpp

541 lines
17 KiB
C++

#include "ShaderAuthoringInternal.h"
namespace XCEngine {
namespace Resources {
namespace Internal {
namespace {
std::vector<std::string> NormalizeDirectiveTokens(
const std::vector<std::string>& tokens,
size_t startIndex) {
std::vector<std::string> normalizedTokens;
normalizedTokens.reserve(tokens.size() > startIndex ? tokens.size() - startIndex : 0u);
for (size_t tokenIndex = startIndex; tokenIndex < tokens.size(); ++tokenIndex) {
if (tokens[tokenIndex] == ",") {
continue;
}
std::string normalizedToken = tokens[tokenIndex];
while (!normalizedToken.empty() && normalizedToken.back() == ',') {
normalizedToken.pop_back();
}
if (!normalizedToken.empty()) {
normalizedTokens.push_back(std::move(normalizedToken));
}
}
return normalizedTokens;
}
bool TryParseUInt8Value(const std::string& token, Core::uint8& outValue) {
try {
const unsigned long value = std::stoul(token, nullptr, 0);
if (value > 0xFFul) {
return false;
}
outValue = static_cast<Core::uint8>(value);
return true;
} catch (...) {
return false;
}
}
} // namespace
MaterialRenderState BuildDefaultAuthoringFixedFunctionState() {
MaterialRenderState state = {};
state.blendEnable = false;
state.srcBlend = MaterialBlendFactor::One;
state.dstBlend = MaterialBlendFactor::Zero;
state.srcBlendAlpha = MaterialBlendFactor::One;
state.dstBlendAlpha = MaterialBlendFactor::Zero;
state.blendOp = MaterialBlendOp::Add;
state.blendOpAlpha = MaterialBlendOp::Add;
state.colorWriteMask = 0xF;
state.depthTestEnable = true;
state.depthWriteEnable = true;
state.depthFunc = MaterialComparisonFunc::LessEqual;
state.cullMode = MaterialCullMode::Back;
return state;
}
void EnsureAuthoringFixedFunctionStateInitialized(
bool& hasFixedFunctionState,
MaterialRenderState& fixedFunctionState) {
if (!hasFixedFunctionState) {
hasFixedFunctionState = true;
fixedFunctionState = BuildDefaultAuthoringFixedFunctionState();
}
}
bool TryParseAuthoringBoolDirectiveToken(const std::string& token, bool& outValue) {
const Containers::String normalized = Containers::String(token.c_str()).Trim().ToLower();
if (normalized == "on") {
outValue = true;
return true;
}
if (normalized == "off") {
outValue = false;
return true;
}
return false;
}
bool TryParseAuthoringCullMode(const std::string& token, MaterialCullMode& outMode) {
const Containers::String normalized = Containers::String(token.c_str()).Trim().ToLower();
if (normalized == "back") {
outMode = MaterialCullMode::Back;
return true;
}
if (normalized == "front") {
outMode = MaterialCullMode::Front;
return true;
}
if (normalized == "off") {
outMode = MaterialCullMode::None;
return true;
}
return false;
}
bool TryParseAuthoringComparisonFunc(const std::string& token, MaterialComparisonFunc& outFunc) {
const Containers::String normalized = Containers::String(token.c_str()).Trim().ToLower();
if (normalized == "never") {
outFunc = MaterialComparisonFunc::Never;
return true;
}
if (normalized == "less") {
outFunc = MaterialComparisonFunc::Less;
return true;
}
if (normalized == "equal") {
outFunc = MaterialComparisonFunc::Equal;
return true;
}
if (normalized == "lequal" || normalized == "lessequal" || normalized == "less_equal") {
outFunc = MaterialComparisonFunc::LessEqual;
return true;
}
if (normalized == "greater") {
outFunc = MaterialComparisonFunc::Greater;
return true;
}
if (normalized == "notequal" || normalized == "not_equal") {
outFunc = MaterialComparisonFunc::NotEqual;
return true;
}
if (normalized == "gequal" || normalized == "greaterequal" || normalized == "greater_equal") {
outFunc = MaterialComparisonFunc::GreaterEqual;
return true;
}
if (normalized == "always") {
outFunc = MaterialComparisonFunc::Always;
return true;
}
return false;
}
bool TryParseAuthoringBlendFactor(const std::string& token, MaterialBlendFactor& outFactor) {
const Containers::String normalized = Containers::String(token.c_str()).Trim().ToLower();
if (normalized == "zero") {
outFactor = MaterialBlendFactor::Zero;
return true;
}
if (normalized == "one") {
outFactor = MaterialBlendFactor::One;
return true;
}
if (normalized == "srccolor" || normalized == "src_color") {
outFactor = MaterialBlendFactor::SrcColor;
return true;
}
if (normalized == "oneminussrccolor" || normalized == "one_minus_src_color" || normalized == "invsrccolor") {
outFactor = MaterialBlendFactor::InvSrcColor;
return true;
}
if (normalized == "srcalpha" || normalized == "src_alpha") {
outFactor = MaterialBlendFactor::SrcAlpha;
return true;
}
if (normalized == "oneminussrcalpha" || normalized == "one_minus_src_alpha" || normalized == "invsrcalpha") {
outFactor = MaterialBlendFactor::InvSrcAlpha;
return true;
}
if (normalized == "dstalpha" || normalized == "dst_alpha") {
outFactor = MaterialBlendFactor::DstAlpha;
return true;
}
if (normalized == "oneminusdstalpha" || normalized == "one_minus_dst_alpha" || normalized == "invdstalpha") {
outFactor = MaterialBlendFactor::InvDstAlpha;
return true;
}
if (normalized == "dstcolor" || normalized == "dst_color") {
outFactor = MaterialBlendFactor::DstColor;
return true;
}
if (normalized == "oneminusdstcolor" || normalized == "one_minus_dst_color" || normalized == "invdstcolor") {
outFactor = MaterialBlendFactor::InvDstColor;
return true;
}
if (normalized == "srcalphasaturate" || normalized == "src_alpha_saturate" || normalized == "srcalphasat") {
outFactor = MaterialBlendFactor::SrcAlphaSat;
return true;
}
return false;
}
bool TryParseAuthoringBlendOp(const std::string& token, MaterialBlendOp& outOp) {
const Containers::String normalized = Containers::String(token.c_str()).Trim().ToLower();
if (normalized == "add") {
outOp = MaterialBlendOp::Add;
return true;
}
if (normalized == "sub" || normalized == "subtract") {
outOp = MaterialBlendOp::Subtract;
return true;
}
if (normalized == "revsub" || normalized == "reverse_subtract" || normalized == "reversesubtract") {
outOp = MaterialBlendOp::ReverseSubtract;
return true;
}
if (normalized == "min") {
outOp = MaterialBlendOp::Min;
return true;
}
if (normalized == "max") {
outOp = MaterialBlendOp::Max;
return true;
}
return false;
}
bool TryParseAuthoringStencilOp(const std::string& token, MaterialStencilOp& outOp) {
const Containers::String normalized = Containers::String(token.c_str()).Trim().ToLower();
if (normalized == "keep") {
outOp = MaterialStencilOp::Keep;
return true;
}
if (normalized == "zero") {
outOp = MaterialStencilOp::Zero;
return true;
}
if (normalized == "replace") {
outOp = MaterialStencilOp::Replace;
return true;
}
if (normalized == "incrsat" || normalized == "incr_sat") {
outOp = MaterialStencilOp::IncrSat;
return true;
}
if (normalized == "decrsat" || normalized == "decr_sat") {
outOp = MaterialStencilOp::DecrSat;
return true;
}
if (normalized == "invert") {
outOp = MaterialStencilOp::Invert;
return true;
}
if (normalized == "incrwrap" || normalized == "incr_wrap" || normalized == "incr") {
outOp = MaterialStencilOp::IncrWrap;
return true;
}
if (normalized == "decrwrap" || normalized == "decr_wrap" || normalized == "decr") {
outOp = MaterialStencilOp::DecrWrap;
return true;
}
return false;
}
bool TryParseAuthoringColorMask(const std::string& token, Core::uint8& outMask) {
const Containers::String normalized = Containers::String(token.c_str()).Trim().ToUpper();
if (normalized == "0") {
outMask = 0u;
return true;
}
Core::uint8 mask = 0u;
for (size_t index = 0; index < normalized.Length(); ++index) {
switch (normalized[index]) {
case 'R':
mask |= 0x1u;
break;
case 'G':
mask |= 0x2u;
break;
case 'B':
mask |= 0x4u;
break;
case 'A':
mask |= 0x8u;
break;
default:
return false;
}
}
outMask = mask;
return true;
}
bool TryParseAuthoringBlendDirective(
const std::vector<std::string>& tokens,
MaterialRenderState& outState) {
const std::vector<std::string> normalizedTokens = NormalizeDirectiveTokens(tokens, 0u);
if (normalizedTokens.size() != 2u &&
normalizedTokens.size() != 3u &&
normalizedTokens.size() != 5u) {
return false;
}
if (normalizedTokens.size() == 2u) {
bool enabled = false;
if (!TryParseAuthoringBoolDirectiveToken(normalizedTokens[1], enabled)) {
return false;
}
outState.blendEnable = enabled;
if (!enabled) {
outState.srcBlend = MaterialBlendFactor::One;
outState.dstBlend = MaterialBlendFactor::Zero;
outState.srcBlendAlpha = MaterialBlendFactor::One;
outState.dstBlendAlpha = MaterialBlendFactor::Zero;
}
return true;
}
MaterialBlendFactor srcBlend = MaterialBlendFactor::One;
MaterialBlendFactor dstBlend = MaterialBlendFactor::Zero;
if (!TryParseAuthoringBlendFactor(normalizedTokens[1], srcBlend) ||
!TryParseAuthoringBlendFactor(normalizedTokens[2], dstBlend)) {
return false;
}
outState.blendEnable = true;
outState.srcBlend = srcBlend;
outState.dstBlend = dstBlend;
if (normalizedTokens.size() == 5u) {
if (!TryParseAuthoringBlendFactor(normalizedTokens[3], outState.srcBlendAlpha) ||
!TryParseAuthoringBlendFactor(normalizedTokens[4], outState.dstBlendAlpha)) {
return false;
}
} else {
outState.srcBlendAlpha = srcBlend;
outState.dstBlendAlpha = dstBlend;
}
return true;
}
bool TryParseAuthoringBlendOpDirective(
const std::vector<std::string>& tokens,
MaterialRenderState& outState) {
const std::vector<std::string> normalizedTokens = NormalizeDirectiveTokens(tokens, 1u);
if (normalizedTokens.size() != 1u && normalizedTokens.size() != 2u) {
return false;
}
if (!TryParseAuthoringBlendOp(normalizedTokens[0], outState.blendOp)) {
return false;
}
if (normalizedTokens.size() == 2u) {
return TryParseAuthoringBlendOp(normalizedTokens[1], outState.blendOpAlpha);
}
outState.blendOpAlpha = outState.blendOp;
return true;
}
bool TryParseAuthoringOffsetDirective(
const std::vector<std::string>& tokens,
MaterialRenderState& outState) {
const std::vector<std::string> normalizedTokens = NormalizeDirectiveTokens(tokens, 1u);
if (normalizedTokens.size() != 2u) {
return false;
}
try {
outState.depthBiasFactor = std::stof(normalizedTokens[0]);
outState.depthBiasUnits = static_cast<Core::int32>(std::stol(normalizedTokens[1]));
return true;
} catch (...) {
return false;
}
}
bool TryParseAuthoringStencilDirective(
const std::vector<std::string>& tokens,
MaterialStencilState& outState) {
if (tokens.size() != 2u && tokens.size() != 3u) {
return false;
}
const Containers::String directive = Containers::String(tokens[0].c_str()).Trim().ToLower();
if (directive == "ref") {
Core::uint8 reference = 0;
if (!TryParseUInt8Value(tokens[1], reference)) {
return false;
}
outState.reference = reference;
outState.enabled = true;
return true;
}
if (directive == "readmask") {
Core::uint8 readMask = 0;
if (!TryParseUInt8Value(tokens[1], readMask)) {
return false;
}
outState.readMask = readMask;
outState.enabled = true;
return true;
}
if (directive == "writemask") {
Core::uint8 writeMask = 0;
if (!TryParseUInt8Value(tokens[1], writeMask)) {
return false;
}
outState.writeMask = writeMask;
outState.enabled = true;
return true;
}
auto applyComparison = [&](MaterialStencilFaceState& faceState) -> bool {
return TryParseAuthoringComparisonFunc(tokens[1], faceState.func);
};
auto applyOperation = [&](MaterialStencilFaceState& faceState, MaterialStencilOp MaterialStencilFaceState::* member) -> bool {
MaterialStencilOp op = MaterialStencilOp::Keep;
if (!TryParseAuthoringStencilOp(tokens[1], op)) {
return false;
}
faceState.*member = op;
return true;
};
if (directive == "comp") {
outState.enabled = true;
return applyComparison(outState.front) && applyComparison(outState.back);
}
if (directive == "pass") {
outState.enabled = true;
return applyOperation(outState.front, &MaterialStencilFaceState::passOp) &&
applyOperation(outState.back, &MaterialStencilFaceState::passOp);
}
if (directive == "fail") {
outState.enabled = true;
return applyOperation(outState.front, &MaterialStencilFaceState::failOp) &&
applyOperation(outState.back, &MaterialStencilFaceState::failOp);
}
if (directive == "zfail") {
outState.enabled = true;
return applyOperation(outState.front, &MaterialStencilFaceState::depthFailOp) &&
applyOperation(outState.back, &MaterialStencilFaceState::depthFailOp);
}
if (directive == "compfront") {
outState.enabled = true;
return applyComparison(outState.front);
}
if (directive == "passfront") {
outState.enabled = true;
return applyOperation(outState.front, &MaterialStencilFaceState::passOp);
}
if (directive == "failfront") {
outState.enabled = true;
return applyOperation(outState.front, &MaterialStencilFaceState::failOp);
}
if (directive == "zfailfront") {
outState.enabled = true;
return applyOperation(outState.front, &MaterialStencilFaceState::depthFailOp);
}
if (directive == "compback") {
outState.enabled = true;
return applyComparison(outState.back);
}
if (directive == "passback") {
outState.enabled = true;
return applyOperation(outState.back, &MaterialStencilFaceState::passOp);
}
if (directive == "failback") {
outState.enabled = true;
return applyOperation(outState.back, &MaterialStencilFaceState::failOp);
}
if (directive == "zfailback") {
outState.enabled = true;
return applyOperation(outState.back, &MaterialStencilFaceState::depthFailOp);
}
return false;
}
bool TryParseAuthoringUsePassReference(
const Containers::String& reference,
Containers::String& outShaderName,
Containers::String& outPassName) {
outShaderName.Clear();
outPassName.Clear();
const std::string referenceText = reference.Trim().CStr();
const size_t slashPos = referenceText.rfind('/');
if (slashPos == std::string::npos ||
slashPos == 0u ||
slashPos + 1u >= referenceText.size()) {
return false;
}
outShaderName = Containers::String(referenceText.substr(0, slashPos).c_str());
outPassName = Containers::String(referenceText.substr(slashPos + 1u).c_str());
return !outShaderName.Empty() && !outPassName.Empty();
}
void SetOrReplaceAuthoringTag(
std::vector<ShaderTagIR>& tags,
const Containers::String& name,
const Containers::String& value) {
for (ShaderTagIR& tag : tags) {
if (tag.name == name) {
tag.value = value;
return;
}
}
tags.push_back({ name, value });
}
bool TryParseShaderKeywordDeclarationPragma(
const std::vector<std::string>& pragmaTokens,
ShaderKeywordDeclaration& outDeclaration) {
outDeclaration = {};
if (pragmaTokens.size() < 3u) {
return false;
}
if (pragmaTokens[1] == "multi_compile") {
outDeclaration.type = ShaderKeywordDeclarationType::MultiCompile;
} else if (pragmaTokens[1] == "multi_compile_local") {
outDeclaration.type = ShaderKeywordDeclarationType::MultiCompileLocal;
} else if (pragmaTokens[1] == "shader_feature") {
outDeclaration.type = ShaderKeywordDeclarationType::ShaderFeature;
} else if (pragmaTokens[1] == "shader_feature_local") {
outDeclaration.type = ShaderKeywordDeclarationType::ShaderFeatureLocal;
} else {
return false;
}
for (size_t tokenIndex = 2; tokenIndex < pragmaTokens.size(); ++tokenIndex) {
outDeclaration.options.PushBack(pragmaTokens[tokenIndex].c_str());
}
return !outDeclaration.options.Empty();
}
} // namespace Internal
} // namespace Resources
} // namespace XCEngine