292 lines
10 KiB
C++
292 lines
10 KiB
C++
#include "SupportedShaderCatalog.h"
|
|
#include "ShaderPackageRegistry.h"
|
|
|
|
#include <filesystem>
|
|
#include <iostream>
|
|
#include <string>
|
|
|
|
namespace
|
|
{
|
|
int gFailures = 0;
|
|
|
|
void Expect(bool condition, const std::string& message)
|
|
{
|
|
if (condition)
|
|
return;
|
|
|
|
++gFailures;
|
|
std::cerr << "FAIL: " << message << "\n";
|
|
}
|
|
|
|
ShaderPackage MakeSinglePassPackage()
|
|
{
|
|
ShaderPackage shaderPackage;
|
|
shaderPackage.id = "supported";
|
|
ShaderPassDefinition pass;
|
|
pass.id = "main";
|
|
pass.entryPoint = "mainImage";
|
|
pass.sourcePath = "shader.slang";
|
|
pass.outputName = "layerOutput";
|
|
shaderPackage.passes.push_back(pass);
|
|
return shaderPackage;
|
|
}
|
|
|
|
std::filesystem::path RepoRoot()
|
|
{
|
|
std::filesystem::path path = std::filesystem::current_path();
|
|
while (!path.empty())
|
|
{
|
|
if (std::filesystem::exists(path / "shaders" / "text-overlay" / "shader.json"))
|
|
return path;
|
|
const std::filesystem::path parent = path.parent_path();
|
|
if (parent.empty() || parent == path)
|
|
break;
|
|
path = parent;
|
|
}
|
|
return std::filesystem::current_path();
|
|
}
|
|
|
|
bool CanRunMsdfAtlasGen(std::string& reason)
|
|
{
|
|
const std::filesystem::path repoRoot = RepoRoot();
|
|
std::filesystem::path executablePath;
|
|
if (!RenderCadenceCompositor::FontAtlasBuilder::FindMsdfAtlasGenExecutable(repoRoot, executablePath))
|
|
{
|
|
reason = "msdf-atlas-gen executable is not available";
|
|
return false;
|
|
}
|
|
|
|
ShaderPackageRegistry registry(4);
|
|
ShaderPackage shaderPackage;
|
|
if (!registry.ParseManifest(repoRoot / "shaders" / "text-overlay" / "shader.json", shaderPackage, reason))
|
|
return false;
|
|
|
|
RenderCadenceCompositor::FontAtlasBuildConfig config;
|
|
config.repoRoot = repoRoot;
|
|
config.cacheRoot = repoRoot / "runtime" / "test_font_cache";
|
|
RenderCadenceCompositor::FontAtlasBuilder builder(config);
|
|
std::vector<RenderCadenceCompositor::FontAtlasBuildOutput> outputs;
|
|
if (!builder.BuildPackageFontAtlases(shaderPackage, outputs, reason))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void SupportsSinglePassStatelessPackage()
|
|
{
|
|
const ShaderPackage shaderPackage = MakeSinglePassPackage();
|
|
const RenderCadenceCompositor::ShaderSupportResult result =
|
|
RenderCadenceCompositor::CheckStatelessSinglePassShaderSupport(shaderPackage);
|
|
|
|
Expect(result.supported, "single-pass stateless packages should be supported");
|
|
Expect(result.reason.empty(), "supported packages should not report a rejection reason");
|
|
}
|
|
|
|
void SupportsStatelessNamedPassPackage()
|
|
{
|
|
ShaderPackage shaderPackage = MakeSinglePassPackage();
|
|
shaderPackage.passes.front().outputName = "generatedMask";
|
|
ShaderPassDefinition secondPass;
|
|
secondPass.id = "second";
|
|
secondPass.entryPoint = "mainImage";
|
|
secondPass.sourcePath = "shader.slang";
|
|
secondPass.inputNames.push_back("generatedMask");
|
|
secondPass.outputName = "layerOutput";
|
|
shaderPackage.passes.push_back(secondPass);
|
|
|
|
const RenderCadenceCompositor::ShaderSupportResult result =
|
|
RenderCadenceCompositor::CheckStatelessSinglePassShaderSupport(shaderPackage);
|
|
|
|
Expect(result.supported, "stateless named-pass packages should be supported");
|
|
Expect(result.reason.empty(), "supported named-pass packages should not report a rejection reason");
|
|
}
|
|
|
|
void RejectsUnknownPassInput()
|
|
{
|
|
ShaderPackage shaderPackage = MakeSinglePassPackage();
|
|
shaderPackage.passes.front().inputNames.push_back("missingIntermediate");
|
|
|
|
const RenderCadenceCompositor::ShaderSupportResult result =
|
|
RenderCadenceCompositor::CheckStatelessSinglePassShaderSupport(shaderPackage);
|
|
|
|
Expect(!result.supported, "packages with unknown pass inputs should be rejected");
|
|
Expect(result.reason.find("unknown input") != std::string::npos, "unknown input rejection should explain the missing named output");
|
|
}
|
|
|
|
void RejectsTemporalPackage()
|
|
{
|
|
ShaderPackage shaderPackage = MakeSinglePassPackage();
|
|
shaderPackage.temporal.enabled = true;
|
|
|
|
const RenderCadenceCompositor::ShaderSupportResult result =
|
|
RenderCadenceCompositor::CheckStatelessSinglePassShaderSupport(shaderPackage);
|
|
|
|
Expect(!result.supported, "temporal packages should be rejected");
|
|
Expect(result.reason.find("temporal") != std::string::npos, "temporal rejection should mention temporal storage");
|
|
}
|
|
|
|
void RejectsTextureAssets()
|
|
{
|
|
ShaderPackage shaderPackage = MakeSinglePassPackage();
|
|
ShaderTextureAsset asset;
|
|
asset.id = "lut";
|
|
shaderPackage.textureAssets.push_back(asset);
|
|
|
|
const RenderCadenceCompositor::ShaderSupportResult result =
|
|
RenderCadenceCompositor::CheckStatelessSinglePassShaderSupport(shaderPackage);
|
|
|
|
Expect(!result.supported, "texture-backed packages should be rejected for now");
|
|
Expect(result.reason.find("texture") != std::string::npos, "texture rejection should mention texture assets");
|
|
}
|
|
|
|
void RejectsTextParametersWithoutDeclaredFont()
|
|
{
|
|
ShaderPackage shaderPackage = MakeSinglePassPackage();
|
|
ShaderParameterDefinition parameter;
|
|
parameter.id = "caption";
|
|
parameter.type = ShaderParameterType::Text;
|
|
parameter.fontId = "missing";
|
|
shaderPackage.parameters.push_back(parameter);
|
|
|
|
const RenderCadenceCompositor::ShaderSupportResult result =
|
|
RenderCadenceCompositor::CheckStatelessSinglePassShaderSupport(shaderPackage);
|
|
|
|
Expect(!result.supported, "text parameters without declared fonts should be rejected");
|
|
Expect(result.reason.find("unknown font") != std::string::npos, "text rejection should mention the missing font");
|
|
}
|
|
|
|
void SupportsTextParametersWithDeclaredFont()
|
|
{
|
|
ShaderPackage shaderPackage = MakeSinglePassPackage();
|
|
ShaderFontAsset fontAsset;
|
|
fontAsset.id = "roboto";
|
|
shaderPackage.fontAssets.push_back(fontAsset);
|
|
ShaderParameterDefinition parameter;
|
|
parameter.id = "caption";
|
|
parameter.type = ShaderParameterType::Text;
|
|
parameter.fontId = "roboto";
|
|
shaderPackage.parameters.push_back(parameter);
|
|
|
|
const RenderCadenceCompositor::ShaderSupportResult result =
|
|
RenderCadenceCompositor::CheckStatelessSinglePassShaderSupport(shaderPackage);
|
|
|
|
Expect(result.supported, "text parameters with declared fonts should be supported");
|
|
Expect(result.reason.empty(), "supported text parameters should not report a rejection reason");
|
|
}
|
|
|
|
void SupportsTextParametersWithFontSelector()
|
|
{
|
|
ShaderPackage shaderPackage = MakeSinglePassPackage();
|
|
ShaderFontAsset roboto;
|
|
roboto.id = "roboto";
|
|
shaderPackage.fontAssets.push_back(roboto);
|
|
ShaderFontAsset mono;
|
|
mono.id = "mono";
|
|
shaderPackage.fontAssets.push_back(mono);
|
|
|
|
ShaderParameterDefinition fontSelector;
|
|
fontSelector.id = "font";
|
|
fontSelector.type = ShaderParameterType::Enum;
|
|
fontSelector.defaultEnumValue = "roboto";
|
|
fontSelector.enumOptions = { { "roboto", "Roboto" }, { "mono", "Mono" } };
|
|
shaderPackage.parameters.push_back(fontSelector);
|
|
|
|
ShaderParameterDefinition parameter;
|
|
parameter.id = "caption";
|
|
parameter.type = ShaderParameterType::Text;
|
|
parameter.fontId = "roboto";
|
|
parameter.fontParameterId = "font";
|
|
shaderPackage.parameters.push_back(parameter);
|
|
|
|
const RenderCadenceCompositor::ShaderSupportResult result =
|
|
RenderCadenceCompositor::CheckStatelessSinglePassShaderSupport(shaderPackage);
|
|
|
|
Expect(result.supported, "text parameters with font selector enum should be supported");
|
|
Expect(result.reason.empty(), "supported font selector text parameters should not report a rejection reason");
|
|
}
|
|
|
|
void RejectsTextParametersWithFontSelectorUnknownOption()
|
|
{
|
|
ShaderPackage shaderPackage = MakeSinglePassPackage();
|
|
ShaderFontAsset roboto;
|
|
roboto.id = "roboto";
|
|
shaderPackage.fontAssets.push_back(roboto);
|
|
|
|
ShaderParameterDefinition fontSelector;
|
|
fontSelector.id = "font";
|
|
fontSelector.type = ShaderParameterType::Enum;
|
|
fontSelector.defaultEnumValue = "roboto";
|
|
fontSelector.enumOptions = { { "roboto", "Roboto" }, { "missing", "Missing" } };
|
|
shaderPackage.parameters.push_back(fontSelector);
|
|
|
|
ShaderParameterDefinition parameter;
|
|
parameter.id = "caption";
|
|
parameter.type = ShaderParameterType::Text;
|
|
parameter.fontId = "roboto";
|
|
parameter.fontParameterId = "font";
|
|
shaderPackage.parameters.push_back(parameter);
|
|
|
|
const RenderCadenceCompositor::ShaderSupportResult result =
|
|
RenderCadenceCompositor::CheckStatelessSinglePassShaderSupport(shaderPackage);
|
|
|
|
Expect(!result.supported, "font selector enum options must reference declared font assets");
|
|
Expect(result.reason.find("unknown font asset") != std::string::npos, "font selector rejection mentions unknown font asset");
|
|
}
|
|
|
|
void BuildsDeclaredFontAtlasesDuringCatalogLoad()
|
|
{
|
|
std::string msdfReason;
|
|
if (!CanRunMsdfAtlasGen(msdfReason))
|
|
{
|
|
std::cout << "SKIP: msdf-atlas-gen could not run in this environment: " << msdfReason << "\n";
|
|
return;
|
|
}
|
|
|
|
RenderCadenceCompositor::SupportedShaderCatalog catalog;
|
|
std::string error;
|
|
Expect(catalog.Load(RepoRoot() / "shaders", 12, error), "shader catalog loads");
|
|
|
|
bool textOverlaySupported = false;
|
|
for (const RenderCadenceCompositor::SupportedShaderSummary& shader : catalog.Shaders())
|
|
{
|
|
if (shader.id == "text-overlay")
|
|
textOverlaySupported = true;
|
|
}
|
|
Expect(textOverlaySupported, "text overlay is listed as a supported shader after font atlas preparation");
|
|
|
|
const auto& fontAtlases = catalog.FontAtlases();
|
|
const auto textOverlayIt = fontAtlases.find("text-overlay");
|
|
Expect(textOverlayIt != fontAtlases.end(), "text overlay font atlas is prepared during catalog load");
|
|
if (textOverlayIt != fontAtlases.end() && !textOverlayIt->second.empty())
|
|
{
|
|
Expect(std::filesystem::exists(textOverlayIt->second.front().imagePath), "catalog font atlas image exists");
|
|
Expect(std::filesystem::exists(textOverlayIt->second.front().jsonPath), "catalog font atlas json exists");
|
|
Expect(std::filesystem::file_size(textOverlayIt->second.front().imagePath) > 0, "catalog font atlas image is not empty");
|
|
Expect(std::filesystem::file_size(textOverlayIt->second.front().jsonPath) > 0, "catalog font atlas json is not empty");
|
|
}
|
|
}
|
|
}
|
|
|
|
int main()
|
|
{
|
|
SupportsSinglePassStatelessPackage();
|
|
SupportsStatelessNamedPassPackage();
|
|
RejectsUnknownPassInput();
|
|
RejectsTemporalPackage();
|
|
RejectsTextureAssets();
|
|
RejectsTextParametersWithoutDeclaredFont();
|
|
SupportsTextParametersWithDeclaredFont();
|
|
SupportsTextParametersWithFontSelector();
|
|
RejectsTextParametersWithFontSelectorUnknownOption();
|
|
BuildsDeclaredFontAtlasesDuringCatalogLoad();
|
|
|
|
if (gFailures != 0)
|
|
{
|
|
std::cerr << gFailures << " RenderCadenceCompositorSupportedShaderCatalog test failure(s).\n";
|
|
return 1;
|
|
}
|
|
|
|
std::cout << "RenderCadenceCompositorSupportedShaderCatalog tests passed.\n";
|
|
return 0;
|
|
}
|