Files
UnrealEngine/Engine/Source/ThirdParty/MaterialX/MaterialX-1.38.10/source/MaterialXTest/MaterialXRender/RenderUtil.cpp
2025-05-18 13:04:45 +08:00

506 lines
20 KiB
C++

//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//
#include <MaterialXTest/External/Catch/catch.hpp>
#include <MaterialXTest/MaterialXRender/RenderUtil.h>
#include <MaterialXFormat/Util.h>
namespace mx = MaterialX;
namespace RenderUtil
{
ShaderRenderTester::ShaderRenderTester(mx::ShaderGeneratorPtr shaderGenerator) :
_shaderGenerator(shaderGenerator),
_resolveImageFilenames(false),
_emitColorTransforms(true)
{
}
ShaderRenderTester::~ShaderRenderTester()
{
}
// Create a list of generation options based on unit test options
// These options will override the original generation context options.
void ShaderRenderTester::getGenerationOptions(const GenShaderUtil::TestSuiteOptions& testOptions,
const mx::GenOptions& originalOptions,
std::vector<mx::GenOptions>& optionsList)
{
optionsList.clear();
if (testOptions.shaderInterfaces & 1)
{
mx::GenOptions reducedOption = originalOptions;
reducedOption.shaderInterfaceType = mx::SHADER_INTERFACE_REDUCED;
optionsList.push_back(reducedOption);
}
// Alway fallback to complete if no options specified.
if ((testOptions.shaderInterfaces & 2) || optionsList.empty())
{
mx::GenOptions completeOption = originalOptions;
completeOption.shaderInterfaceType = mx::SHADER_INTERFACE_COMPLETE;
optionsList.push_back(completeOption);
}
}
void ShaderRenderTester::printRunLog(const RenderProfileTimes &profileTimes,
const GenShaderUtil::TestSuiteOptions& options,
std::ostream& stream,
mx::DocumentPtr dependLib)
{
profileTimes.print(stream);
stream << "---------------------------------------" << std::endl;
options.print(stream);
}
void ShaderRenderTester::loadDependentLibraries(GenShaderUtil::TestSuiteOptions options, mx::FileSearchPath searchPath, mx::DocumentPtr& dependLib)
{
dependLib = mx::createDocument();
mx::loadLibraries({ "libraries" }, searchPath, dependLib);
for (size_t i = 0; i < options.extraLibraryPaths.size(); i++)
{
const mx::FilePath& libraryPath = options.extraLibraryPaths[i];
for (const mx::FilePath& libraryFile : libraryPath.getFilesInDirectory("mtlx"))
{
std::cout << "Extra library path: " << (libraryPath / libraryFile).asString() << std::endl;
mx::loadLibrary((libraryPath / libraryFile), dependLib);
}
}
// Load any addition per renderer libraries
loadAdditionalLibraries(dependLib, options);
}
bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath)
{
#ifdef LOG_TO_FILE
std::ofstream logfile(_shaderGenerator->getTarget() + "_render_log.txt");
std::ostream& log(logfile);
std::string docValidLogFilename = _shaderGenerator->getTarget() + "_render_doc_validation_log.txt";
std::ofstream docValidLogFile(docValidLogFilename);
std::ostream& docValidLog(docValidLogFile);
std::ofstream profilingLogfile(_shaderGenerator->getTarget() + "_render_profiling_log.txt");
std::ostream& profilingLog(profilingLogfile);
#else
std::ostream& log(std::cout);
std::string docValidLogFilename = "std::cout";
std::ostream& docValidLog(std::cout);
std::ostream& profilingLog(std::cout);
#endif
// Test has been turned off so just do nothing.
// Check for an option file
GenShaderUtil::TestSuiteOptions options;
if (!options.readOptions(optionsFilePath))
{
log << "Can't find options file. Skip test." << std::endl;
return false;
}
if (!runTest(options))
{
log << "Target: " << _shaderGenerator->getTarget() << " not set to run. Skip test." << std::endl;
return false;
}
// Profiling times
RenderUtil::RenderProfileTimes profileTimes;
// Global setup timer
mx::ScopedTimer totalTime(&profileTimes.totalTime);
// Add files to override the files in the test suite to be tested.
mx::StringSet testfileOverride;
for (const auto& filterFile : options.overrideFiles)
{
testfileOverride.insert(filterFile);
}
// Data search path
mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath();
mx::ScopedTimer ioTimer(&profileTimes.ioTime);
mx::FilePathVec dirs;
for (const auto& root : options.renderTestPaths)
{
mx::FilePathVec testRootDirs = searchPath.find(root).getSubDirectories();
dirs.insert(std::end(dirs), std::begin(testRootDirs), std::end(testRootDirs));
}
ioTimer.endTimer();
// Load in the library dependencies once
// This will be imported in each test document below
ioTimer.startTimer();
mx::DocumentPtr dependLib;
loadDependentLibraries(options, searchPath, dependLib);
ioTimer.endTimer();
// Create renderers and generators
mx::ScopedTimer setupTime(&profileTimes.languageTimes.setupTime);
createRenderer(log);
mx::ColorManagementSystemPtr colorManagementSystem = mx::DefaultColorManagementSystem::create(_shaderGenerator->getTarget());
colorManagementSystem->loadLibrary(dependLib);
_shaderGenerator->setColorManagementSystem(colorManagementSystem);
// Setup Unit system and working space
mx::UnitSystemPtr unitSystem = mx::UnitSystem::create(_shaderGenerator->getTarget());
_shaderGenerator->setUnitSystem(unitSystem);
mx::UnitConverterRegistryPtr registry = mx::UnitConverterRegistry::create();
mx::UnitTypeDefPtr distanceTypeDef = dependLib->getUnitTypeDef("distance");
registry->addUnitConverter(distanceTypeDef, mx::LinearUnitConverter::create(distanceTypeDef));
mx::UnitTypeDefPtr angleTypeDef = dependLib->getUnitTypeDef("angle");
registry->addUnitConverter(angleTypeDef, mx::LinearUnitConverter::create(angleTypeDef));
_shaderGenerator->getUnitSystem()->loadLibrary(dependLib);
_shaderGenerator->getUnitSystem()->setUnitConverterRegistry(registry);
mx::GenContext context(_shaderGenerator);
context.registerSourceCodeSearchPath(searchPath);
context.registerSourceCodeSearchPath(searchPath.find("libraries/stdlib/genosl/include"));
// Set target unit space
context.getOptions().targetDistanceUnit = "meter";
// Set whether to emit colorspace transforms
context.getOptions().emitColorTransforms = _emitColorTransforms;
// Register shader metadata defined in the libraries.
_shaderGenerator->registerShaderMetadata(dependLib, context);
setupTime.endTimer();
if (!options.enableDirectLighting)
{
context.getOptions().hwMaxActiveLightSources = 0;
}
registerLights(dependLib, options, context);
// Map to replace "/" in Element path and ":" in namespaced names with "_".
mx::StringMap pathMap;
pathMap["/"] = "_";
pathMap[":"] = "_";
mx::ScopedTimer validateTimer(&profileTimes.validateTime);
mx::ScopedTimer renderableSearchTimer(&profileTimes.renderableSearchTime);
mx::StringSet usedImpls;
const std::string MTLX_EXTENSION("mtlx");
for (const auto& dir : dirs)
{
ioTimer.startTimer();
mx::FilePathVec files;
files = dir.getFilesInDirectory(MTLX_EXTENSION);
ioTimer.endTimer();
for (const mx::FilePath& file : files)
{
ioTimer.startTimer();
// Check if a file override set is used and ignore all files
// not part of the override set
if (testfileOverride.size() && testfileOverride.count(file) == 0)
{
ioTimer.endTimer();
continue;
}
const mx::FilePath filename = mx::FilePath(dir) / mx::FilePath(file);
mx::DocumentPtr doc = mx::createDocument();
try
{
mx::FileSearchPath readSearchPath(searchPath);
readSearchPath.append(dir);
mx::readFromXmlFile(doc, filename, readSearchPath);
}
catch (mx::Exception& e)
{
docValidLog << "Failed to load in file: " << filename.asString() << ". Error: " << e.what() << std::endl;
WARN("Failed to load in file: " + filename.asString() + "See: " + docValidLogFilename + " for details.");
}
// For each new file clear the implementation cache.
// Since the new file might contain implementations with names
// colliding with implementations in previous test cases.
context.clearNodeImplementations();
doc->importLibrary(dependLib);
ioTimer.endTimer();
validateTimer.startTimer();
log << "MTLX Filename: " << filename.asString() << std::endl;
// Validate the test document
std::string validationErrors;
bool validDoc = doc->validate(&validationErrors);
if (!validDoc)
{
docValidLog << filename.asString() << std::endl;
docValidLog << validationErrors << std::endl;
}
validateTimer.endTimer();
CHECK(validDoc);
mx::FileSearchPath imageSearchPath(dir);
imageSearchPath.append(searchPath);
// Resolve file names if specified
if (_resolveImageFilenames)
{
mx::flattenFilenames(doc, imageSearchPath, _customFilenameResolver);
}
mx::FilePath outputPath = mx::FilePath(dir) / file;
outputPath.removeExtension();
renderableSearchTimer.startTimer();
std::vector<mx::TypedElementPtr> elements;
try
{
elements = mx::findRenderableElements(doc);
}
catch (mx::Exception& e)
{
docValidLog << e.what() << std::endl;
WARN("Shader generation error in " + filename.asString() + ": " + e.what());
}
renderableSearchTimer.endTimer();
for (const auto& element : elements)
{
mx::string elementName = mx::createValidName(mx::replaceSubstrings(element->getNamePath(), pathMap));
runRenderer(elementName, element, context, doc, log, options, profileTimes, imageSearchPath, outputPath, nullptr);
}
}
}
// Dump out profiling information
totalTime.endTimer();
printRunLog(profileTimes, options, profilingLog, dependLib);
return true;
}
void ShaderRenderTester::addAdditionalTestStreams(mx::MeshPtr mesh)
{
size_t vertexCount = mesh->getVertexCount();
if (vertexCount < 1)
{
return;
}
const std::string TEXCOORD_STREAM0_NAME("i_" + mx::MeshStream::TEXCOORD_ATTRIBUTE + "_0");
mx::MeshStreamPtr texCoordStream1 = mesh->getStream(TEXCOORD_STREAM0_NAME);
mx::MeshFloatBuffer uv = texCoordStream1->getData();
const std::string TEXCOORD_STREAM1_NAME("i_" + mx::MeshStream::TEXCOORD_ATTRIBUTE + "_1");
mx::MeshFloatBuffer* texCoordData2 = nullptr;
if (!mesh->getStream(TEXCOORD_STREAM1_NAME))
{
mx::MeshStreamPtr texCoordStream2 = mx::MeshStream::create(TEXCOORD_STREAM1_NAME, mx::MeshStream::TEXCOORD_ATTRIBUTE, 1);
texCoordStream2->setStride(2);
texCoordData2 = &(texCoordStream2->getData());
texCoordData2->resize(vertexCount * 2);
mesh->addStream(texCoordStream2);
}
const std::string COLOR_STREAM0_NAME("i_" + mx::MeshStream::COLOR_ATTRIBUTE + "_0");
mx::MeshFloatBuffer* colorData1 = nullptr;
if (!mesh->getStream(COLOR_STREAM0_NAME))
{
mx::MeshStreamPtr colorStream1 = mx::MeshStream::create(COLOR_STREAM0_NAME, mx::MeshStream::COLOR_ATTRIBUTE, 0);
colorData1 = &(colorStream1->getData());
colorStream1->setStride(4);
colorData1->resize(vertexCount * 4);
mesh->addStream(colorStream1);
}
const std::string COLOR_STREAM1_NAME("i_" + mx::MeshStream::COLOR_ATTRIBUTE + "_1");
mx::MeshFloatBuffer* colorData2 = nullptr;
if (!mesh->getStream(COLOR_STREAM1_NAME))
{
mx::MeshStreamPtr colorStream2 = mx::MeshStream::create(COLOR_STREAM1_NAME, mx::MeshStream::COLOR_ATTRIBUTE, 1);
colorData2 = &(colorStream2->getData());
colorStream2->setStride(4);
colorData2->resize(vertexCount * 4);
mesh->addStream(colorStream2);
}
const std::string GEOM_INT_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_integer");
int32_t* geomIntData = nullptr;
if (!mesh->getStream(GEOM_INT_STREAM_NAME))
{
mx::MeshStreamPtr geomIntStream = mx::MeshStream::create(GEOM_INT_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 0);
geomIntStream->setStride(1);
geomIntStream->getData().resize(vertexCount);
mesh->addStream(geomIntStream);
// Float and int32 have same size.
geomIntData = reinterpret_cast<int32_t*>(geomIntStream->getData().data());
}
const std::string GEOM_FLOAT_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_float");
mx::MeshFloatBuffer* geomFloatData = nullptr;
if (!mesh->getStream(GEOM_FLOAT_STREAM_NAME))
{
mx::MeshStreamPtr geomFloatStream = mx::MeshStream::create(GEOM_FLOAT_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 1);
geomFloatData = &(geomFloatStream->getData());
geomFloatStream->setStride(1);
geomFloatData->resize(vertexCount);
mesh->addStream(geomFloatStream);
}
const std::string GEOM_VECTOR2_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_vector2");
mx::MeshFloatBuffer* geomVector2Data = nullptr;
if (!mesh->getStream(GEOM_VECTOR2_STREAM_NAME))
{
mx::MeshStreamPtr geomVector2Stream = mx::MeshStream::create(GEOM_VECTOR2_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 1);
geomVector2Data = &(geomVector2Stream->getData());
geomVector2Stream->setStride(2);
geomVector2Data->resize(vertexCount * 2);
mesh->addStream(geomVector2Stream);
}
const std::string GEOM_VECTOR3_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_vector3");
mx::MeshFloatBuffer* geomVector3Data = nullptr;
if (!mesh->getStream(GEOM_VECTOR3_STREAM_NAME))
{
mx::MeshStreamPtr geomVector3Stream = mx::MeshStream::create(GEOM_VECTOR3_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 1);
geomVector3Data = &(geomVector3Stream->getData());
geomVector3Stream->setStride(3);
geomVector3Data->resize(vertexCount * 3);
mesh->addStream(geomVector3Stream);
}
const std::string GEOM_VECTOR4_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_vector4");
mx::MeshFloatBuffer* geomVector4Data = nullptr;
if (!mesh->getStream(GEOM_VECTOR4_STREAM_NAME))
{
mx::MeshStreamPtr geomVector4Stream = mx::MeshStream::create(GEOM_VECTOR4_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 1);
geomVector4Data = &(geomVector4Stream->getData());
geomVector4Stream->setStride(4);
geomVector4Data->resize(vertexCount * 4);
mesh->addStream(geomVector4Stream);
}
const std::string GEOM_COLOR2_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_color2");
mx::MeshFloatBuffer* geomColor2Data = nullptr;
if (!mesh->getStream(GEOM_COLOR2_STREAM_NAME))
{
mx::MeshStreamPtr geomColor2Stream = mx::MeshStream::create(GEOM_COLOR2_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 1);
geomColor2Data = &(geomColor2Stream->getData());
geomColor2Stream->setStride(2);
geomColor2Data->resize(vertexCount * 2);
mesh->addStream(geomColor2Stream);
}
const std::string GEOM_COLOR3_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_color3");
mx::MeshFloatBuffer* geomColor3Data = nullptr;
if (!mesh->getStream(GEOM_COLOR3_STREAM_NAME))
{
mx::MeshStreamPtr geomColor3Stream = mx::MeshStream::create(GEOM_COLOR3_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 1);
geomColor3Data = &(geomColor3Stream->getData());
geomColor3Stream->setStride(3);
geomColor3Data->resize(vertexCount * 3);
mesh->addStream(geomColor3Stream);
}
const std::string GEOM_COLOR4_STREAM_NAME("i_" + mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE + "_geompropvalue_color4");
mx::MeshFloatBuffer* geomColor4Data = nullptr;
if (!mesh->getStream(GEOM_COLOR4_STREAM_NAME))
{
mx::MeshStreamPtr geomColor4Stream = mx::MeshStream::create(GEOM_COLOR4_STREAM_NAME, mx::MeshStream::GEOMETRY_PROPERTY_ATTRIBUTE, 1);
geomColor4Data = &(geomColor4Stream->getData());
geomColor4Stream->setStride(4);
geomColor4Data->resize(vertexCount * 4);
mesh->addStream(geomColor4Stream);
}
auto sineData = [](float uv, float freq){
const float PI = std::acos(-1.0f);
float angle = uv * 2 * PI * freq;
return std::sin(angle) / 2.0f + 1.0f;
};
if (!uv.empty())
{
for (size_t i = 0; i < vertexCount; i++)
{
const size_t i2 = 2 * i;
const size_t i21 = i2 + 1;
const size_t i3 = 3 * i;
const size_t i4 = 4 * i;
// Fake second set of texture coordinates
if (texCoordData2)
{
(*texCoordData2)[i2] = uv[i21];
(*texCoordData2)[i21] = uv[i2];
}
if (colorData1)
{
// Fake some colors
(*colorData1)[i4] = uv[i2];
(*colorData1)[i4 + 1] = uv[i21];
(*colorData1)[i4 + 2] = 1.0f;
(*colorData1)[i4 + 3] = 1.0f;
}
if (colorData2)
{
(*colorData2)[i4] = 1.0f;
(*colorData2)[i4 + 1] = uv[i2];
(*colorData2)[i4 + 2] = uv[i21];
(*colorData2)[i4 + 3] = 1.0f;
}
if (geomIntData)
{
geomIntData[i] = static_cast<int32_t>(uv[i21] * 5);
}
if (geomFloatData)
{
(*geomFloatData)[i] = sineData(uv[i21], 12.0f);
}
if (geomVector2Data)
{
(*geomVector2Data)[i2] = sineData(uv[i21], 6.0f);
(*geomVector2Data)[i21] = 0.0f;
}
if (geomVector3Data)
{
(*geomVector3Data)[i3] = 0.0f;
(*geomVector3Data)[i3 + 1] = sineData(uv[i21], 8.0f);
(*geomVector3Data)[i3 + 2] = 0.0f;
}
if (geomVector4Data)
{
(*geomVector4Data)[i4] = 0.0f;
(*geomVector4Data)[i4 + 1] = 0.0f;
(*geomVector4Data)[i4 + 2] = sineData(uv[i21], 10.0f);
(*geomVector4Data)[i4 + 3] = 1.0f;
}
if (geomColor2Data)
{
(*geomColor2Data)[i2] = sineData(uv[i2], 10.0f);
(*geomColor2Data)[i21] = 0.0f;
}
if (geomColor3Data)
{
(*geomColor3Data)[i3] = 0.0f;
(*geomColor3Data)[i3 + 1] = sineData(uv[i2], 8.0f);
(*geomColor3Data)[i3 + 2] = 0.0f;
}
if (geomColor4Data)
{
(*geomColor4Data)[i4] = 0.0f;
(*geomColor4Data)[i4 + 1] = 0.0f;
(*geomColor4Data)[i4 + 2] = sineData(uv[i2], 6.0f);
(*geomColor4Data)[i4 + 3] = 1.0f;
}
}
}
}
} // namespace RenderUtil