Add shader package include dependency pipeline

This commit is contained in:
2026-04-25 15:13:12 +08:00
parent 378965efca
commit d5eaa339f9
24 changed files with 1991 additions and 49 deletions

View File

@@ -71,11 +71,18 @@ void AppendAuthoringSourceBlock(
Containers::String& target,
const Containers::String& sourceBlock);
void CollectQuotedIncludeDependencyPaths(
bool CollectQuotedIncludeDependencyPaths(
const Containers::String& sourcePath,
const Containers::String& sourceText,
std::unordered_set<std::string>& seenPaths,
Containers::Array<Containers::String>& outDependencies);
Containers::Array<Containers::String>& outDependencies,
Containers::String* outError = nullptr);
bool ExpandShaderPackageIncludeSource(
const Containers::String& sourcePath,
const Containers::String& sourceText,
Containers::String& outSourceText,
Containers::String* outError = nullptr);
bool IsShaderAuthoringPragmaDirective(const std::string& line);

View File

@@ -2,6 +2,7 @@
#include "../ShaderAuthoringParser.h"
#include "../ShaderSourceUtils.h"
#include "ShaderAuthoringInternal.h"
#include "ShaderFileUtils.h"
#include "ShaderRuntimeBuildUtils.h"
@@ -22,7 +23,8 @@ bool CollectShaderAuthoringDependencyPathsRecursive(
const std::string& sourceText,
std::unordered_set<std::string>& seenShaderPaths,
std::unordered_set<std::string>& seenDependencyPaths,
Containers::Array<Containers::String>& outDependencies) {
Containers::Array<Containers::String>& outDependencies,
Containers::String* outError) {
const fs::path normalizedShaderPath = fs::path(path.CStr()).lexically_normal();
const std::string shaderKey = normalizedShaderPath.generic_string();
if (!shaderKey.empty() && !seenShaderPaths.insert(shaderKey).second) {
@@ -32,16 +34,45 @@ bool CollectShaderAuthoringDependencyPathsRecursive(
ShaderIR shaderIR = {};
Containers::String parseError;
if (!ParseShaderAuthoring(path, sourceText, shaderIR, &parseError)) {
if (outError != nullptr) {
*outError = parseError;
}
return false;
}
CollectQuotedIncludeDependencyPaths(path, shaderIR.sharedProgramSource, seenDependencyPaths, outDependencies);
if (!::XCEngine::Resources::Internal::CollectQuotedIncludeDependencyPaths(
path,
shaderIR.sharedProgramSource,
seenDependencyPaths,
outDependencies,
outError)) {
return false;
}
for (const ShaderSubShaderIR& subShader : shaderIR.subShaders) {
CollectQuotedIncludeDependencyPaths(path, subShader.sharedProgramSource, seenDependencyPaths, outDependencies);
if (!::XCEngine::Resources::Internal::CollectQuotedIncludeDependencyPaths(
path,
subShader.sharedProgramSource,
seenDependencyPaths,
outDependencies,
outError)) {
return false;
}
for (const ShaderPassIR& pass : subShader.passes) {
if (!pass.isUsePass) {
CollectQuotedIncludeDependencyPaths(path, pass.sharedProgramSource, seenDependencyPaths, outDependencies);
CollectQuotedIncludeDependencyPaths(path, pass.programSource, seenDependencyPaths, outDependencies);
if (!::XCEngine::Resources::Internal::CollectQuotedIncludeDependencyPaths(
path,
pass.sharedProgramSource,
seenDependencyPaths,
outDependencies,
outError) ||
!::XCEngine::Resources::Internal::CollectQuotedIncludeDependencyPaths(
path,
pass.programSource,
seenDependencyPaths,
outDependencies,
outError)) {
return false;
}
continue;
}
@@ -53,6 +84,13 @@ bool CollectShaderAuthoringDependencyPathsRecursive(
resolvedUsePassPath)) {
Containers::String builtinShaderPath;
if (!TryGetBuiltinShaderPathByShaderName(pass.usePassShaderName, builtinShaderPath)) {
if (outError != nullptr) {
*outError =
Containers::String("Failed to resolve shader UsePass: source=") +
path +
", requested=" +
pass.usePassShaderName;
}
return false;
}
@@ -70,6 +108,13 @@ bool CollectShaderAuthoringDependencyPathsRecursive(
if (IsBuiltinShaderPath(resolvedUsePassPath)) {
Containers::String builtinAssetPath;
if (!TryResolveBuiltinShaderAssetPath(resolvedUsePassPath, builtinAssetPath)) {
if (outError != nullptr) {
*outError =
Containers::String("Failed to resolve builtin shader UsePass asset: source=") +
path +
", requested=" +
resolvedUsePassPath;
}
return false;
}
@@ -84,6 +129,13 @@ bool CollectShaderAuthoringDependencyPathsRecursive(
Containers::String referencedSourceText;
if (!ReadShaderTextFile(builtinAssetPath, referencedSourceText)) {
if (outError != nullptr) {
*outError =
Containers::String("Failed to read shader UsePass source: source=") +
path +
", requested=" +
builtinAssetPath;
}
return false;
}
@@ -92,7 +144,8 @@ bool CollectShaderAuthoringDependencyPathsRecursive(
referencedSourceText.CStr(),
seenShaderPaths,
seenDependencyPaths,
outDependencies)) {
outDependencies,
outError)) {
return false;
}
continue;
@@ -109,6 +162,13 @@ bool CollectShaderAuthoringDependencyPathsRecursive(
Containers::String referencedSourceText;
if (!ReadShaderTextFile(resolvedUsePassPath, referencedSourceText)) {
if (outError != nullptr) {
*outError =
Containers::String("Failed to read shader UsePass source: source=") +
path +
", requested=" +
resolvedUsePassPath;
}
return false;
}
@@ -117,7 +177,8 @@ bool CollectShaderAuthoringDependencyPathsRecursive(
referencedSourceText.CStr(),
seenShaderPaths,
seenDependencyPaths,
outDependencies)) {
outDependencies,
outError)) {
return false;
}
}
@@ -132,7 +193,18 @@ bool CollectShaderAuthoringDependencyPaths(
const Containers::String& path,
const std::string& sourceText,
Containers::Array<Containers::String>& outDependencies) {
return CollectShaderAuthoringDependencyPaths(path, sourceText, outDependencies, nullptr);
}
bool CollectShaderAuthoringDependencyPaths(
const Containers::String& path,
const std::string& sourceText,
Containers::Array<Containers::String>& outDependencies,
Containers::String* outError) {
outDependencies.Clear();
if (outError != nullptr) {
outError->Clear();
}
std::unordered_set<std::string> seenShaderPaths;
std::unordered_set<std::string> seenDependencyPaths;
@@ -141,7 +213,8 @@ bool CollectShaderAuthoringDependencyPaths(
sourceText,
seenShaderPaths,
seenDependencyPaths,
outDependencies);
outDependencies,
outError);
}
LoadResult LoadShaderAuthoring(

View File

@@ -14,6 +14,12 @@ bool CollectShaderAuthoringDependencyPaths(
const std::string& sourceText,
Containers::Array<Containers::String>& outDependencies);
bool CollectShaderAuthoringDependencyPaths(
const Containers::String& path,
const std::string& sourceText,
Containers::Array<Containers::String>& outDependencies,
Containers::String* outError);
LoadResult LoadShaderAuthoring(
const Containers::String& path,
const std::string& sourceText);

View File

@@ -1,6 +1,7 @@
#include "ShaderAuthoringInternal.h"
#include "../ShaderSourceUtils.h"
#include "ShaderFileUtils.h"
#include <sstream>
#include <string>
@@ -29,6 +30,225 @@ Containers::String BuildShaderKeywordSetSignature(const ShaderKeywordSet& keywor
return signature;
}
void SetIncludeResolveError(
const Containers::String& sourcePath,
const Containers::String& includePath,
const Containers::String& includeChain,
Containers::String* outError) {
if (outError == nullptr) {
return;
}
*outError = BuildShaderDependencyResolutionDiagnostic(
includePath,
sourcePath,
includeChain);
}
Containers::String NormalizeIncludePathSeparators(const Containers::String& path) {
std::string normalized(path.CStr());
for (char& ch : normalized) {
if (ch == '\\') {
ch = '/';
}
}
return Containers::String(normalized.c_str());
}
bool IsShaderPackageIncludePath(const Containers::String& includePath) {
return NormalizeIncludePathSeparators(includePath).StartsWith("Packages/");
}
bool TryParseQuotedIncludePath(
const std::string& rawLine,
Containers::String& outIncludePath) {
const std::string line = TrimCopy(StripAuthoringLineComment(rawLine));
if (line.rfind("#include", 0) != 0) {
return false;
}
const size_t firstQuote = line.find('"');
if (firstQuote == std::string::npos) {
return false;
}
const size_t secondQuote = line.find('"', firstQuote + 1);
if (secondQuote == std::string::npos || secondQuote <= firstQuote + 1) {
return false;
}
outIncludePath = line.substr(firstQuote + 1, secondQuote - firstQuote - 1).c_str();
return true;
}
Containers::String BuildIncludeChainText(
const std::vector<Containers::String>& includeChain,
const Containers::String& requestedIncludePath) {
Containers::String text;
for (const Containers::String& chainEntry : includeChain) {
if (chainEntry.Empty()) {
continue;
}
if (!text.Empty()) {
text += " -> ";
}
text += chainEntry;
}
if (!requestedIncludePath.Empty()) {
if (!text.Empty()) {
text += " -> ";
}
text += requestedIncludePath;
}
return text;
}
bool ExpandShaderPackageIncludeSourceRecursive(
const Containers::String& sourcePath,
const Containers::String& sourceText,
std::unordered_set<std::string>& activeIncludePaths,
std::vector<Containers::String>& includeChain,
Containers::String& outSourceText,
Containers::String* outError) {
outSourceText.Clear();
std::istringstream stream(ToStdString(sourceText));
std::string rawLine;
while (std::getline(stream, rawLine)) {
Containers::String includePath;
if (!TryParseQuotedIncludePath(rawLine, includePath) ||
!IsShaderPackageIncludePath(includePath)) {
outSourceText += rawLine.c_str();
outSourceText += '\n';
continue;
}
const Containers::String resolvedPath = ResolveShaderDependencyPath(includePath, sourcePath);
if (resolvedPath.Empty()) {
SetIncludeResolveError(
sourcePath,
includePath,
BuildIncludeChainText(includeChain, includePath),
outError);
return false;
}
const std::string activePathKey = ToStdString(resolvedPath);
if (!activeIncludePaths.insert(activePathKey).second) {
if (outError != nullptr) {
*outError =
Containers::String("Cyclic shader package include: requested=") +
includePath +
", source=" +
sourcePath +
", resolved=" +
resolvedPath +
", includeChain=" +
BuildIncludeChainText(includeChain, includePath);
}
return false;
}
Containers::String includedSourceText;
if (!ReadShaderTextFile(resolvedPath, includedSourceText)) {
activeIncludePaths.erase(activePathKey);
SetIncludeResolveError(
sourcePath,
includePath,
BuildIncludeChainText(includeChain, includePath),
outError);
return false;
}
Containers::String expandedIncludedSource;
includeChain.push_back(resolvedPath);
if (!ExpandShaderPackageIncludeSourceRecursive(
resolvedPath,
includedSourceText,
activeIncludePaths,
includeChain,
expandedIncludedSource,
outError)) {
includeChain.pop_back();
activeIncludePaths.erase(activePathKey);
return false;
}
includeChain.pop_back();
activeIncludePaths.erase(activePathKey);
outSourceText += expandedIncludedSource;
if (!expandedIncludedSource.Empty() &&
expandedIncludedSource[expandedIncludedSource.Length() - 1] != '\n') {
outSourceText += '\n';
}
}
return true;
}
bool CollectQuotedIncludeDependencyPathsRecursive(
const Containers::String& sourcePath,
const Containers::String& sourceText,
std::unordered_set<std::string>& seenPaths,
std::vector<Containers::String>& includeChain,
Containers::Array<Containers::String>& outDependencies,
Containers::String* outError) {
std::istringstream stream(ToStdString(sourceText));
std::string rawLine;
while (std::getline(stream, rawLine)) {
Containers::String includePath;
if (!TryParseQuotedIncludePath(rawLine, includePath)) {
continue;
}
const Containers::String resolvedPath = ResolveShaderDependencyPath(includePath, sourcePath);
if (resolvedPath.Empty()) {
SetIncludeResolveError(
sourcePath,
includePath,
BuildIncludeChainText(includeChain, includePath),
outError);
return false;
}
const std::string key = ToStdString(resolvedPath);
if (key.empty() || !seenPaths.insert(key).second) {
continue;
}
outDependencies.PushBack(resolvedPath);
Containers::String includedSourceText;
if (!ReadShaderTextFile(resolvedPath, includedSourceText)) {
SetIncludeResolveError(
sourcePath,
includePath,
BuildIncludeChainText(includeChain, includePath),
outError);
return false;
}
includeChain.push_back(resolvedPath);
if (!CollectQuotedIncludeDependencyPathsRecursive(
resolvedPath,
includedSourceText,
seenPaths,
includeChain,
outDependencies,
outError)) {
includeChain.pop_back();
return false;
}
includeChain.pop_back();
}
return true;
}
} // namespace
void AppendAuthoringSourceBlock(
@@ -44,36 +264,42 @@ void AppendAuthoringSourceBlock(
target += sourceBlock;
}
void CollectQuotedIncludeDependencyPaths(
bool CollectQuotedIncludeDependencyPaths(
const Containers::String& sourcePath,
const Containers::String& sourceText,
std::unordered_set<std::string>& seenPaths,
Containers::Array<Containers::String>& outDependencies) {
std::istringstream stream(ToStdString(sourceText));
std::string rawLine;
while (std::getline(stream, rawLine)) {
const std::string line = TrimCopy(StripAuthoringLineComment(rawLine));
if (line.rfind("#include", 0) != 0) {
continue;
}
Containers::Array<Containers::String>& outDependencies,
Containers::String* outError) {
std::vector<Containers::String> includeChain;
includeChain.push_back(sourcePath);
return CollectQuotedIncludeDependencyPathsRecursive(
sourcePath,
sourceText,
seenPaths,
includeChain,
outDependencies,
outError);
}
const size_t firstQuote = line.find('"');
if (firstQuote == std::string::npos) {
continue;
}
const size_t secondQuote = line.find('"', firstQuote + 1);
if (secondQuote == std::string::npos || secondQuote <= firstQuote + 1) {
continue;
}
const Containers::String includePath(line.substr(firstQuote + 1, secondQuote - firstQuote - 1).c_str());
const Containers::String resolvedPath = ResolveShaderDependencyPath(includePath, sourcePath);
const std::string key = ToStdString(resolvedPath);
if (!key.empty() && seenPaths.insert(key).second) {
outDependencies.PushBack(resolvedPath);
}
bool ExpandShaderPackageIncludeSource(
const Containers::String& sourcePath,
const Containers::String& sourceText,
Containers::String& outSourceText,
Containers::String* outError) {
if (outError != nullptr) {
outError->Clear();
}
std::unordered_set<std::string> activeIncludePaths;
std::vector<Containers::String> includeChain;
includeChain.push_back(sourcePath);
return ExpandShaderPackageIncludeSourceRecursive(
sourcePath,
sourceText,
activeIncludePaths,
includeChain,
outSourceText,
outError);
}
bool IsShaderAuthoringPragmaDirective(const std::string& line) {

View File

@@ -395,10 +395,15 @@ void AppendScannedAuthoringResourceBindings(
}
}
ShaderPass BuildConcretePass(
bool BuildConcretePass(
const Containers::String& path,
const ShaderIR& shaderIR,
const ShaderSubShaderIR& subShader,
const ShaderPassIR& pass) {
const ShaderPassIR& pass,
ShaderPass& outShaderPass,
Containers::String& outError) {
outError.Clear();
ShaderPass shaderPass = {};
shaderPass.name = pass.name;
shaderPass.hasFixedFunctionState = pass.hasFixedFunctionState;
@@ -426,7 +431,8 @@ ShaderPass BuildConcretePass(
}
}
return shaderPass;
outShaderPass = std::move(shaderPass);
return true;
}
Containers::String combinedSource;
@@ -435,7 +441,16 @@ ShaderPass BuildConcretePass(
AppendAuthoringSourceBlock(combinedSource, pass.sharedProgramSource);
AppendAuthoringSourceBlock(combinedSource, pass.programSource);
const Containers::String strippedCombinedSource = StripShaderAuthoringPragmas(combinedSource);
Containers::String expandedCombinedSource;
if (!Internal::ExpandShaderPackageIncludeSource(
path,
combinedSource,
expandedCombinedSource,
&outError)) {
return false;
}
const Containers::String strippedCombinedSource = StripShaderAuthoringPragmas(expandedCombinedSource);
if (shaderPass.resources.Empty()) {
Containers::Array<ShaderResourceBindingDesc> defaultBindings;
if (::XCEngine::Rendering::TryBuildBuiltinPassDefaultResourceBindings(shaderPass, defaultBindings)) {
@@ -504,7 +519,8 @@ ShaderPass BuildConcretePass(
shaderPass.variants.PushBack(fragmentVariant);
}
return shaderPass;
outShaderPass = std::move(shaderPass);
return true;
}
bool TryLoadReferencedUsePassShader(
@@ -764,7 +780,17 @@ LoadResult BuildShaderFromIRInternal(
continue;
}
ShaderPass concretePass = BuildConcretePass(shaderIR, subShader, pass);
ShaderPass concretePass = {};
Containers::String concretePassError;
if (!BuildConcretePass(
path,
shaderIR,
subShader,
pass,
concretePass,
concretePassError)) {
return LoadResult(concretePassError);
}
const std::string passKey = ToStdString(concretePass.name);
if (passKey.empty()) {
return LoadResult("Shader authoring produced a pass with an empty name");

View File

@@ -70,7 +70,17 @@ ImportSettings* ShaderLoader::GetDefaultSettings() const {
bool ShaderLoader::CollectSourceDependencies(
const Containers::String& path,
Containers::Array<Containers::String>& outDependencies) const {
return CollectSourceDependencies(path, outDependencies, nullptr);
}
bool ShaderLoader::CollectSourceDependencies(
const Containers::String& path,
Containers::Array<Containers::String>& outDependencies,
Containers::String* outError) const {
outDependencies.Clear();
if (outError != nullptr) {
outError->Clear();
}
if (IsBuiltinShaderPath(path)) {
return true;
@@ -83,15 +93,23 @@ bool ShaderLoader::CollectSourceDependencies(
const Containers::Array<Core::uint8> data = ReadShaderFileData(path);
if (data.Empty()) {
if (outError != nullptr) {
*outError = Containers::String("Failed to read shader file: ") + path;
}
return false;
}
const std::string sourceText = ToStdStringFromBytes(data);
if (!LooksLikeShaderAuthoring(sourceText)) {
if (outError != nullptr) {
*outError =
Containers::String("Shader authoring file must start with a Shader declaration: ") +
path;
}
return false;
}
return CollectShaderAuthoringDependencyPaths(path, sourceText, outDependencies);
return CollectShaderAuthoringDependencyPaths(path, sourceText, outDependencies, outError);
}
ShaderType ShaderLoader::DetectShaderType(const Containers::String& path, const Containers::String& source) {

View File

@@ -14,10 +14,66 @@ std::string ToStdString(const Containers::String& value) {
namespace {
constexpr const char* kBuiltinShaderPackagePrefix = "Packages/";
constexpr const char* kBuiltinShaderPackageRootRelativePath =
"engine/assets/builtin/shaders/Packages";
Containers::String NormalizeDependencyPathSeparators(const Containers::String& path) {
std::string normalized(path.CStr());
for (char& ch : normalized) {
if (ch == '\\') {
ch = '/';
}
}
return Containers::String(normalized.c_str());
}
Containers::String NormalizePathString(const std::filesystem::path& path) {
return Containers::String(path.lexically_normal().generic_string().c_str());
}
bool TryResolvePathFromAnchor(
const std::filesystem::path& anchor,
const std::filesystem::path& relativePath,
std::filesystem::path& outPath) {
if (anchor.empty()) {
return false;
}
std::error_code ec;
std::filesystem::path current = anchor.lexically_normal();
if (std::filesystem::is_regular_file(current, ec)) {
current = current.parent_path();
}
while (!current.empty()) {
const std::filesystem::path candidate = (current / relativePath).lexically_normal();
if (std::filesystem::exists(candidate, ec)) {
outPath = candidate;
return true;
}
const std::filesystem::path parent = current.parent_path();
if (parent == current) {
break;
}
current = parent;
}
return false;
}
bool ContainsParentTraversal(const std::filesystem::path& path) {
for (const std::filesystem::path& part : path) {
if (part == "..") {
return true;
}
}
return false;
}
} // namespace
size_t SkipWhitespace(const std::string& text, size_t pos) {
@@ -229,9 +285,16 @@ Containers::String ResolveShaderDependencyPath(
return dependencyPath;
}
const std::filesystem::path dependencyFsPath(dependencyPath.CStr());
const Containers::String normalizedDependencyPath =
NormalizeDependencyPathSeparators(dependencyPath);
if (normalizedDependencyPath.StartsWith(kBuiltinShaderPackagePrefix)) {
return ResolveBuiltinShaderPackageDependencyPath(normalizedDependencyPath);
}
const std::filesystem::path dependencyFsPath(normalizedDependencyPath.CStr());
if (dependencyFsPath.is_absolute()) {
return NormalizePathString(dependencyFsPath);
return Containers::String();
}
const std::filesystem::path sourceFsPath(sourcePath.CStr());
@@ -250,5 +313,110 @@ Containers::String ResolveShaderDependencyPath(
return NormalizePathString(sourceFsPath.parent_path() / dependencyFsPath);
}
Containers::String BuildShaderDependencyResolutionDiagnostic(
const Containers::String& dependencyPath,
const Containers::String& sourcePath,
const Containers::String& includeChain) {
const Containers::String normalizedDependencyPath =
NormalizeDependencyPathSeparators(dependencyPath);
Containers::String diagnostic =
Containers::String("Failed to resolve shader include: requested=") +
dependencyPath +
", normalized=" +
normalizedDependencyPath +
", source=" +
sourcePath +
", builtinPackageRoot=" +
kBuiltinShaderPackageRootRelativePath;
if (!includeChain.Empty()) {
diagnostic += ", includeChain=";
diagnostic += includeChain;
}
std::filesystem::path normalizedCandidate;
if (normalizedDependencyPath.StartsWith(kBuiltinShaderPackagePrefix)) {
const std::filesystem::path dependencyFsPath(normalizedDependencyPath.CStr());
const std::filesystem::path packageRelativePath =
dependencyFsPath.lexically_relative("Packages");
if (!packageRelativePath.empty() &&
!packageRelativePath.is_absolute() &&
!ContainsParentTraversal(packageRelativePath)) {
normalizedCandidate =
(std::filesystem::path(kBuiltinShaderPackageRootRelativePath) /
packageRelativePath).lexically_normal();
}
} else {
const std::filesystem::path dependencyFsPath(normalizedDependencyPath.CStr());
const std::filesystem::path sourceFsPath(sourcePath.CStr());
if (!dependencyFsPath.is_absolute()) {
normalizedCandidate =
(sourceFsPath.parent_path() / dependencyFsPath).lexically_normal();
}
}
diagnostic += ", normalizedCandidate=";
diagnostic += normalizedCandidate.empty()
? Containers::String("<none>")
: NormalizePathString(normalizedCandidate);
diagnostic += ", searchedAnchors=[sourceRoot=";
diagnostic += NormalizePathString(std::filesystem::path(__FILE__));
const Containers::String& resourceRoot = ResourceManager::Get().GetResourceRoot();
diagnostic += ", resourceRoot=";
diagnostic += resourceRoot.Empty() ? Containers::String("<empty>") : resourceRoot;
std::error_code ec;
diagnostic += ", currentPath=";
diagnostic += ec
? Containers::String("<unavailable>")
: NormalizePathString(std::filesystem::current_path(ec));
diagnostic += "]";
return diagnostic;
}
Containers::String ResolveBuiltinShaderPackageDependencyPath(
const Containers::String& dependencyPath) {
const Containers::String normalizedDependencyPath =
NormalizeDependencyPathSeparators(dependencyPath);
if (!normalizedDependencyPath.StartsWith(kBuiltinShaderPackagePrefix)) {
return Containers::String();
}
const std::filesystem::path dependencyFsPath(normalizedDependencyPath.CStr());
const std::filesystem::path packageRelativePath = dependencyFsPath.lexically_relative("Packages");
if (packageRelativePath.empty() ||
packageRelativePath.is_absolute() ||
ContainsParentTraversal(packageRelativePath)) {
return Containers::String();
}
const std::filesystem::path relativePath =
std::filesystem::path(kBuiltinShaderPackageRootRelativePath) /
packageRelativePath;
std::filesystem::path resolvedPath;
if (TryResolvePathFromAnchor(std::filesystem::path(__FILE__), relativePath, resolvedPath)) {
return NormalizePathString(resolvedPath);
}
const Containers::String& resourceRoot = ResourceManager::Get().GetResourceRoot();
if (!resourceRoot.Empty() &&
TryResolvePathFromAnchor(std::filesystem::path(resourceRoot.CStr()), relativePath, resolvedPath)) {
return NormalizePathString(resolvedPath);
}
std::error_code ec;
if (TryResolvePathFromAnchor(std::filesystem::current_path(ec), relativePath, resolvedPath)) {
return NormalizePathString(resolvedPath);
}
return Containers::String();
}
} // namespace Resources
} // namespace XCEngine

View File

@@ -29,5 +29,13 @@ Containers::String ResolveShaderDependencyPath(
const Containers::String& dependencyPath,
const Containers::String& sourcePath);
Containers::String BuildShaderDependencyResolutionDiagnostic(
const Containers::String& dependencyPath,
const Containers::String& sourcePath,
const Containers::String& includeChain = Containers::String());
Containers::String ResolveBuiltinShaderPackageDependencyPath(
const Containers::String& dependencyPath);
} // namespace Resources
} // namespace XCEngine