Close shader authoring pipeline and UsePass dependency tracking
This commit is contained in:
@@ -1193,6 +1193,171 @@ TEST(ShaderLoader, AssetDatabaseReimportsShaderWhenUsePassDependencyChanges) {
|
||||
fs::remove_all(projectRoot);
|
||||
}
|
||||
|
||||
TEST(ShaderLoader, AssetDatabaseReimportsShaderWhenBuiltinUsePassDependencyChanges) {
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
const fs::path sandboxRoot =
|
||||
fs::temp_directory_path() / "xc_shader_authoring_builtin_usepass_reimport";
|
||||
const fs::path projectRoot = sandboxRoot / "Project";
|
||||
const fs::path shaderDir = projectRoot / "Assets" / "Shaders";
|
||||
const fs::path mainShaderPath = shaderDir / "main.shader";
|
||||
const fs::path builtinShaderAssetPath =
|
||||
sandboxRoot / "engine" / "assets" / "builtin" / "shaders" / "shadow-caster.shader";
|
||||
|
||||
const fs::path previousPath = fs::current_path();
|
||||
fs::remove_all(sandboxRoot);
|
||||
fs::create_directories(shaderDir);
|
||||
fs::create_directories(builtinShaderAssetPath.parent_path());
|
||||
|
||||
auto writeBuiltinShader = [&](const char* cullMode, const char* marker) {
|
||||
WriteTextFile(
|
||||
builtinShaderAssetPath,
|
||||
std::string(R"(Shader "Builtin Shadow Caster"
|
||||
{
|
||||
SubShader
|
||||
{
|
||||
Pass
|
||||
{
|
||||
Name "ShadowCaster"
|
||||
Tags { "LightMode" = "ShadowCaster" }
|
||||
Cull )") +
|
||||
cullMode +
|
||||
R"(
|
||||
HLSLPROGRAM
|
||||
#pragma vertex MainVS
|
||||
#pragma fragment MainPS
|
||||
float4 MainVS() : SV_POSITION { return 0; }
|
||||
float4 MainPS() : SV_TARGET { return )" +
|
||||
marker +
|
||||
R"(; }
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
}
|
||||
)");
|
||||
};
|
||||
|
||||
writeBuiltinShader("Back", "float4(1.0, 0.0, 0.0, 1.0)");
|
||||
WriteTextFile(
|
||||
mainShaderPath,
|
||||
R"(Shader "Builtin UsePass Dependency Shader"
|
||||
{
|
||||
SubShader
|
||||
{
|
||||
UsePass "Builtin Shadow Caster/ShadowCaster"
|
||||
}
|
||||
}
|
||||
)");
|
||||
|
||||
fs::current_path(projectRoot);
|
||||
|
||||
ShaderLoader dependencyLoader;
|
||||
Array<String> firstDependencies;
|
||||
ASSERT_TRUE(dependencyLoader.CollectSourceDependencies(mainShaderPath.string().c_str(), firstDependencies));
|
||||
ASSERT_EQ(firstDependencies.Size(), 1u);
|
||||
EXPECT_EQ(
|
||||
fs::path(firstDependencies[0].CStr()).lexically_normal(),
|
||||
builtinShaderAssetPath.lexically_normal());
|
||||
|
||||
AssetDatabase database;
|
||||
database.Initialize(projectRoot.string().c_str());
|
||||
|
||||
AssetDatabase::ResolvedAsset firstResolve;
|
||||
ASSERT_TRUE(database.EnsureArtifact("Assets/Shaders/main.shader", ResourceType::Shader, firstResolve));
|
||||
ASSERT_TRUE(firstResolve.artifactReady);
|
||||
|
||||
ShaderLoader loader;
|
||||
LoadResult firstLoad = loader.Load(firstResolve.artifactMainPath.CStr());
|
||||
ASSERT_TRUE(firstLoad);
|
||||
auto* firstShader = static_cast<Shader*>(firstLoad.resource);
|
||||
ASSERT_NE(firstShader, nullptr);
|
||||
const ShaderPass* firstPass = firstShader->FindPass("ShadowCaster");
|
||||
ASSERT_NE(firstPass, nullptr);
|
||||
EXPECT_EQ(firstPass->fixedFunctionState.cullMode, MaterialCullMode::Back);
|
||||
const ShaderStageVariant* firstFragmentVariant =
|
||||
firstShader->FindVariant("ShadowCaster", ShaderType::Fragment, ShaderBackend::D3D12);
|
||||
ASSERT_NE(firstFragmentVariant, nullptr);
|
||||
EXPECT_NE(
|
||||
std::string(firstFragmentVariant->sourceCode.CStr()).find("float4(1.0, 0.0, 0.0, 1.0)"),
|
||||
std::string::npos);
|
||||
delete firstShader;
|
||||
|
||||
const String firstArtifactPath = firstResolve.artifactMainPath;
|
||||
database.Shutdown();
|
||||
|
||||
std::this_thread::sleep_for(50ms);
|
||||
writeBuiltinShader("Front", "float4(0.0, 1.0, 0.0, 1.0)");
|
||||
|
||||
database.Initialize(projectRoot.string().c_str());
|
||||
AssetDatabase::ResolvedAsset secondResolve;
|
||||
ASSERT_TRUE(database.EnsureArtifact("Assets/Shaders/main.shader", ResourceType::Shader, secondResolve));
|
||||
ASSERT_TRUE(secondResolve.artifactReady);
|
||||
EXPECT_NE(secondResolve.artifactMainPath, firstArtifactPath);
|
||||
|
||||
LoadResult secondLoad = loader.Load(secondResolve.artifactMainPath.CStr());
|
||||
ASSERT_TRUE(secondLoad);
|
||||
auto* secondShader = static_cast<Shader*>(secondLoad.resource);
|
||||
ASSERT_NE(secondShader, nullptr);
|
||||
const ShaderPass* secondPass = secondShader->FindPass("ShadowCaster");
|
||||
ASSERT_NE(secondPass, nullptr);
|
||||
EXPECT_EQ(secondPass->fixedFunctionState.cullMode, MaterialCullMode::Front);
|
||||
const ShaderStageVariant* secondFragmentVariant =
|
||||
secondShader->FindVariant("ShadowCaster", ShaderType::Fragment, ShaderBackend::D3D12);
|
||||
ASSERT_NE(secondFragmentVariant, nullptr);
|
||||
EXPECT_NE(
|
||||
std::string(secondFragmentVariant->sourceCode.CStr()).find("float4(0.0, 1.0, 0.0, 1.0)"),
|
||||
std::string::npos);
|
||||
delete secondShader;
|
||||
|
||||
database.Shutdown();
|
||||
fs::current_path(previousPath);
|
||||
fs::remove_all(sandboxRoot);
|
||||
}
|
||||
|
||||
TEST(ShaderLoader, LoadShaderAuthoringRejectsCyclicProjectUsePass) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
const fs::path shaderRoot = fs::temp_directory_path() / "xc_shader_authoring_usepass_cycle";
|
||||
const fs::path shaderAPath = shaderRoot / "a.shader";
|
||||
const fs::path shaderBPath = shaderRoot / "b.shader";
|
||||
|
||||
fs::remove_all(shaderRoot);
|
||||
fs::create_directories(shaderRoot);
|
||||
|
||||
WriteTextFile(
|
||||
shaderAPath,
|
||||
R"(Shader "Cycle Shader A"
|
||||
{
|
||||
SubShader
|
||||
{
|
||||
UsePass "Cycle Shader B/PassB"
|
||||
}
|
||||
}
|
||||
)");
|
||||
|
||||
WriteTextFile(
|
||||
shaderBPath,
|
||||
R"(Shader "Cycle Shader B"
|
||||
{
|
||||
SubShader
|
||||
{
|
||||
UsePass "Cycle Shader A/PassA"
|
||||
}
|
||||
}
|
||||
)");
|
||||
|
||||
ShaderLoader loader;
|
||||
LoadResult result = loader.Load(shaderAPath.string().c_str());
|
||||
EXPECT_FALSE(result.success);
|
||||
EXPECT_EQ(result.resource, nullptr);
|
||||
EXPECT_NE(
|
||||
std::string(result.errorMessage.CStr()).find("cyclic shader reference"),
|
||||
std::string::npos);
|
||||
|
||||
fs::remove_all(shaderRoot);
|
||||
}
|
||||
|
||||
TEST(ShaderLoader, LoadShaderAuthoringRejectsLegacyBackendPragma) {
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user