Close shader authoring usepass regressions
This commit is contained in:
@@ -19,6 +19,9 @@ bool IsBuiltinMeshPath(const Containers::String& path);
|
||||
bool IsBuiltinMaterialPath(const Containers::String& path);
|
||||
bool IsBuiltinShaderPath(const Containers::String& path);
|
||||
bool IsBuiltinTexturePath(const Containers::String& path);
|
||||
bool TryGetBuiltinShaderPathByShaderName(
|
||||
const Containers::String& shaderName,
|
||||
Containers::String& outPath);
|
||||
|
||||
const char* GetBuiltinPrimitiveDisplayName(BuiltinPrimitiveType primitiveType);
|
||||
Containers::String GetBuiltinPrimitiveMeshPath(BuiltinPrimitiveType primitiveType);
|
||||
@@ -28,6 +31,7 @@ Containers::String GetBuiltinUnlitShaderPath();
|
||||
Containers::String GetBuiltinDepthOnlyShaderPath();
|
||||
Containers::String GetBuiltinShadowCasterShaderPath();
|
||||
Containers::String GetBuiltinObjectIdShaderPath();
|
||||
Containers::String GetBuiltinObjectIdOutlineShaderPath();
|
||||
Containers::String GetBuiltinSkyboxShaderPath();
|
||||
Containers::String GetBuiltinColorScalePostProcessShaderPath();
|
||||
Containers::String GetBuiltinFinalColorShaderPath();
|
||||
|
||||
@@ -32,6 +32,7 @@ constexpr const char* kBuiltinUnlitShaderPath = "builtin://shaders/unlit";
|
||||
constexpr const char* kBuiltinDepthOnlyShaderPath = "builtin://shaders/depth-only";
|
||||
constexpr const char* kBuiltinShadowCasterShaderPath = "builtin://shaders/shadow-caster";
|
||||
constexpr const char* kBuiltinObjectIdShaderPath = "builtin://shaders/object-id";
|
||||
constexpr const char* kBuiltinObjectIdOutlineShaderPath = "builtin://shaders/object-id-outline";
|
||||
constexpr const char* kBuiltinSkyboxShaderPath = "builtin://shaders/skybox";
|
||||
constexpr const char* kBuiltinColorScalePostProcessShaderPath =
|
||||
"builtin://shaders/color-scale-post-process";
|
||||
@@ -46,22 +47,24 @@ struct MeshBuffers {
|
||||
|
||||
size_t CalculateBuiltinShaderMemorySize(const Shader& shader);
|
||||
|
||||
constexpr const char* kBuiltinForwardLitShaderManifestRelativePath =
|
||||
"engine/assets/builtin/shaders/forward-lit/forward-lit.shader";
|
||||
constexpr const char* kBuiltinUnlitShaderManifestRelativePath =
|
||||
"engine/assets/builtin/shaders/unlit/unlit.shader";
|
||||
constexpr const char* kBuiltinDepthOnlyShaderManifestRelativePath =
|
||||
"engine/assets/builtin/shaders/depth-only/depth-only.shader";
|
||||
constexpr const char* kBuiltinShadowCasterShaderManifestRelativePath =
|
||||
"engine/assets/builtin/shaders/shadow-caster/shadow-caster.shader";
|
||||
constexpr const char* kBuiltinObjectIdShaderManifestRelativePath =
|
||||
"engine/assets/builtin/shaders/object-id/object-id.shader";
|
||||
constexpr const char* kBuiltinSkyboxShaderManifestRelativePath =
|
||||
"engine/assets/builtin/shaders/skybox/skybox.shader";
|
||||
constexpr const char* kBuiltinColorScalePostProcessShaderManifestRelativePath =
|
||||
"engine/assets/builtin/shaders/color-scale-post-process/color-scale-post-process.shader";
|
||||
constexpr const char* kBuiltinFinalColorShaderManifestRelativePath =
|
||||
"engine/assets/builtin/shaders/final-color/final-color.shader";
|
||||
constexpr const char* kBuiltinForwardLitShaderAssetRelativePath =
|
||||
"engine/assets/builtin/shaders/forward-lit.shader";
|
||||
constexpr const char* kBuiltinUnlitShaderAssetRelativePath =
|
||||
"engine/assets/builtin/shaders/unlit.shader";
|
||||
constexpr const char* kBuiltinDepthOnlyShaderAssetRelativePath =
|
||||
"engine/assets/builtin/shaders/depth-only.shader";
|
||||
constexpr const char* kBuiltinShadowCasterShaderAssetRelativePath =
|
||||
"engine/assets/builtin/shaders/shadow-caster.shader";
|
||||
constexpr const char* kBuiltinObjectIdShaderAssetRelativePath =
|
||||
"engine/assets/builtin/shaders/object-id.shader";
|
||||
constexpr const char* kBuiltinObjectIdOutlineShaderAssetRelativePath =
|
||||
"engine/assets/builtin/shaders/object-id-outline.shader";
|
||||
constexpr const char* kBuiltinSkyboxShaderAssetRelativePath =
|
||||
"engine/assets/builtin/shaders/skybox.shader";
|
||||
constexpr const char* kBuiltinColorScalePostProcessShaderAssetRelativePath =
|
||||
"engine/assets/builtin/shaders/color-scale-post-process.shader";
|
||||
constexpr const char* kBuiltinFinalColorShaderAssetRelativePath =
|
||||
"engine/assets/builtin/shaders/final-color.shader";
|
||||
|
||||
Containers::String NormalizeBuiltinAssetPath(const std::filesystem::path& path) {
|
||||
return Containers::String(path.lexically_normal().generic_string().c_str());
|
||||
@@ -98,7 +101,7 @@ bool TryResolveBuiltinAssetPathFromAnchor(
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TryResolveBuiltinShaderManifestPath(
|
||||
bool TryResolveBuiltinShaderAssetPath(
|
||||
const std::filesystem::path& relativePath,
|
||||
Containers::String& outPath) {
|
||||
std::filesystem::path resolvedPath;
|
||||
@@ -124,51 +127,54 @@ bool TryResolveBuiltinShaderManifestPath(
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* GetBuiltinShaderManifestRelativePath(const Containers::String& builtinShaderPath) {
|
||||
const char* GetBuiltinShaderAssetRelativePath(const Containers::String& builtinShaderPath) {
|
||||
if (builtinShaderPath == Containers::String(kBuiltinForwardLitShaderPath)) {
|
||||
return kBuiltinForwardLitShaderManifestRelativePath;
|
||||
return kBuiltinForwardLitShaderAssetRelativePath;
|
||||
}
|
||||
if (builtinShaderPath == Containers::String(kBuiltinUnlitShaderPath)) {
|
||||
return kBuiltinUnlitShaderManifestRelativePath;
|
||||
return kBuiltinUnlitShaderAssetRelativePath;
|
||||
}
|
||||
if (builtinShaderPath == Containers::String(kBuiltinDepthOnlyShaderPath)) {
|
||||
return kBuiltinDepthOnlyShaderManifestRelativePath;
|
||||
return kBuiltinDepthOnlyShaderAssetRelativePath;
|
||||
}
|
||||
if (builtinShaderPath == Containers::String(kBuiltinShadowCasterShaderPath)) {
|
||||
return kBuiltinShadowCasterShaderManifestRelativePath;
|
||||
return kBuiltinShadowCasterShaderAssetRelativePath;
|
||||
}
|
||||
if (builtinShaderPath == Containers::String(kBuiltinObjectIdShaderPath)) {
|
||||
return kBuiltinObjectIdShaderManifestRelativePath;
|
||||
return kBuiltinObjectIdShaderAssetRelativePath;
|
||||
}
|
||||
if (builtinShaderPath == Containers::String(kBuiltinObjectIdOutlineShaderPath)) {
|
||||
return kBuiltinObjectIdOutlineShaderAssetRelativePath;
|
||||
}
|
||||
if (builtinShaderPath == Containers::String(kBuiltinSkyboxShaderPath)) {
|
||||
return kBuiltinSkyboxShaderManifestRelativePath;
|
||||
return kBuiltinSkyboxShaderAssetRelativePath;
|
||||
}
|
||||
if (builtinShaderPath == Containers::String(kBuiltinColorScalePostProcessShaderPath)) {
|
||||
return kBuiltinColorScalePostProcessShaderManifestRelativePath;
|
||||
return kBuiltinColorScalePostProcessShaderAssetRelativePath;
|
||||
}
|
||||
if (builtinShaderPath == Containers::String(kBuiltinFinalColorShaderPath)) {
|
||||
return kBuiltinFinalColorShaderManifestRelativePath;
|
||||
return kBuiltinFinalColorShaderAssetRelativePath;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool TryResolveBuiltinShaderManifestPath(
|
||||
bool TryResolveBuiltinShaderAssetPath(
|
||||
const Containers::String& builtinShaderPath,
|
||||
Containers::String& outPath) {
|
||||
const char* relativePath = GetBuiltinShaderManifestRelativePath(builtinShaderPath);
|
||||
const char* relativePath = GetBuiltinShaderAssetRelativePath(builtinShaderPath);
|
||||
if (relativePath == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryResolveBuiltinShaderManifestPath(std::filesystem::path(relativePath), outPath);
|
||||
return TryResolveBuiltinShaderAssetPath(std::filesystem::path(relativePath), outPath);
|
||||
}
|
||||
|
||||
Shader* LoadBuiltinShaderFromManifest(
|
||||
Shader* LoadBuiltinShaderFromAsset(
|
||||
const Containers::String& builtinPath,
|
||||
const Containers::String& manifestPath) {
|
||||
const Containers::String& assetPath) {
|
||||
ShaderLoader shaderLoader;
|
||||
LoadResult result = shaderLoader.Load(manifestPath);
|
||||
LoadResult result = shaderLoader.Load(assetPath);
|
||||
if (!result || result.resource == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -180,13 +186,13 @@ Shader* LoadBuiltinShaderFromManifest(
|
||||
return shader;
|
||||
}
|
||||
|
||||
Shader* TryLoadBuiltinShaderFromManifest(const Containers::String& builtinPath) {
|
||||
Containers::String manifestPath;
|
||||
if (!TryResolveBuiltinShaderManifestPath(builtinPath, manifestPath)) {
|
||||
Shader* TryLoadBuiltinShaderFromAsset(const Containers::String& builtinPath) {
|
||||
Containers::String assetPath;
|
||||
if (!TryResolveBuiltinShaderAssetPath(builtinPath, assetPath)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return LoadBuiltinShaderFromManifest(builtinPath, manifestPath);
|
||||
return LoadBuiltinShaderFromAsset(builtinPath, assetPath);
|
||||
}
|
||||
|
||||
Math::Bounds ComputeBounds(const std::vector<StaticMeshVertex>& vertices) {
|
||||
@@ -675,35 +681,39 @@ size_t CalculateBuiltinShaderMemorySize(const Shader& shader) {
|
||||
}
|
||||
|
||||
Shader* BuildBuiltinForwardLitShader(const Containers::String& path) {
|
||||
return TryLoadBuiltinShaderFromManifest(path);
|
||||
return TryLoadBuiltinShaderFromAsset(path);
|
||||
}
|
||||
|
||||
Shader* BuildBuiltinUnlitShader(const Containers::String& path) {
|
||||
return TryLoadBuiltinShaderFromManifest(path);
|
||||
return TryLoadBuiltinShaderFromAsset(path);
|
||||
}
|
||||
|
||||
Shader* BuildBuiltinDepthOnlyShader(const Containers::String& path) {
|
||||
return TryLoadBuiltinShaderFromManifest(path);
|
||||
return TryLoadBuiltinShaderFromAsset(path);
|
||||
}
|
||||
|
||||
Shader* BuildBuiltinShadowCasterShader(const Containers::String& path) {
|
||||
return TryLoadBuiltinShaderFromManifest(path);
|
||||
return TryLoadBuiltinShaderFromAsset(path);
|
||||
}
|
||||
|
||||
Shader* BuildBuiltinObjectIdShader(const Containers::String& path) {
|
||||
return TryLoadBuiltinShaderFromManifest(path);
|
||||
return TryLoadBuiltinShaderFromAsset(path);
|
||||
}
|
||||
|
||||
Shader* BuildBuiltinObjectIdOutlineShader(const Containers::String& path) {
|
||||
return TryLoadBuiltinShaderFromAsset(path);
|
||||
}
|
||||
|
||||
Shader* BuildBuiltinSkyboxShader(const Containers::String& path) {
|
||||
return TryLoadBuiltinShaderFromManifest(path);
|
||||
return TryLoadBuiltinShaderFromAsset(path);
|
||||
}
|
||||
|
||||
Shader* BuildBuiltinColorScalePostProcessShader(const Containers::String& path) {
|
||||
return TryLoadBuiltinShaderFromManifest(path);
|
||||
return TryLoadBuiltinShaderFromAsset(path);
|
||||
}
|
||||
|
||||
Shader* BuildBuiltinFinalColorShader(const Containers::String& path) {
|
||||
return TryLoadBuiltinShaderFromManifest(path);
|
||||
return TryLoadBuiltinShaderFromAsset(path);
|
||||
}
|
||||
|
||||
Material* BuildDefaultPrimitiveMaterial(const Containers::String& path) {
|
||||
@@ -776,6 +786,50 @@ bool IsBuiltinTexturePath(const Containers::String& path) {
|
||||
return path.StartsWith(kBuiltinTexturePrefix);
|
||||
}
|
||||
|
||||
bool TryGetBuiltinShaderPathByShaderName(
|
||||
const Containers::String& shaderName,
|
||||
Containers::String& outPath) {
|
||||
if (shaderName == "Builtin Forward Lit") {
|
||||
outPath = GetBuiltinForwardLitShaderPath();
|
||||
return true;
|
||||
}
|
||||
if (shaderName == "Builtin Unlit") {
|
||||
outPath = GetBuiltinUnlitShaderPath();
|
||||
return true;
|
||||
}
|
||||
if (shaderName == "Builtin Depth Only") {
|
||||
outPath = GetBuiltinDepthOnlyShaderPath();
|
||||
return true;
|
||||
}
|
||||
if (shaderName == "Builtin Shadow Caster") {
|
||||
outPath = GetBuiltinShadowCasterShaderPath();
|
||||
return true;
|
||||
}
|
||||
if (shaderName == "Builtin Object Id") {
|
||||
outPath = GetBuiltinObjectIdShaderPath();
|
||||
return true;
|
||||
}
|
||||
if (shaderName == "Builtin Object Id Outline") {
|
||||
outPath = GetBuiltinObjectIdOutlineShaderPath();
|
||||
return true;
|
||||
}
|
||||
if (shaderName == "Builtin Skybox") {
|
||||
outPath = GetBuiltinSkyboxShaderPath();
|
||||
return true;
|
||||
}
|
||||
if (shaderName == "Builtin Color Scale Post Process") {
|
||||
outPath = GetBuiltinColorScalePostProcessShaderPath();
|
||||
return true;
|
||||
}
|
||||
if (shaderName == "Builtin Final Color") {
|
||||
outPath = GetBuiltinFinalColorShaderPath();
|
||||
return true;
|
||||
}
|
||||
|
||||
outPath.Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* GetBuiltinPrimitiveDisplayName(BuiltinPrimitiveType primitiveType) {
|
||||
switch (primitiveType) {
|
||||
case BuiltinPrimitiveType::Cube: return "Cube";
|
||||
@@ -824,6 +878,10 @@ Containers::String GetBuiltinObjectIdShaderPath() {
|
||||
return Containers::String(kBuiltinObjectIdShaderPath);
|
||||
}
|
||||
|
||||
Containers::String GetBuiltinObjectIdOutlineShaderPath() {
|
||||
return Containers::String(kBuiltinObjectIdOutlineShaderPath);
|
||||
}
|
||||
|
||||
Containers::String GetBuiltinSkyboxShaderPath() {
|
||||
return Containers::String(kBuiltinSkyboxShaderPath);
|
||||
}
|
||||
@@ -934,6 +992,8 @@ LoadResult CreateBuiltinShaderResource(const Containers::String& path) {
|
||||
shader = BuildBuiltinShadowCasterShader(path);
|
||||
} else if (path == GetBuiltinObjectIdShaderPath()) {
|
||||
shader = BuildBuiltinObjectIdShader(path);
|
||||
} else if (path == GetBuiltinObjectIdOutlineShaderPath()) {
|
||||
shader = BuildBuiltinObjectIdOutlineShader(path);
|
||||
} else if (path == GetBuiltinSkyboxShaderPath()) {
|
||||
shader = BuildBuiltinSkyboxShader(path);
|
||||
} else if (path == GetBuiltinColorScalePostProcessShaderPath()) {
|
||||
|
||||
@@ -4,6 +4,47 @@ 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;
|
||||
@@ -146,6 +187,68 @@ bool TryParseAuthoringBlendFactor(const std::string& token, MaterialBlendFactor&
|
||||
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") {
|
||||
@@ -180,21 +283,7 @@ bool TryParseAuthoringColorMask(const std::string& token, Core::uint8& outMask)
|
||||
bool TryParseAuthoringBlendDirective(
|
||||
const std::vector<std::string>& tokens,
|
||||
MaterialRenderState& outState) {
|
||||
std::vector<std::string> normalizedTokens;
|
||||
normalizedTokens.reserve(tokens.size());
|
||||
for (const std::string& token : tokens) {
|
||||
if (token == ",") {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string normalizedToken = token;
|
||||
while (!normalizedToken.empty() && normalizedToken.back() == ',') {
|
||||
normalizedToken.pop_back();
|
||||
}
|
||||
if (!normalizedToken.empty()) {
|
||||
normalizedTokens.push_back(std::move(normalizedToken));
|
||||
}
|
||||
}
|
||||
const std::vector<std::string> normalizedTokens = NormalizeDirectiveTokens(tokens, 0u);
|
||||
|
||||
if (normalizedTokens.size() != 2u &&
|
||||
normalizedTokens.size() != 3u &&
|
||||
@@ -242,6 +331,169 @@ bool TryParseAuthoringBlendDirective(
|
||||
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,
|
||||
|
||||
@@ -1,19 +1,33 @@
|
||||
#include "ShaderAuthoringLoader.h"
|
||||
|
||||
#include "../ShaderAuthoringParser.h"
|
||||
#include "ShaderRuntimeBuildUtils.h"
|
||||
#include "../ShaderSourceUtils.h"
|
||||
#include "ShaderFileUtils.h"
|
||||
#include "ShaderRuntimeBuildUtils.h"
|
||||
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
bool CollectShaderAuthoringDependencyPaths(
|
||||
namespace {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
bool CollectShaderAuthoringDependencyPathsRecursive(
|
||||
const Containers::String& path,
|
||||
const std::string& sourceText,
|
||||
std::unordered_set<std::string>& seenShaderPaths,
|
||||
std::unordered_set<std::string>& seenDependencyPaths,
|
||||
Containers::Array<Containers::String>& outDependencies) {
|
||||
outDependencies.Clear();
|
||||
const fs::path normalizedShaderPath = fs::path(path.CStr()).lexically_normal();
|
||||
const std::string shaderKey = normalizedShaderPath.generic_string();
|
||||
if (!shaderKey.empty() && !seenShaderPaths.insert(shaderKey).second) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ShaderIR shaderIR = {};
|
||||
Containers::String parseError;
|
||||
@@ -21,19 +35,91 @@ bool CollectShaderAuthoringDependencyPaths(
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> seenPaths;
|
||||
CollectQuotedIncludeDependencyPaths(path, shaderIR.sharedProgramSource, seenPaths, outDependencies);
|
||||
CollectQuotedIncludeDependencyPaths(path, shaderIR.sharedProgramSource, seenDependencyPaths, outDependencies);
|
||||
for (const ShaderSubShaderIR& subShader : shaderIR.subShaders) {
|
||||
CollectQuotedIncludeDependencyPaths(path, subShader.sharedProgramSource, seenPaths, outDependencies);
|
||||
CollectQuotedIncludeDependencyPaths(path, subShader.sharedProgramSource, seenDependencyPaths, outDependencies);
|
||||
for (const ShaderPassIR& pass : subShader.passes) {
|
||||
CollectQuotedIncludeDependencyPaths(path, pass.sharedProgramSource, seenPaths, outDependencies);
|
||||
CollectQuotedIncludeDependencyPaths(path, pass.programSource, seenPaths, outDependencies);
|
||||
if (!pass.isUsePass) {
|
||||
CollectQuotedIncludeDependencyPaths(path, pass.sharedProgramSource, seenDependencyPaths, outDependencies);
|
||||
CollectQuotedIncludeDependencyPaths(path, pass.programSource, seenDependencyPaths, outDependencies);
|
||||
continue;
|
||||
}
|
||||
|
||||
Containers::String resolvedUsePassPath;
|
||||
if (!ResolveShaderUsePassPath(
|
||||
path,
|
||||
shaderIR.name,
|
||||
pass.usePassShaderName,
|
||||
resolvedUsePassPath)) {
|
||||
Containers::String builtinShaderPath;
|
||||
if (!TryGetBuiltinShaderPathByShaderName(pass.usePassShaderName, builtinShaderPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!builtinShaderPath.Empty() &&
|
||||
seenDependencyPaths.insert(ToStdString(builtinShaderPath)).second) {
|
||||
outDependencies.PushBack(builtinShaderPath);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (resolvedUsePassPath.Empty() || resolvedUsePassPath == path) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsBuiltinShaderPath(resolvedUsePassPath)) {
|
||||
if (seenDependencyPaths.insert(ToStdString(resolvedUsePassPath)).second) {
|
||||
outDependencies.PushBack(resolvedUsePassPath);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const fs::path normalizedDependencyPath =
|
||||
fs::path(resolvedUsePassPath.CStr()).lexically_normal();
|
||||
const Containers::String normalizedDependency =
|
||||
normalizedDependencyPath.generic_string().c_str();
|
||||
if (!normalizedDependency.Empty() &&
|
||||
seenDependencyPaths.insert(ToStdString(normalizedDependency)).second) {
|
||||
outDependencies.PushBack(normalizedDependency);
|
||||
}
|
||||
|
||||
Containers::String referencedSourceText;
|
||||
if (!ReadShaderTextFile(resolvedUsePassPath, referencedSourceText)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CollectShaderAuthoringDependencyPathsRecursive(
|
||||
resolvedUsePassPath,
|
||||
referencedSourceText.CStr(),
|
||||
seenShaderPaths,
|
||||
seenDependencyPaths,
|
||||
outDependencies)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool CollectShaderAuthoringDependencyPaths(
|
||||
const Containers::String& path,
|
||||
const std::string& sourceText,
|
||||
Containers::Array<Containers::String>& outDependencies) {
|
||||
outDependencies.Clear();
|
||||
|
||||
std::unordered_set<std::string> seenShaderPaths;
|
||||
std::unordered_set<std::string> seenDependencyPaths;
|
||||
return CollectShaderAuthoringDependencyPathsRecursive(
|
||||
path,
|
||||
sourceText,
|
||||
seenShaderPaths,
|
||||
seenDependencyPaths,
|
||||
outDependencies);
|
||||
}
|
||||
|
||||
LoadResult LoadShaderAuthoring(
|
||||
const Containers::String& path,
|
||||
const std::string& sourceText) {
|
||||
|
||||
@@ -17,7 +17,8 @@ bool ParseShaderAuthoring(
|
||||
Shader,
|
||||
Properties,
|
||||
SubShader,
|
||||
Pass
|
||||
Pass,
|
||||
Stencil
|
||||
};
|
||||
|
||||
auto fail = [&outError](const std::string& message, size_t lineNumber) -> bool {
|
||||
@@ -48,6 +49,7 @@ bool ParseShaderAuthoring(
|
||||
BlockKind pendingBlock = BlockKind::None;
|
||||
ShaderSubShaderIR* currentSubShader = nullptr;
|
||||
ShaderPassIR* currentPass = nullptr;
|
||||
MaterialRenderState* currentStencilRenderState = nullptr;
|
||||
bool inProgramBlock = false;
|
||||
bool inSharedIncludeBlock = false;
|
||||
|
||||
@@ -76,9 +78,193 @@ bool ParseShaderAuthoring(
|
||||
return true;
|
||||
};
|
||||
|
||||
auto tryParseFixedFunctionDirective =
|
||||
[&](const std::string& line, bool& outMatched, std::string& outErrorMessage) -> bool {
|
||||
outMatched = false;
|
||||
|
||||
MaterialRenderState* targetState = nullptr;
|
||||
bool* hasFixedFunctionState = nullptr;
|
||||
if (currentBlock() == BlockKind::Pass && currentPass != nullptr) {
|
||||
targetState = ¤tPass->fixedFunctionState;
|
||||
hasFixedFunctionState = ¤tPass->hasFixedFunctionState;
|
||||
} else if (currentBlock() == BlockKind::SubShader && currentSubShader != nullptr) {
|
||||
targetState = ¤tSubShader->fixedFunctionState;
|
||||
hasFixedFunctionState = ¤tSubShader->hasFixedFunctionState;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
|
||||
if (StartsWithKeyword(line, "Cull")) {
|
||||
outMatched = true;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens) || tokens.size() != 2u) {
|
||||
outErrorMessage = "Cull directive must use Front, Back, or Off";
|
||||
return false;
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(*hasFixedFunctionState, *targetState);
|
||||
if (!TryParseAuthoringCullMode(tokens[1], targetState->cullMode)) {
|
||||
outErrorMessage = "Cull directive must use Front, Back, or Off";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "ZWrite")) {
|
||||
outMatched = true;
|
||||
bool enabled = false;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens) ||
|
||||
tokens.size() != 2u ||
|
||||
!TryParseAuthoringBoolDirectiveToken(tokens[1], enabled)) {
|
||||
outErrorMessage = "ZWrite directive must use On or Off";
|
||||
return false;
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(*hasFixedFunctionState, *targetState);
|
||||
targetState->depthWriteEnable = enabled;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "ZTest")) {
|
||||
outMatched = true;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens) || tokens.size() != 2u) {
|
||||
outErrorMessage = "ZTest directive uses an unsupported compare function";
|
||||
return false;
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(*hasFixedFunctionState, *targetState);
|
||||
if (!TryParseAuthoringComparisonFunc(tokens[1], targetState->depthFunc)) {
|
||||
outErrorMessage = "ZTest directive uses an unsupported compare function";
|
||||
return false;
|
||||
}
|
||||
targetState->depthTestEnable = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "BlendOp")) {
|
||||
outMatched = true;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens)) {
|
||||
outErrorMessage = "BlendOp directive could not be tokenized";
|
||||
return false;
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(*hasFixedFunctionState, *targetState);
|
||||
if (!TryParseAuthoringBlendOpDirective(tokens, *targetState)) {
|
||||
outErrorMessage = "BlendOp directive uses an unsupported blend operation";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "Blend")) {
|
||||
outMatched = true;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens)) {
|
||||
outErrorMessage = "Blend directive could not be tokenized";
|
||||
return false;
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(*hasFixedFunctionState, *targetState);
|
||||
if (!TryParseAuthoringBlendDirective(tokens, *targetState)) {
|
||||
outErrorMessage = "Blend directive uses an unsupported factor combination";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "ColorMask")) {
|
||||
outMatched = true;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens) ||
|
||||
(tokens.size() != 2u && tokens.size() != 3u)) {
|
||||
outErrorMessage = "ColorMask directive uses an unsupported channel mask";
|
||||
return false;
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(*hasFixedFunctionState, *targetState);
|
||||
if (!TryParseAuthoringColorMask(tokens[1], targetState->colorWriteMask)) {
|
||||
outErrorMessage = "ColorMask directive uses an unsupported channel mask";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "Offset")) {
|
||||
outMatched = true;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens)) {
|
||||
outErrorMessage = "Offset directive could not be tokenized";
|
||||
return false;
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(*hasFixedFunctionState, *targetState);
|
||||
if (!TryParseAuthoringOffsetDirective(tokens, *targetState)) {
|
||||
outErrorMessage = "Offset directive must provide factor and units";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
auto tryParseStencilDirective =
|
||||
[&](const std::string& line, bool& outMatched, std::string& outErrorMessage) -> bool {
|
||||
outMatched = false;
|
||||
if (currentBlock() != BlockKind::Stencil || currentStencilRenderState == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens)) {
|
||||
outMatched = true;
|
||||
outErrorMessage = "Stencil directive could not be tokenized";
|
||||
return false;
|
||||
}
|
||||
|
||||
outMatched = true;
|
||||
if (!TryParseAuthoringStencilDirective(tokens, currentStencilRenderState->stencil)) {
|
||||
outErrorMessage = "Stencil block contains an unsupported directive";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
auto tryParseUsePassDirective =
|
||||
[&](const std::string& line, bool& outMatched, std::string& outErrorMessage) -> bool {
|
||||
outMatched = false;
|
||||
if (currentBlock() != BlockKind::SubShader || currentSubShader == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!StartsWithKeyword(line, "UsePass")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
outMatched = true;
|
||||
std::vector<std::string> tokens;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens) || tokens.size() != 2u) {
|
||||
outErrorMessage = "UsePass directive must reference \"ShaderName/PassName\"";
|
||||
return false;
|
||||
}
|
||||
|
||||
ShaderPassIR usePass = {};
|
||||
usePass.isUsePass = true;
|
||||
if (!TryParseAuthoringUsePassReference(
|
||||
tokens[1].c_str(),
|
||||
usePass.usePassShaderName,
|
||||
usePass.usePassPassName)) {
|
||||
outErrorMessage = "UsePass directive must reference \"ShaderName/PassName\"";
|
||||
return false;
|
||||
}
|
||||
|
||||
usePass.name = usePass.usePassPassName;
|
||||
currentSubShader->passes.push_back(std::move(usePass));
|
||||
return true;
|
||||
};
|
||||
|
||||
for (size_t lineIndex = 0; lineIndex < lines.size(); ++lineIndex) {
|
||||
const std::string& line = lines[lineIndex];
|
||||
const size_t humanLine = lineIndex + 1;
|
||||
const size_t humanLine = lineIndex + 1u;
|
||||
|
||||
if (inSharedIncludeBlock || inProgramBlock) {
|
||||
if (line == "ENDHLSL" || line == "ENDCG") {
|
||||
@@ -159,6 +345,24 @@ bool ParseShaderAuthoring(
|
||||
}
|
||||
blockStack.push_back(BlockKind::Pass);
|
||||
break;
|
||||
case BlockKind::Stencil:
|
||||
if (currentPass != nullptr) {
|
||||
EnsureAuthoringFixedFunctionStateInitialized(
|
||||
currentPass->hasFixedFunctionState,
|
||||
currentPass->fixedFunctionState);
|
||||
currentStencilRenderState = ¤tPass->fixedFunctionState;
|
||||
} else if (currentSubShader != nullptr) {
|
||||
EnsureAuthoringFixedFunctionStateInitialized(
|
||||
currentSubShader->hasFixedFunctionState,
|
||||
currentSubShader->fixedFunctionState);
|
||||
currentStencilRenderState = ¤tSubShader->fixedFunctionState;
|
||||
} else {
|
||||
return fail("Stencil block must be inside a SubShader or Pass", humanLine);
|
||||
}
|
||||
|
||||
currentStencilRenderState->stencil.enabled = true;
|
||||
blockStack.push_back(BlockKind::Stencil);
|
||||
break;
|
||||
case BlockKind::None:
|
||||
default:
|
||||
return fail("unexpected opening brace", humanLine);
|
||||
@@ -175,7 +379,9 @@ bool ParseShaderAuthoring(
|
||||
|
||||
const BlockKind closingBlock = blockStack.back();
|
||||
blockStack.pop_back();
|
||||
if (closingBlock == BlockKind::Pass) {
|
||||
if (closingBlock == BlockKind::Stencil) {
|
||||
currentStencilRenderState = nullptr;
|
||||
} else if (closingBlock == BlockKind::Pass) {
|
||||
currentPass = nullptr;
|
||||
} else if (closingBlock == BlockKind::SubShader) {
|
||||
currentSubShader = nullptr;
|
||||
@@ -212,7 +418,7 @@ bool ParseShaderAuthoring(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "Pass")) {
|
||||
if (line == "Pass") {
|
||||
pendingBlock = BlockKind::Pass;
|
||||
continue;
|
||||
}
|
||||
@@ -288,88 +494,41 @@ bool ParseShaderAuthoring(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (currentBlock() == BlockKind::SubShader && currentSubShader != nullptr) {
|
||||
if (StartsWithKeyword(line, "Cull")) {
|
||||
std::vector<std::string> tokens;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens) || tokens.size() != 2u) {
|
||||
return fail("Cull directive must use Front, Back, or Off", humanLine);
|
||||
}
|
||||
if ((currentBlock() == BlockKind::SubShader || currentBlock() == BlockKind::Pass) &&
|
||||
line == "Stencil") {
|
||||
pendingBlock = BlockKind::Stencil;
|
||||
continue;
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(
|
||||
currentSubShader->hasFixedFunctionState,
|
||||
currentSubShader->fixedFunctionState);
|
||||
if (!TryParseAuthoringCullMode(tokens[1], currentSubShader->fixedFunctionState.cullMode)) {
|
||||
return fail("Cull directive must use Front, Back, or Off", humanLine);
|
||||
}
|
||||
if (currentBlock() == BlockKind::Stencil) {
|
||||
bool matched = false;
|
||||
std::string errorMessage;
|
||||
if (!tryParseStencilDirective(line, matched, errorMessage)) {
|
||||
return fail(errorMessage, humanLine);
|
||||
}
|
||||
if (matched) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "ZWrite")) {
|
||||
std::vector<std::string> tokens;
|
||||
bool enabled = false;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens) ||
|
||||
tokens.size() != 2u ||
|
||||
!TryParseAuthoringBoolDirectiveToken(tokens[1], enabled)) {
|
||||
return fail("ZWrite directive must use On or Off", humanLine);
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(
|
||||
currentSubShader->hasFixedFunctionState,
|
||||
currentSubShader->fixedFunctionState);
|
||||
currentSubShader->fixedFunctionState.depthWriteEnable = enabled;
|
||||
if (currentBlock() == BlockKind::SubShader) {
|
||||
bool matched = false;
|
||||
std::string errorMessage;
|
||||
if (!tryParseUsePassDirective(line, matched, errorMessage)) {
|
||||
return fail(errorMessage, humanLine);
|
||||
}
|
||||
if (matched) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "ZTest")) {
|
||||
std::vector<std::string> tokens;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens) || tokens.size() != 2u) {
|
||||
return fail("ZTest directive uses an unsupported compare function", humanLine);
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(
|
||||
currentSubShader->hasFixedFunctionState,
|
||||
currentSubShader->fixedFunctionState);
|
||||
if (!TryParseAuthoringComparisonFunc(tokens[1], currentSubShader->fixedFunctionState.depthFunc)) {
|
||||
return fail("ZTest directive uses an unsupported compare function", humanLine);
|
||||
}
|
||||
currentSubShader->fixedFunctionState.depthTestEnable = true;
|
||||
continue;
|
||||
if (currentBlock() == BlockKind::SubShader || currentBlock() == BlockKind::Pass) {
|
||||
bool matched = false;
|
||||
std::string errorMessage;
|
||||
if (!tryParseFixedFunctionDirective(line, matched, errorMessage)) {
|
||||
return fail(errorMessage, humanLine);
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "Blend")) {
|
||||
std::vector<std::string> tokens;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens)) {
|
||||
return fail("Blend directive could not be tokenized", humanLine);
|
||||
}
|
||||
|
||||
for (size_t tokenIndex = 1; tokenIndex < tokens.size(); ++tokenIndex) {
|
||||
if (!tokens[tokenIndex].empty() && tokens[tokenIndex].back() == ',') {
|
||||
tokens[tokenIndex].pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(
|
||||
currentSubShader->hasFixedFunctionState,
|
||||
currentSubShader->fixedFunctionState);
|
||||
if (!TryParseAuthoringBlendDirective(tokens, currentSubShader->fixedFunctionState)) {
|
||||
return fail("Blend directive uses an unsupported factor combination", humanLine);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "ColorMask")) {
|
||||
std::vector<std::string> tokens;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens) ||
|
||||
(tokens.size() != 2u && tokens.size() != 3u)) {
|
||||
return fail("ColorMask directive uses an unsupported channel mask", humanLine);
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(
|
||||
currentSubShader->hasFixedFunctionState,
|
||||
currentSubShader->fixedFunctionState);
|
||||
if (!TryParseAuthoringColorMask(tokens[1], currentSubShader->fixedFunctionState.colorWriteMask)) {
|
||||
return fail("ColorMask directive uses an unsupported channel mask", humanLine);
|
||||
}
|
||||
if (matched) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -384,90 +543,6 @@ bool ParseShaderAuthoring(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "Cull")) {
|
||||
std::vector<std::string> tokens;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens) || tokens.size() != 2u) {
|
||||
return fail("Cull directive must use Front, Back, or Off", humanLine);
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(
|
||||
currentPass->hasFixedFunctionState,
|
||||
currentPass->fixedFunctionState);
|
||||
if (!TryParseAuthoringCullMode(tokens[1], currentPass->fixedFunctionState.cullMode)) {
|
||||
return fail("Cull directive must use Front, Back, or Off", humanLine);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "ZWrite")) {
|
||||
std::vector<std::string> tokens;
|
||||
bool enabled = false;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens) ||
|
||||
tokens.size() != 2u ||
|
||||
!TryParseAuthoringBoolDirectiveToken(tokens[1], enabled)) {
|
||||
return fail("ZWrite directive must use On or Off", humanLine);
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(
|
||||
currentPass->hasFixedFunctionState,
|
||||
currentPass->fixedFunctionState);
|
||||
currentPass->fixedFunctionState.depthWriteEnable = enabled;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "ZTest")) {
|
||||
std::vector<std::string> tokens;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens) || tokens.size() != 2u) {
|
||||
return fail("ZTest directive uses an unsupported compare function", humanLine);
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(
|
||||
currentPass->hasFixedFunctionState,
|
||||
currentPass->fixedFunctionState);
|
||||
if (!TryParseAuthoringComparisonFunc(tokens[1], currentPass->fixedFunctionState.depthFunc)) {
|
||||
return fail("ZTest directive uses an unsupported compare function", humanLine);
|
||||
}
|
||||
currentPass->fixedFunctionState.depthTestEnable = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "Blend")) {
|
||||
std::vector<std::string> tokens;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens)) {
|
||||
return fail("Blend directive could not be tokenized", humanLine);
|
||||
}
|
||||
|
||||
for (size_t tokenIndex = 1; tokenIndex < tokens.size(); ++tokenIndex) {
|
||||
if (!tokens[tokenIndex].empty() && tokens[tokenIndex].back() == ',') {
|
||||
tokens[tokenIndex].pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(
|
||||
currentPass->hasFixedFunctionState,
|
||||
currentPass->fixedFunctionState);
|
||||
if (!TryParseAuthoringBlendDirective(tokens, currentPass->fixedFunctionState)) {
|
||||
return fail("Blend directive uses an unsupported factor combination", humanLine);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (StartsWithKeyword(line, "ColorMask")) {
|
||||
std::vector<std::string> tokens;
|
||||
if (!TryTokenizeQuotedArguments(line, tokens) ||
|
||||
(tokens.size() != 2u && tokens.size() != 3u)) {
|
||||
return fail("ColorMask directive uses an unsupported channel mask", humanLine);
|
||||
}
|
||||
|
||||
EnsureAuthoringFixedFunctionStateInitialized(
|
||||
currentPass->hasFixedFunctionState,
|
||||
currentPass->fixedFunctionState);
|
||||
if (!TryParseAuthoringColorMask(tokens[1], currentPass->fixedFunctionState.colorWriteMask)) {
|
||||
return fail("ColorMask directive uses an unsupported channel mask", humanLine);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line == "HLSLPROGRAM" || line == "CGPROGRAM") {
|
||||
inProgramBlock = true;
|
||||
if (!consumeExtractedBlock(
|
||||
@@ -503,6 +578,13 @@ bool ParseShaderAuthoring(
|
||||
}
|
||||
|
||||
for (ShaderPassIR& pass : subShader.passes) {
|
||||
if (pass.isUsePass) {
|
||||
if (pass.usePassShaderName.Empty() || pass.usePassPassName.Empty()) {
|
||||
return fail("a UsePass directive is missing a valid target", 0);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pass.name.Empty()) {
|
||||
return fail("a Pass is missing a Name directive", 0);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,300 @@
|
||||
#include "ShaderRuntimeBuildUtils.h"
|
||||
|
||||
#include "../ShaderAuthoringParser.h"
|
||||
#include "ShaderFileUtils.h"
|
||||
#include "../ShaderSourceUtils.h"
|
||||
#include "ShaderAuthoringInternal.h"
|
||||
#include "ShaderFileUtils.h"
|
||||
|
||||
#include <XCEngine/Core/Asset/ResourceManager.h>
|
||||
#include <XCEngine/Resources/BuiltinResources.h>
|
||||
#include <XCEngine/Resources/Shader/ShaderLoader.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <system_error>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace Resources {
|
||||
|
||||
namespace {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
Containers::String ResolveBuiltinShaderPathByAuthoringName(const Containers::String& shaderName) {
|
||||
Containers::String builtinShaderPath;
|
||||
if (TryGetBuiltinShaderPathByShaderName(shaderName, builtinShaderPath)) {
|
||||
return builtinShaderPath;
|
||||
}
|
||||
|
||||
return Containers::String();
|
||||
}
|
||||
|
||||
void AddUniqueSearchRoot(
|
||||
const fs::path& candidate,
|
||||
std::vector<fs::path>& searchRoots,
|
||||
std::unordered_set<std::string>& seenRoots) {
|
||||
if (candidate.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fs::path normalized = candidate.lexically_normal();
|
||||
const std::string key = normalized.generic_string();
|
||||
if (key.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (seenRoots.insert(key).second) {
|
||||
searchRoots.push_back(normalized);
|
||||
}
|
||||
}
|
||||
|
||||
bool TryReadDeclaredAuthoringShaderName(
|
||||
const Containers::String& shaderPath,
|
||||
Containers::String& outShaderName) {
|
||||
outShaderName.Clear();
|
||||
|
||||
const Containers::Array<Core::uint8> data = ReadShaderFileData(shaderPath);
|
||||
if (data.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string sourceText = ToStdStringFromBytes(data);
|
||||
std::vector<std::string> lines;
|
||||
Internal::SplitShaderAuthoringLines(sourceText, lines);
|
||||
if (lines.empty() || !Internal::StartsWithKeyword(lines.front(), "Shader")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
if (!Internal::TryTokenizeQuotedArguments(lines.front(), tokens) || tokens.size() < 2u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outShaderName = tokens[1].c_str();
|
||||
return !outShaderName.Empty();
|
||||
}
|
||||
|
||||
bool TryResolveProjectShaderPathByAuthoringName(
|
||||
const Containers::String& currentShaderPath,
|
||||
const Containers::String& shaderName,
|
||||
Containers::String& outResolvedPath) {
|
||||
outResolvedPath.Clear();
|
||||
|
||||
fs::path currentPath(currentShaderPath.CStr());
|
||||
if (!currentPath.is_absolute()) {
|
||||
const Containers::String& resourceRoot = ResourceManager::Get().GetResourceRoot();
|
||||
if (!resourceRoot.Empty()) {
|
||||
currentPath = fs::path(resourceRoot.CStr()) / currentPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentPath.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<fs::path> searchRoots;
|
||||
std::unordered_set<std::string> seenRoots;
|
||||
|
||||
AddUniqueSearchRoot(currentPath.parent_path(), searchRoots, seenRoots);
|
||||
|
||||
for (fs::path ancestor = currentPath.parent_path();
|
||||
!ancestor.empty();
|
||||
ancestor = ancestor.parent_path()) {
|
||||
std::error_code ec;
|
||||
const fs::path assetsRoot = ancestor / "Assets";
|
||||
if (fs::exists(assetsRoot, ec) && fs::is_directory(assetsRoot, ec)) {
|
||||
AddUniqueSearchRoot(assetsRoot, searchRoots, seenRoots);
|
||||
}
|
||||
|
||||
const fs::path parent = ancestor.parent_path();
|
||||
if (parent == ancestor) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<fs::path> matches;
|
||||
std::unordered_set<std::string> seenMatches;
|
||||
|
||||
for (const fs::path& root : searchRoots) {
|
||||
std::error_code ec;
|
||||
if (!fs::exists(root, ec) || !fs::is_directory(root, ec)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (fs::recursive_directory_iterator it(root, ec), end; !ec && it != end; it.increment(ec)) {
|
||||
if (!it->is_regular_file()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fs::path candidatePath = it->path();
|
||||
if (candidatePath.extension() != ".shader") {
|
||||
continue;
|
||||
}
|
||||
|
||||
Containers::String declaredShaderName;
|
||||
if (!TryReadDeclaredAuthoringShaderName(
|
||||
Containers::String(candidatePath.generic_string().c_str()),
|
||||
declaredShaderName) ||
|
||||
declaredShaderName != shaderName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string matchKey = candidatePath.lexically_normal().generic_string();
|
||||
if (seenMatches.insert(matchKey).second) {
|
||||
matches.push_back(candidatePath.lexically_normal());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (matches.size() != 1u) {
|
||||
return false;
|
||||
}
|
||||
|
||||
outResolvedPath = matches.front().generic_string().c_str();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ImportConcretePass(Shader& shader, const ShaderPass& sourcePass) {
|
||||
if (ShaderPass* existingPass = shader.FindPass(sourcePass.name)) {
|
||||
*existingPass = sourcePass;
|
||||
return;
|
||||
}
|
||||
|
||||
shader.AddPass(sourcePass);
|
||||
}
|
||||
|
||||
ShaderPass BuildConcretePass(
|
||||
const ShaderIR& shaderIR,
|
||||
const ShaderSubShaderIR& subShader,
|
||||
const ShaderPassIR& pass) {
|
||||
ShaderPass shaderPass = {};
|
||||
shaderPass.name = pass.name;
|
||||
shaderPass.hasFixedFunctionState = pass.hasFixedFunctionState;
|
||||
shaderPass.fixedFunctionState = pass.fixedFunctionState;
|
||||
|
||||
for (const ShaderTagIR& subShaderTag : subShader.tags) {
|
||||
shaderPass.tags.PushBack({ subShaderTag.name, subShaderTag.value });
|
||||
}
|
||||
for (const ShaderTagIR& passTag : pass.tags) {
|
||||
shaderPass.tags.PushBack({ passTag.name, passTag.value });
|
||||
}
|
||||
for (const ShaderResourceBindingDesc& resourceBinding : pass.resources) {
|
||||
shaderPass.resources.PushBack(resourceBinding);
|
||||
}
|
||||
for (const ShaderKeywordDeclaration& keywordDeclaration : pass.keywordDeclarations) {
|
||||
shaderPass.keywordDeclarations.PushBack(keywordDeclaration);
|
||||
}
|
||||
|
||||
if (pass.programSource.Empty()) {
|
||||
return shaderPass;
|
||||
}
|
||||
|
||||
Containers::String combinedSource;
|
||||
AppendAuthoringSourceBlock(combinedSource, shaderIR.sharedProgramSource);
|
||||
AppendAuthoringSourceBlock(combinedSource, subShader.sharedProgramSource);
|
||||
AppendAuthoringSourceBlock(combinedSource, pass.sharedProgramSource);
|
||||
AppendAuthoringSourceBlock(combinedSource, pass.programSource);
|
||||
|
||||
const Containers::String strippedCombinedSource = StripShaderAuthoringPragmas(combinedSource);
|
||||
const std::vector<ShaderKeywordSet> keywordSets =
|
||||
BuildShaderKeywordVariantSets(pass.keywordDeclarations);
|
||||
|
||||
for (const ShaderKeywordSet& keywordSet : keywordSets) {
|
||||
const Containers::String variantSource =
|
||||
BuildKeywordVariantSource(strippedCombinedSource, keywordSet);
|
||||
|
||||
ShaderStageVariant vertexVariant = {};
|
||||
vertexVariant.stage = ShaderType::Vertex;
|
||||
vertexVariant.backend = ShaderBackend::Generic;
|
||||
vertexVariant.language = ShaderLanguage::HLSL;
|
||||
vertexVariant.requiredKeywords = keywordSet;
|
||||
vertexVariant.entryPoint =
|
||||
!pass.vertexEntryPoint.Empty()
|
||||
? pass.vertexEntryPoint
|
||||
: GetDefaultEntryPoint(ShaderLanguage::HLSL, ShaderType::Vertex);
|
||||
vertexVariant.profile = GetDefaultProfile(
|
||||
ShaderLanguage::HLSL,
|
||||
ShaderBackend::Generic,
|
||||
ShaderType::Vertex);
|
||||
vertexVariant.sourceCode = variantSource;
|
||||
shaderPass.variants.PushBack(vertexVariant);
|
||||
|
||||
ShaderStageVariant fragmentVariant = {};
|
||||
fragmentVariant.stage = ShaderType::Fragment;
|
||||
fragmentVariant.backend = ShaderBackend::Generic;
|
||||
fragmentVariant.language = ShaderLanguage::HLSL;
|
||||
fragmentVariant.requiredKeywords = keywordSet;
|
||||
fragmentVariant.entryPoint =
|
||||
!pass.fragmentEntryPoint.Empty()
|
||||
? pass.fragmentEntryPoint
|
||||
: GetDefaultEntryPoint(ShaderLanguage::HLSL, ShaderType::Fragment);
|
||||
fragmentVariant.profile = GetDefaultProfile(
|
||||
ShaderLanguage::HLSL,
|
||||
ShaderBackend::Generic,
|
||||
ShaderType::Fragment);
|
||||
fragmentVariant.sourceCode = variantSource;
|
||||
shaderPass.variants.PushBack(fragmentVariant);
|
||||
}
|
||||
|
||||
return shaderPass;
|
||||
}
|
||||
|
||||
bool TryResolveUsePass(
|
||||
const Containers::String& currentShaderPath,
|
||||
const Containers::String& currentShaderName,
|
||||
const Containers::String& referencedShaderName,
|
||||
const Containers::String& referencedPassName,
|
||||
const std::unordered_map<std::string, ShaderPass>& localConcretePasses,
|
||||
ShaderPass& outPass,
|
||||
Containers::String& outError) {
|
||||
if (referencedShaderName == currentShaderName) {
|
||||
const auto passIt = localConcretePasses.find(ToStdString(referencedPassName));
|
||||
if (passIt == localConcretePasses.end()) {
|
||||
outError =
|
||||
Containers::String("UsePass could not resolve local pass: ") + referencedPassName;
|
||||
return false;
|
||||
}
|
||||
|
||||
outPass = passIt->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
Containers::String referencedShaderPath;
|
||||
if (!ResolveShaderUsePassPath(
|
||||
currentShaderPath,
|
||||
currentShaderName,
|
||||
referencedShaderName,
|
||||
referencedShaderPath)) {
|
||||
outError =
|
||||
Containers::String("UsePass could not resolve referenced shader: ") +
|
||||
referencedShaderName;
|
||||
return false;
|
||||
}
|
||||
|
||||
ShaderLoader loader;
|
||||
LoadResult referencedShaderResult = loader.Load(referencedShaderPath);
|
||||
if (!referencedShaderResult || referencedShaderResult.resource == nullptr) {
|
||||
outError =
|
||||
Containers::String("UsePass failed to load referenced shader: ") + referencedShaderName;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<Shader> referencedShader(static_cast<Shader*>(referencedShaderResult.resource));
|
||||
const ShaderPass* referencedPass = referencedShader->FindPass(referencedPassName);
|
||||
if (referencedPass == nullptr) {
|
||||
outError =
|
||||
Containers::String("UsePass could not find referenced pass: ") + referencedPassName;
|
||||
return false;
|
||||
}
|
||||
|
||||
outPass = *referencedPass;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Containers::String GetDefaultEntryPoint(ShaderLanguage language, ShaderType stage) {
|
||||
if (language == ShaderLanguage::HLSL) {
|
||||
switch (stage) {
|
||||
@@ -98,6 +384,33 @@ size_t CalculateShaderMemorySize(const Shader& shader) {
|
||||
return memorySize;
|
||||
}
|
||||
|
||||
bool ResolveShaderUsePassPath(
|
||||
const Containers::String& currentShaderPath,
|
||||
const Containers::String& currentShaderName,
|
||||
const Containers::String& targetShaderName,
|
||||
Containers::String& outResolvedPath) {
|
||||
outResolvedPath.Clear();
|
||||
|
||||
if (targetShaderName.Empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targetShaderName == currentShaderName) {
|
||||
outResolvedPath = currentShaderPath;
|
||||
return true;
|
||||
}
|
||||
|
||||
outResolvedPath = ResolveBuiltinShaderPathByAuthoringName(targetShaderName);
|
||||
if (!outResolvedPath.Empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return TryResolveProjectShaderPathByAuthoringName(
|
||||
currentShaderPath,
|
||||
targetShaderName,
|
||||
outResolvedPath);
|
||||
}
|
||||
|
||||
LoadResult BuildShaderFromIR(
|
||||
const Containers::String& path,
|
||||
const ShaderIR& shaderIR) {
|
||||
@@ -113,76 +426,56 @@ LoadResult BuildShaderFromIR(
|
||||
shader->AddProperty(property);
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, ShaderPass> localConcretePasses;
|
||||
for (const ShaderSubShaderIR& subShader : shaderIR.subShaders) {
|
||||
for (const ShaderPassIR& pass : subShader.passes) {
|
||||
ShaderPass shaderPass = {};
|
||||
shaderPass.name = pass.name;
|
||||
shaderPass.hasFixedFunctionState = pass.hasFixedFunctionState;
|
||||
shaderPass.fixedFunctionState = pass.fixedFunctionState;
|
||||
shader->AddPass(shaderPass);
|
||||
|
||||
for (const ShaderTagIR& subShaderTag : subShader.tags) {
|
||||
shader->SetPassTag(pass.name, subShaderTag.name, subShaderTag.value);
|
||||
}
|
||||
for (const ShaderTagIR& passTag : pass.tags) {
|
||||
shader->SetPassTag(pass.name, passTag.name, passTag.value);
|
||||
if (pass.isUsePass) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const ShaderResourceBindingDesc& resourceBinding : pass.resources) {
|
||||
shader->AddPassResourceBinding(pass.name, resourceBinding);
|
||||
}
|
||||
for (const ShaderKeywordDeclaration& keywordDeclaration : pass.keywordDeclarations) {
|
||||
shader->AddPassKeywordDeclaration(pass.name, keywordDeclaration);
|
||||
ShaderPass concretePass = BuildConcretePass(shaderIR, subShader, pass);
|
||||
const std::string passKey = ToStdString(concretePass.name);
|
||||
if (passKey.empty()) {
|
||||
return LoadResult("Shader authoring produced a pass with an empty name");
|
||||
}
|
||||
|
||||
if (!pass.programSource.Empty()) {
|
||||
Containers::String combinedSource;
|
||||
AppendAuthoringSourceBlock(combinedSource, shaderIR.sharedProgramSource);
|
||||
AppendAuthoringSourceBlock(combinedSource, subShader.sharedProgramSource);
|
||||
AppendAuthoringSourceBlock(combinedSource, pass.sharedProgramSource);
|
||||
AppendAuthoringSourceBlock(combinedSource, pass.programSource);
|
||||
const Containers::String strippedCombinedSource =
|
||||
StripShaderAuthoringPragmas(combinedSource);
|
||||
const std::vector<ShaderKeywordSet> keywordSets =
|
||||
BuildShaderKeywordVariantSets(pass.keywordDeclarations);
|
||||
if (!localConcretePasses.emplace(passKey, std::move(concretePass)).second) {
|
||||
return LoadResult(
|
||||
Containers::String("Shader authoring produced duplicate pass name: ") + pass.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const ShaderKeywordSet& keywordSet : keywordSets) {
|
||||
const Containers::String variantSource =
|
||||
BuildKeywordVariantSource(strippedCombinedSource, keywordSet);
|
||||
|
||||
ShaderStageVariant vertexVariant = {};
|
||||
vertexVariant.stage = ShaderType::Vertex;
|
||||
vertexVariant.backend = ShaderBackend::Generic;
|
||||
vertexVariant.language = ShaderLanguage::HLSL;
|
||||
vertexVariant.requiredKeywords = keywordSet;
|
||||
vertexVariant.entryPoint =
|
||||
!pass.vertexEntryPoint.Empty()
|
||||
? pass.vertexEntryPoint
|
||||
: GetDefaultEntryPoint(ShaderLanguage::HLSL, ShaderType::Vertex);
|
||||
vertexVariant.profile = GetDefaultProfile(
|
||||
ShaderLanguage::HLSL,
|
||||
ShaderBackend::Generic,
|
||||
ShaderType::Vertex);
|
||||
vertexVariant.sourceCode = variantSource;
|
||||
shader->AddPassVariant(pass.name, vertexVariant);
|
||||
|
||||
ShaderStageVariant fragmentVariant = {};
|
||||
fragmentVariant.stage = ShaderType::Fragment;
|
||||
fragmentVariant.backend = ShaderBackend::Generic;
|
||||
fragmentVariant.language = ShaderLanguage::HLSL;
|
||||
fragmentVariant.requiredKeywords = keywordSet;
|
||||
fragmentVariant.entryPoint =
|
||||
!pass.fragmentEntryPoint.Empty()
|
||||
? pass.fragmentEntryPoint
|
||||
: GetDefaultEntryPoint(ShaderLanguage::HLSL, ShaderType::Fragment);
|
||||
fragmentVariant.profile = GetDefaultProfile(
|
||||
ShaderLanguage::HLSL,
|
||||
ShaderBackend::Generic,
|
||||
ShaderType::Fragment);
|
||||
fragmentVariant.sourceCode = variantSource;
|
||||
shader->AddPassVariant(pass.name, fragmentVariant);
|
||||
for (const ShaderSubShaderIR& subShader : shaderIR.subShaders) {
|
||||
for (const ShaderPassIR& pass : subShader.passes) {
|
||||
if (pass.isUsePass) {
|
||||
ShaderPass importedPass = {};
|
||||
Containers::String importError;
|
||||
if (!TryResolveUsePass(
|
||||
path,
|
||||
shaderIR.name,
|
||||
pass.usePassShaderName,
|
||||
pass.usePassPassName,
|
||||
localConcretePasses,
|
||||
importedPass,
|
||||
importError)) {
|
||||
return LoadResult(importError);
|
||||
}
|
||||
|
||||
ImportConcretePass(*shader, importedPass);
|
||||
for (const ShaderTagIR& subShaderTag : subShader.tags) {
|
||||
shader->SetPassTag(importedPass.name, subShaderTag.name, subShaderTag.value);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto passIt = localConcretePasses.find(ToStdString(pass.name));
|
||||
if (passIt == localConcretePasses.end()) {
|
||||
return LoadResult(
|
||||
Containers::String("Shader authoring lost concrete pass during build: ") + pass.name);
|
||||
}
|
||||
|
||||
ImportConcretePass(*shader, passIt->second);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,32 +483,5 @@ LoadResult BuildShaderFromIR(
|
||||
return LoadResult(shader.release());
|
||||
}
|
||||
|
||||
LoadResult LoadLegacySingleStageShader(
|
||||
const Containers::String& path,
|
||||
const std::string& sourceText) {
|
||||
auto shader = std::make_unique<Shader>();
|
||||
shader->m_path = path;
|
||||
shader->m_name = path;
|
||||
shader->m_guid = ResourceGUID::Generate(path);
|
||||
|
||||
const Containers::String ext = GetShaderPathExtension(path).ToLower();
|
||||
if (ext == "hlsl") {
|
||||
shader->SetShaderLanguage(ShaderLanguage::HLSL);
|
||||
} else {
|
||||
shader->SetShaderLanguage(ShaderLanguage::GLSL);
|
||||
}
|
||||
|
||||
shader->SetShaderType(DetectShaderTypeFromPath(path));
|
||||
shader->SetSourceCode(sourceText.c_str());
|
||||
shader->m_isValid = true;
|
||||
shader->m_memorySize =
|
||||
sizeof(Shader) +
|
||||
shader->m_name.Length() +
|
||||
shader->m_path.Length() +
|
||||
shader->GetSourceCode().Length();
|
||||
|
||||
return LoadResult(shader.release());
|
||||
}
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
|
||||
@@ -22,9 +22,11 @@ LoadResult BuildShaderFromIR(
|
||||
const Containers::String& path,
|
||||
const ShaderIR& shaderIR);
|
||||
|
||||
LoadResult LoadLegacySingleStageShader(
|
||||
const Containers::String& path,
|
||||
const std::string& sourceText);
|
||||
bool ResolveShaderUsePassPath(
|
||||
const Containers::String& currentShaderPath,
|
||||
const Containers::String& currentShaderName,
|
||||
const Containers::String& targetShaderName,
|
||||
Containers::String& outResolvedPath);
|
||||
|
||||
} // namespace Resources
|
||||
} // namespace XCEngine
|
||||
|
||||
@@ -16,6 +16,9 @@ struct ShaderTagIR {
|
||||
|
||||
struct ShaderPassIR {
|
||||
Containers::String name;
|
||||
bool isUsePass = false;
|
||||
Containers::String usePassShaderName;
|
||||
Containers::String usePassPassName;
|
||||
bool hasFixedFunctionState = false;
|
||||
MaterialRenderState fixedFunctionState = {};
|
||||
std::vector<ShaderTagIR> tags;
|
||||
|
||||
Reference in New Issue
Block a user