Files
XCEngine/engine/Runtime/Resources/Shader/Internal/ShaderAuthoringLoader.cpp

234 lines
8.3 KiB
C++

#include "ShaderAuthoringLoader.h"
#include "../ShaderAuthoringParser.h"
#include "../ShaderSourceUtils.h"
#include "ShaderAuthoringInternal.h"
#include "ShaderFileUtils.h"
#include "ShaderRuntimeBuildUtils.h"
#include <XCEngine/Resources/BuiltinResources.h>
#include <filesystem>
#include <unordered_set>
namespace XCEngine {
namespace Resources {
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,
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) {
return true;
}
ShaderIR shaderIR = {};
Containers::String parseError;
if (!ParseShaderAuthoring(path, sourceText, shaderIR, &parseError)) {
if (outError != nullptr) {
*outError = parseError;
}
return false;
}
if (!::XCEngine::Resources::Internal::CollectQuotedIncludeDependencyPaths(
path,
shaderIR.sharedProgramSource,
seenDependencyPaths,
outDependencies,
outError)) {
return false;
}
for (const ShaderSubShaderIR& subShader : shaderIR.subShaders) {
if (!::XCEngine::Resources::Internal::CollectQuotedIncludeDependencyPaths(
path,
subShader.sharedProgramSource,
seenDependencyPaths,
outDependencies,
outError)) {
return false;
}
for (const ShaderPassIR& pass : subShader.passes) {
if (!pass.isUsePass) {
if (!::XCEngine::Resources::Internal::CollectQuotedIncludeDependencyPaths(
path,
pass.sharedProgramSource,
seenDependencyPaths,
outDependencies,
outError) ||
!::XCEngine::Resources::Internal::CollectQuotedIncludeDependencyPaths(
path,
pass.programSource,
seenDependencyPaths,
outDependencies,
outError)) {
return false;
}
continue;
}
Containers::String resolvedUsePassPath;
if (!ResolveShaderUsePassPath(
path,
shaderIR.name,
pass.usePassShaderName,
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;
}
if (!builtinShaderPath.Empty() &&
seenDependencyPaths.insert(ToStdString(builtinShaderPath)).second) {
outDependencies.PushBack(builtinShaderPath);
}
continue;
}
if (resolvedUsePassPath.Empty() || resolvedUsePassPath == path) {
continue;
}
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;
}
const fs::path normalizedBuiltinAssetPath =
fs::path(builtinAssetPath.CStr()).lexically_normal();
const Containers::String normalizedBuiltinDependency =
normalizedBuiltinAssetPath.generic_string().c_str();
if (!normalizedBuiltinDependency.Empty() &&
seenDependencyPaths.insert(ToStdString(normalizedBuiltinDependency)).second) {
outDependencies.PushBack(normalizedBuiltinDependency);
}
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;
}
if (!CollectShaderAuthoringDependencyPathsRecursive(
builtinAssetPath,
referencedSourceText.CStr(),
seenShaderPaths,
seenDependencyPaths,
outDependencies,
outError)) {
return false;
}
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)) {
if (outError != nullptr) {
*outError =
Containers::String("Failed to read shader UsePass source: source=") +
path +
", requested=" +
resolvedUsePassPath;
}
return false;
}
if (!CollectShaderAuthoringDependencyPathsRecursive(
resolvedUsePassPath,
referencedSourceText.CStr(),
seenShaderPaths,
seenDependencyPaths,
outDependencies,
outError)) {
return false;
}
}
}
return true;
}
} // namespace
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;
return CollectShaderAuthoringDependencyPathsRecursive(
path,
sourceText,
seenShaderPaths,
seenDependencyPaths,
outDependencies,
outError);
}
LoadResult LoadShaderAuthoring(
const Containers::String& path,
const std::string& sourceText) {
ShaderIR shaderIR = {};
Containers::String parseError;
if (!ParseShaderAuthoring(path, sourceText, shaderIR, &parseError)) {
return LoadResult(parseError);
}
return BuildShaderFromIR(path, shaderIR);
}
} // namespace Resources
} // namespace XCEngine